Source Code
Overview
ETH Balance
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 28953379 | 17 mins ago | 0 ETH | ||||
| 28953379 | 17 mins ago | 0 ETH | ||||
| 28953379 | 17 mins ago | 0 ETH | ||||
| 28953379 | 17 mins ago | 0 ETH | ||||
| 28953379 | 17 mins ago | 0 ETH | ||||
| 28953379 | 17 mins ago | 0 ETH | ||||
| 28953037 | 46 mins ago | 0 ETH | ||||
| 28953037 | 46 mins ago | 0 ETH | ||||
| 28953037 | 46 mins ago | 0 ETH | ||||
| 28953037 | 46 mins ago | 0 ETH | ||||
| 28953037 | 46 mins ago | 0 ETH | ||||
| 28953037 | 46 mins ago | 0 ETH | ||||
| 28953037 | 46 mins ago | 0 ETH | ||||
| 28953037 | 46 mins ago | 0 ETH | ||||
| 28952745 | 1 hr ago | 0 ETH | ||||
| 28952745 | 1 hr ago | 0 ETH | ||||
| 28952745 | 1 hr ago | 0 ETH | ||||
| 28952745 | 1 hr ago | 0 ETH | ||||
| 28952745 | 1 hr ago | 0 ETH | ||||
| 28952745 | 1 hr ago | 0 ETH | ||||
| 28952596 | 1 hr ago | 0 ETH | ||||
| 28952596 | 1 hr ago | 0 ETH | ||||
| 28952007 | 2 hrs ago | 0 ETH | ||||
| 28952007 | 2 hrs ago | 0 ETH | ||||
| 28952007 | 2 hrs ago | 0 ETH |
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:
MToken
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 10000 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.26;
import { ERC20Extended } from "../lib/common/src/ERC20Extended.sol";
import { UIntMath } from "../lib/common/src/libs/UIntMath.sol";
import { Migratable } from "../lib/common/src/Migratable.sol";
import { IERC20 } from "../lib/common/src/interfaces/IERC20.sol";
import { RegistrarReader } from "./libs/RegistrarReader.sol";
import { IContinuousIndexing } from "./interfaces/IContinuousIndexing.sol";
import { IMToken } from "./interfaces/IMToken.sol";
import { ContinuousIndexing } from "./abstract/ContinuousIndexing.sol";
import { ContinuousIndexingMath } from "./libs/ContinuousIndexingMath.sol";
/**
* @title MToken
* @author M^0 Labs
* @notice ERC20 M Token living on other chains.
*/
contract MToken is IMToken, ContinuousIndexing, ERC20Extended, Migratable {
/* ============ Structs ============ */
/**
* @notice MToken balance struct.
* @param isEarning True if the account is earning, false otherwise.
* @param rawBalance Balance (for a non earning account) or balance principal (for an earning account).
*/
struct MBalance {
bool isEarning;
uint240 rawBalance;
}
/* ============ Variables ============ */
/// @inheritdoc IMToken
address public immutable portal;
/// @inheritdoc IMToken
address public immutable registrar;
/// @inheritdoc IMToken
address public immutable migrationAdmin;
/// @inheritdoc IMToken
uint240 public totalNonEarningSupply;
/// @inheritdoc IMToken
uint112 public principalOfTotalEarningSupply;
/// @notice The balance of M for non-earner or principal of earning M balance for earners.
mapping(address account => MBalance balance) internal _balances;
/* ============ Modifiers ============ */
/// @dev Modifier to check if caller is the Portal.
modifier onlyPortal() {
_revertIfNotPortal();
_;
}
/* ============ Constructor ============ */
/**
* @notice Constructs the M Token contract.
* @dev Sets immutable storage.
* @param registrar_ The address of the Registrar contract.
* @param portal_ The address of the Portal contract.
* @param migrationAdmin_ The address of a migration admin.
*/
constructor(address registrar_, address portal_, address migrationAdmin_) ContinuousIndexing() ERC20Extended("M by M^0", "M", 6) {
_disableInitializers();
if ((registrar = registrar_) == address(0)) revert ZeroRegistrar();
if ((portal = portal_) == address(0)) revert ZeroPortal();
if ((migrationAdmin = migrationAdmin_) == address(0)) revert ZeroMigrationAdmin();
}
/* ============ Initializer ============ */
/// @inheritdoc IMToken
function initialize() external initializer {
_initialize();
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IMToken
function mint(address account_, uint256 amount_, uint128 index_) external onlyPortal {
_updateIndex(index_);
_mint(account_, amount_);
}
/// @inheritdoc IMToken
function mint(address account_, uint256 amount_) external onlyPortal {
_mint(account_, amount_);
}
/// @inheritdoc IMToken
function burn(uint256 amount_) external onlyPortal {
_burn(msg.sender, amount_);
}
/// @inheritdoc IMToken
function updateIndex(uint128 index_) external onlyPortal {
_updateIndex(index_);
}
/// @inheritdoc IMToken
function startEarning() external {
if (!_isApprovedEarner(msg.sender)) revert NotApprovedEarner();
if (currentIndex() == ContinuousIndexingMath.EXP_SCALED_ONE) revert IndexNotInitialized();
_startEarning(msg.sender);
}
/// @inheritdoc IMToken
function stopEarning() external {
_stopEarning(msg.sender);
}
/// @inheritdoc IMToken
function stopEarning(address account_) external {
if (_isApprovedEarner(account_)) revert IsApprovedEarner();
_stopEarning(account_);
}
/**
* @dev Performs the contract migration by calling `migrator_`.
* @param migrator_ The address of a migrator contract.
*/
function migrate(address migrator_) external {
if (msg.sender != migrationAdmin) revert UnauthorizedMigration();
_migrate(migrator_);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IMToken
function totalEarningSupply() public view returns (uint240 totalEarningSupply_) {
return _getPresentAmount(principalOfTotalEarningSupply);
}
/// @inheritdoc IERC20
function totalSupply() external view returns (uint256 totalSupply_) {
unchecked {
return totalNonEarningSupply + totalEarningSupply();
}
}
/// @inheritdoc IMToken
function principalBalanceOf(address account_) external view returns (uint240 balance_) {
MBalance storage mBalance_ = _balances[account_];
// Treat the raw balance as principal for earner.
return mBalance_.isEarning ? uint112(mBalance_.rawBalance) : 0;
}
/// @inheritdoc IERC20
function balanceOf(address account_) external view returns (uint256 balance_) {
MBalance storage mBalance_ = _balances[account_];
return
mBalance_.isEarning
? _getPresentAmount(uint112(mBalance_.rawBalance)) // Treat the raw balance as principal for earner.
: mBalance_.rawBalance;
}
/// @inheritdoc IMToken
function isEarning(address account_) external view returns (bool isEarning_) {
return _balances[account_].isEarning;
}
/// @inheritdoc IContinuousIndexing
function currentIndex() public view override(ContinuousIndexing, IContinuousIndexing) returns (uint128 index_) {
return latestIndex;
}
/* ============ Internal Interactive Functions ============ */
/**
* @dev Adds principal to `_balances` of an earning account.
* @param account_ The account to add principal to.
* @param principalAmount_ The principal amount to add.
*/
function _addEarningAmount(address account_, uint112 principalAmount_) internal {
// NOTE: Safe to use unchecked here since overflow of the total supply is checked in `_mint`.
unchecked {
_balances[account_].rawBalance += principalAmount_;
principalOfTotalEarningSupply += principalAmount_;
}
}
/**
* @dev Adds amount to `_balances` of a non-earning account.
* @param account_ The account to add amount to.
* @param amount_ The amount to add.
*/
function _addNonEarningAmount(address account_, uint240 amount_) internal {
// NOTE: Safe to use unchecked here since overflow of the total supply is checked in `_mint`.
unchecked {
_balances[account_].rawBalance += amount_;
totalNonEarningSupply += amount_;
}
}
/**
* @dev Burns amount of earning or non-earning M from account.
* @param account_ The account to burn from.
* @param amount_ The present amount to burn.
*/
function _burn(address account_, uint256 amount_) internal {
_revertIfInsufficientAmount(amount_);
emit Transfer(account_, address(0), amount_);
if (_balances[account_].isEarning) {
// NOTE: When burning a present amount, round the principal up in favor of the protocol.
_subtractEarningAmount(account_, _getPrincipalAmountRoundedUp(UIntMath.safe240(amount_)));
} else {
_subtractNonEarningAmount(account_, UIntMath.safe240(amount_));
}
}
/**
* @dev Mints amount of earning or non-earning M to account.
* @param recipient_ The account to mint to.
* @param amount_ The present amount to mint.
*/
function _mint(address recipient_, uint256 amount_) internal {
_revertIfInsufficientAmount(amount_);
_revertIfInvalidRecipient(recipient_);
emit Transfer(address(0), recipient_, amount_);
uint240 safeAmount_ = UIntMath.safe240(amount_);
unchecked {
// As an edge case precaution, prevent a mint that, if all tokens (earning and non-earning) were converted
// to a principal earning amount, would overflow the `uint112 principalOfTotalEarningSupply`.
if (
uint256(totalNonEarningSupply) + safeAmount_ > type(uint240).max ||
// NOTE: Round the principal up for worst case.
uint256(principalOfTotalEarningSupply) +
_getPrincipalAmountRoundedUp(totalNonEarningSupply + safeAmount_) >=
type(uint112).max
) {
revert OverflowsPrincipalOfTotalSupply();
}
}
if (_balances[recipient_].isEarning) {
// NOTE: When minting a present amount, round the principal down in favor of the protocol.
_addEarningAmount(recipient_, _getPrincipalAmountRoundedDown(safeAmount_));
} else {
_addNonEarningAmount(recipient_, safeAmount_);
}
}
/**
* @dev Starts earning for account.
* @param account_ The account to start earning for.
*/
function _startEarning(address account_) internal {
MBalance storage mBalance_ = _balances[account_];
if (mBalance_.isEarning) return;
emit StartedEarning(account_);
mBalance_.isEarning = true;
// Treat the raw balance as present amount for non earner.
uint240 amount_ = mBalance_.rawBalance;
if (amount_ == 0) return;
// NOTE: When converting a non-earning balance into an earning balance,
// round the principal down in favor of the protocol.
uint112 principalAmount_ = _getPrincipalAmountRoundedDown(amount_);
_balances[account_].rawBalance = principalAmount_;
unchecked {
principalOfTotalEarningSupply += principalAmount_;
totalNonEarningSupply -= amount_;
}
}
/**
* @dev Stops earning for account.
* @param account_ The account to stop earning for.
*/
function _stopEarning(address account_) internal {
MBalance storage mBalance_ = _balances[account_];
if (!mBalance_.isEarning) return;
emit StoppedEarning(account_);
mBalance_.isEarning = false;
// Treat the raw balance as principal for earner.
uint112 principalAmount_ = uint112(_balances[account_].rawBalance);
if (principalAmount_ == 0) return;
uint240 amount_ = _getPresentAmount(principalAmount_);
_balances[account_].rawBalance = amount_;
unchecked {
totalNonEarningSupply += amount_;
principalOfTotalEarningSupply -= principalAmount_;
}
}
/**
* @dev Subtracts principal from `_balances` of an earning account.
* @param account_ The account to subtract principal from.
* @param principalAmount_ The principal amount to subtract.
*/
function _subtractEarningAmount(address account_, uint112 principalAmount_) internal {
uint256 rawBalance_ = _balances[account_].rawBalance;
if (rawBalance_ < principalAmount_) revert InsufficientBalance(account_, rawBalance_, principalAmount_);
unchecked {
// Overflow not possible given the above check.
_balances[account_].rawBalance -= principalAmount_;
principalOfTotalEarningSupply -= principalAmount_;
}
}
/**
* @dev Subtracts amount from `_balances` of a non-earning account.
* @param account_ The account to subtract amount from.
* @param amount_ The amount to subtract.
*/
function _subtractNonEarningAmount(address account_, uint240 amount_) internal {
uint256 rawBalance_ = _balances[account_].rawBalance;
if (rawBalance_ < amount_) revert InsufficientBalance(account_, rawBalance_, amount_);
unchecked {
// Overflow not possible given the above check.
_balances[account_].rawBalance -= amount_;
totalNonEarningSupply -= amount_;
}
}
/**
* @dev Transfer M between both earning and non-earning accounts.
* @param sender_ The account to transfer from. It can be either earning or non-earning account.
* @param recipient_ The account to transfer to. It can be either earning or non-earning account.
* @param amount_ The present amount to transfer.
*/
function _transfer(address sender_, address recipient_, uint256 amount_) internal override {
_revertIfInvalidRecipient(recipient_);
emit Transfer(sender_, recipient_, amount_);
uint240 safeAmount_ = UIntMath.safe240(amount_);
bool senderIsEarning_ = _balances[sender_].isEarning; // Only using the sender's earning status more than once.
// If this is an in-kind transfer, then...
if (senderIsEarning_ == _balances[recipient_].isEarning) {
// NOTE: When subtracting a present amount from an earner, round the principal up in favor of the protocol.
return
_transferAmountInKind( // perform an in-kind transfer with...
sender_,
recipient_,
senderIsEarning_ ? _getPrincipalAmountRoundedUp(safeAmount_) : safeAmount_ // the appropriate amount
);
}
// If this is not an in-kind transfer, then...
if (senderIsEarning_) {
// either the sender is earning and the recipient is not, or...
// NOTE: When subtracting a present amount from an earner, round the principal up in favor of the protocol.
_subtractEarningAmount(sender_, _getPrincipalAmountRoundedUp(safeAmount_));
_addNonEarningAmount(recipient_, safeAmount_);
} else {
// the sender is not earning and the recipient is.
// NOTE: When adding a present amount to an earner, round the principal down in favor of the protocol.
_subtractNonEarningAmount(sender_, safeAmount_);
_addEarningAmount(recipient_, _getPrincipalAmountRoundedDown(safeAmount_));
}
}
/**
* @dev Transfer M between same earning status accounts.
* @param sender_ The account to transfer from.
* @param recipient_ The account to transfer to.
* @param amount_ The amount (present or principal) to transfer.
*/
function _transferAmountInKind(address sender_, address recipient_, uint240 amount_) internal {
uint256 rawBalance_ = _balances[sender_].rawBalance;
if (rawBalance_ < amount_) revert InsufficientBalance(sender_, rawBalance_, amount_);
// NOTE: When transferring an amount in kind, the `rawBalance` can't overflow
// since the total supply would have overflowed first when minting.
unchecked {
_balances[sender_].rawBalance -= amount_;
_balances[recipient_].rawBalance += amount_;
}
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns the present amount (rounded down) given the principal amount, using the current index.
* All present amounts are rounded down in favor of the protocol.
* @param principalAmount_ The principal amount.
* @return The present amount.
*/
function _getPresentAmount(uint112 principalAmount_) internal view returns (uint240) {
return _getPresentAmount(principalAmount_, currentIndex());
}
/**
* @dev Returns the present amount (rounded down) given the principal amount and an index.
* All present amounts are rounded down in favor of the protocol, since they are assets.
* @param principalAmount_ The principal amount.
* @param index_ An index
* @return The present amount.
*/
function _getPresentAmount(uint112 principalAmount_, uint128 index_) internal pure returns (uint240) {
return _getPresentAmountRoundedDown(principalAmount_, index_);
}
/**
* @dev Checks if earner was approved by the Registrar.
* @param account_ The account to check.
* @return True if approved, false otherwise.
*/
function _isApprovedEarner(address account_) internal view returns (bool) {
return RegistrarReader.isEarnersListIgnored(registrar) || RegistrarReader.isApprovedEarner(registrar, account_);
}
/**
* @dev Reverts if the amount of a `mint` or `burn` is equal to 0.
* @param amount_ Amount to check.
*/
function _revertIfInsufficientAmount(uint256 amount_) internal pure {
if (amount_ == 0) revert InsufficientAmount(amount_);
}
/**
* @dev Reverts if the recipient of a `mint` or `transfer` is address(0).
* @param recipient_ Address of the recipient to check.
*/
function _revertIfInvalidRecipient(address recipient_) internal pure {
if (recipient_ == address(0)) revert InvalidRecipient(recipient_);
}
/// @dev Reverts if the caller is not the portal.
function _revertIfNotPortal() internal view {
if (msg.sender != portal) revert NotPortal();
}
/// @inheritdoc Migratable
function _getMigrator() internal pure override returns (address migrator_) {
// NOTE: in this version only the admin-controlled migration via `migrate()` function is supported
return address(0);
}
}// 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: 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 uint128 values and returns the larger one.
* @param a Value to compare.
* @param b Value to compare.
* @return The larger value.
*/
function max128(uint128 a, uint128 b) internal pure returns (uint128) {
return a > b ? a : b;
}
/**
* @notice Compares two uint240 values and returns the larger one.
* @param a Value to compare.
* @param b Value to compare.
* @return The larger value.
*/
function max240(uint240 a, uint240 b) internal pure returns (uint240) {
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;
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);
}// 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.26;
import { IRegistrar } from "../interfaces/IRegistrar.sol";
/**
* @title Library to read Registrar contract parameters.
* @author M^0 Labs
*/
library RegistrarReader {
/* ============ Variables ============ */
/// @notice The parameter name in the Registrar that defines the earners list.
bytes32 internal constant EARNERS_LIST = "earners";
/// @notice The parameter name in the Registrar that defines whether to ignore the earners list.
bytes32 internal constant EARNERS_LIST_IGNORED = "earners_list_ignored";
/* ============ Internal View/Pure Functions ============ */
/// @notice Checks if the given earner is approved.
function isApprovedEarner(address registrar_, address earner_) internal view returns (bool) {
return _contains(registrar_, EARNERS_LIST, earner_);
}
/// @notice Checks if the `earners_list_ignored` exists.
function isEarnersListIgnored(address registrar_) internal view returns (bool) {
return _get(registrar_, EARNERS_LIST_IGNORED) != bytes32(0);
}
/// @notice Gets the Portal contract address.
function getPortal(address registrar_) internal view returns (address) {
return IRegistrar(registrar_).portal();
}
/// @notice Checks if the given list contains the given account.
function _contains(address registrar_, bytes32 listName_, address account_) private view returns (bool) {
return IRegistrar(registrar_).listContains(listName_, account_);
}
/// @notice Gets the value of the given key.
function _get(address registrar_, bytes32 key_) private view returns (bytes32) {
return IRegistrar(registrar_).get(key_);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.26;
/**
* @title Continuous Indexing Interface.
* @author M^0 Labs
*/
interface IContinuousIndexing {
/* ============ Events ============ */
/**
* @notice Emitted when the index is updated.
* @param index The new index.
*/
event IndexUpdated(uint128 indexed index);
/* ============ Custom Error ============ */
/**
* @notice Emitted during index update when the new index is less than the current one.
* @param index The new index.
* @param currentIndex The current index.
*/
error DecreasingIndex(uint128 index, uint128 currentIndex);
/* ============ View/Pure Functions ============ */
/// @notice The current index that was last written to storage when `updatedIndex` was called.
function currentIndex() external view returns (uint128);
/// @notice The latest updated index.
function latestIndex() external view returns (uint128);
/// @notice The latest timestamp when the index was updated.
function latestUpdateTimestamp() external view returns (uint40);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.26;
import { IERC20Extended } from "../../lib/common/src/interfaces/IERC20Extended.sol";
import { IContinuousIndexing } from "./IContinuousIndexing.sol";
/**
* @title M Token Interface.
* @author M^0 Labs
*/
interface IMToken is IContinuousIndexing, IERC20Extended {
/* ============ Events ============ */
/**
* @notice Emitted when account starts being an M earner.
* @param account The account that started earning.
*/
event StartedEarning(address indexed account);
/**
* @notice Emitted when account stops being an M earner.
* @param account The account that stopped earning.
*/
event StoppedEarning(address indexed account);
/* ============ Custom Errors ============ */
/// @notice Emitted when the index from the Hub chain has not yet been propagated to the Spoke chain.
error IndexNotInitialized();
/**
* @notice Emitted when there is insufficient balance to decrement from `account`.
* @param account The account with insufficient balance.
* @param rawBalance The raw balance of the account.
* @param amount The amount to decrement the `rawBalance` by.
*/
error InsufficientBalance(address account, uint256 rawBalance, uint256 amount);
/// @notice Emitted when calling `stopEarning` for an account approved as earner by the Registrar.
error IsApprovedEarner();
/// @notice Emitted when calling `startEarning` for an account not approved as earner by the Registrar.
error NotApprovedEarner();
/// @notice Emitted when the caller is not the Portal.
error NotPortal();
/// @notice Emitted when principal of total supply (earning and non-earning) will overflow a `type(uint112).max`.
error OverflowsPrincipalOfTotalSupply();
/// @notice Emitted when the migrate function is called by a account other than the migration admin.
error UnauthorizedMigration();
/// @notice Emitted in constructor if the Portal address in the Registrar is 0x0.
error ZeroPortal();
/// @notice Emitted in constructor if the Registrar address is 0x0.
error ZeroRegistrar();
/// @notice Emitted in constructor if Migration Admin is 0x0.
error ZeroMigrationAdmin();
/* ============ Interactive Functions ============ */
/**
* @notice Updates the index and mints tokens.
* @dev MUST only be callable by the Spoke Portal.
* @param account The address of account to mint to.
* @param amount The amount of M Token to mint.
* @param index The index to update to.
*/
function mint(address account, uint256 amount, uint128 index) external;
/**
* @notice Mints tokens.
* @dev MUST only be callable by the Spoke Portal.
* @param account The address of account to mint to.
* @param amount The amount of M Token to mint.
*/
function mint(address account, uint256 amount) external;
/**
* @notice Burns `amount` of M tokens from `msg.sender`.
* @dev MUST only be callable by the Spoke Portal.
* @param amount The amount of M Token to burn.
*/
function burn(uint256 amount) external;
/**
* @notice Updates the latest index and latest accrual time in storage.
* @dev MUST only be callable by the Spoke Portal.
* @param index The new index to compute present amounts from principal amounts.
*/
function updateIndex(uint128 index) external;
/// @notice Starts earning for caller if allowed by TTG.
function startEarning() external;
/// @notice Stops earning for caller.
function stopEarning() external;
/**
* @notice Stops earning for `account`.
* @dev MUST revert if `account` is an approved earner in the Registrar.
* @param account The account to stop earning for.
*/
function stopEarning(address account) external;
/// @notice Initializes the Proxy's storage.
function initialize() external;
/**
* @notice Performs an arbitrarily defined migration.
* @param migrator The address of a migrator contract.
*/
function migrate(address migrator) external;
/* ============ View/Pure Functions ============ */
/// @notice The address of the M Portal contract.
function portal() external view returns (address);
/// @notice The address of the Registrar contract.
function registrar() external view returns (address);
/// @notice The account that can call the `migrate(address migrator)` function.
function migrationAdmin() external view returns (address migrationAdmin);
/**
* @notice The principal of an earner M token balance.
* @param account The account to get the principal balance of.
* @return The principal balance of the account.
*/
function principalBalanceOf(address account) external view returns (uint240);
/// @notice The principal of the total earning supply of M Token.
function principalOfTotalEarningSupply() external view returns (uint112);
/// @notice The total earning supply of M Token.
function totalEarningSupply() external view returns (uint240);
/// @notice The total non-earning supply of M Token.
function totalNonEarningSupply() external view returns (uint240);
/**
* @notice Checks if account is an earner.
* @param account The account to check.
* @return True if account is an earner, false otherwise.
*/
function isEarning(address account) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.26;
import { Initializable } from "../../lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol";
import { IContinuousIndexing } from "../interfaces/IContinuousIndexing.sol";
import { ContinuousIndexingMath } from "../libs/ContinuousIndexingMath.sol";
/**
* @title Abstract Continuous Indexing Contract to handle index updates in inheriting contracts.
* @author M^0 Labs
*/
abstract contract ContinuousIndexing is IContinuousIndexing, Initializable {
/* ============ Variables ============ */
/// @inheritdoc IContinuousIndexing
uint128 public latestIndex;
/// @inheritdoc IContinuousIndexing
uint40 public latestUpdateTimestamp;
/* ============ Initializer ============ */
/// @notice Initializes Proxy's storage.
function _initialize() internal onlyInitializing {
latestIndex = ContinuousIndexingMath.EXP_SCALED_ONE;
latestUpdateTimestamp = uint40(block.timestamp);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IContinuousIndexing
function currentIndex() public view virtual returns (uint128);
/* ============ Internal Interactive Functions ============ */
/**
* @notice Updates the latest index and latest accrual time.
* @param index_ The new index to compute present amounts from principal amounts.
*/
function _updateIndex(uint128 index_) internal virtual {
if (index_ < latestIndex) revert DecreasingIndex(index_, latestIndex);
latestIndex = index_;
latestUpdateTimestamp = uint40(block.timestamp);
emit IndexUpdated(index_);
}
/* ============ Internal View/Pure Functions ============ */
/**
* @dev Returns the principal amount (rounded down) given the present amount, using the current index.
* @param presentAmount_ The present amount.
* @return The principal amount rounded down.
*/
function _getPrincipalAmountRoundedDown(uint240 presentAmount_) internal view returns (uint112) {
return _getPrincipalAmountRoundedDown(presentAmount_, currentIndex());
}
/**
* @dev Returns the principal amount (rounded up) given the present amount and an index.
* @param presentAmount_ The present amount.
* @return The principal amount rounded up.
*/
function _getPrincipalAmountRoundedUp(uint240 presentAmount_) internal view returns (uint112) {
return _getPrincipalAmountRoundedUp(presentAmount_, currentIndex());
}
/**
* @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 ContinuousIndexingMath.multiplyDown(principalAmount_, index_);
}
/**
* @dev Returns the present amount (rounded up) given the principal amount and an index.
* @param principalAmount_ The principal amount.
* @param index_ An index.
* @return The present amount rounded up.
*/
function _getPresentAmountRoundedUp(uint112 principalAmount_, uint128 index_) internal pure returns (uint240) {
return ContinuousIndexingMath.multiplyUp(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 ContinuousIndexingMath.divideDown(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 ContinuousIndexingMath.divideUp(presentAmount_, index_);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.26;
import { UIntMath } from "../../lib/common/src/libs/UIntMath.sol";
/**
* @title Arithmetic library with operations for calculating continuous indexing.
* @author M^0 Labs
*/
library ContinuousIndexingMath {
/* ============ Variables ============ */
/// @notice The scaling of rates in 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) / index`, rounded down.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function divideDown(uint240 x, uint128 index) internal pure returns (uint112 z) {
if (index == 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
// `multiplyDown` or `multiplyUp`, which would already satisfy
// `uint256(x) * EXP_SCALED_ONE < type(uint240).max`.
return UIntMath.safe112((uint256(x) * EXP_SCALED_ONE) / index);
}
}
/**
* @notice Helper function to calculate `(x * EXP_SCALED_ONE) / index`, rounded up.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function divideUp(uint240 x, uint128 index) internal pure returns (uint112 z) {
if (index == 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
// `multiplyDown` or `multiplyUp`, which would already satisfy
// `uint256(x) * EXP_SCALED_ONE < type(uint240).max`.
return UIntMath.safe112(((uint256(x) * EXP_SCALED_ONE) + index - 1) / index);
}
}
/**
* @notice Helper function to calculate `(x * index) / EXP_SCALED_ONE`, rounded down.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function multiplyDown(uint112 x, uint128 index) internal pure returns (uint240 z) {
unchecked {
return uint240((uint256(x) * index) / EXP_SCALED_ONE);
}
}
/**
* @notice Helper function to calculate `(x * index) / EXP_SCALED_ONE`, rounded up.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function multiplyUp(uint112 x, uint128 index) internal pure returns (uint240 z) {
unchecked {
return uint240(((uint256(x) * index) + (EXP_SCALED_ONE - 1)) / EXP_SCALED_ONE);
}
}
}// 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: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
/**
* @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 the Registrar.
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);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.26;
/**
* @title Registrar interface.
* @author M^0 Labs
*/
interface IRegistrar {
/**
* @notice Key value pair getter.
* @param key The key to get the value of.
* @return value The value of the key.
*/
function get(bytes32 key) external view returns (bytes32 value);
/**
* @notice Checks if the list contains the account.
* @param list The list to check.
* @param account The account to check.
* @return contains True if the list contains the account, false otherwise.
*/
function listContains(bytes32 list, address account) external view returns (bool contains);
/// @notice Returns the Portal contract address.
function portal() external view returns (address portal);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reinitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;
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": [
"@ensdomains/=lib/uniswap-v4-periphery/lib/v4-core/node_modules/@ensdomains/",
"@openzeppelin/=lib/uniswap-v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"@uniswap/v4-core/=lib/uniswap-v4-periphery/lib/v4-core/",
"common/=lib/common/src/",
"ds-test/=lib/uniswap-v4-periphery/lib/v4-core/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-gas-snapshot/=lib/uniswap-v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"hardhat/=lib/uniswap-v4-periphery/lib/v4-core/node_modules/hardhat/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
"openzeppelin/=lib/openzeppelin/",
"permit2/=lib/uniswap-v4-periphery/lib/permit2/",
"protocol/=lib/protocol/",
"solmate/=lib/uniswap-v4-periphery/lib/v4-core/lib/solmate/",
"ttg/=lib/ttg/",
"uniswap-v4-periphery/=lib/uniswap-v4-periphery/",
"v4-core/=lib/uniswap-v4-periphery/lib/v4-core/src/",
"wrapped-m-token/=lib/wrapped-m-token/"
],
"optimizer": {
"enabled": true,
"runs": 10000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"registrar_","type":"address"},{"internalType":"address","name":"portal_","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":[{"internalType":"uint128","name":"index","type":"uint128"},{"internalType":"uint128","name":"currentIndex","type":"uint128"}],"name":"DecreasingIndex","type":"error"},{"inputs":[],"name":"DivisionByZero","type":"error"},{"inputs":[],"name":"IndexNotInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientAmount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"rawBalance","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expectedNonce","type":"uint256"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"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":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotPortal","type":"error"},{"inputs":[],"name":"OverflowsPrincipalOfTotalSupply","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":"ZeroMigrationAdmin","type":"error"},{"inputs":[],"name":"ZeroMigrator","type":"error"},{"inputs":[],"name":"ZeroPortal","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":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint128","name":"index","type":"uint128"}],"name":"IndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"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"},{"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":"uint256","name":"amount_","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","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":"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":"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":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"isEarning","outputs":[{"internalType":"bool","name":"isEarning_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestUpdateTimestamp","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"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":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint128","name":"index_","type":"uint128"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","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":"portal","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"principalBalanceOf","outputs":[{"internalType":"uint240","name":"balance_","type":"uint240"}],"stateMutability":"view","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":[],"name":"startEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"stopEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stopEarning","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEarningSupply","outputs":[{"internalType":"uint240","name":"totalEarningSupply_","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":"uint128","name":"index_","type":"uint128"}],"name":"updateIndex","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
61018060405234801561001157600080fd5b50604051613f4e380380613f4e833981016040819052610030916103ce565b6040518060400160405280600881526020016704d206279204d5e360c41b815250604051806040016040528060018152602001604d60f81b815250600682808061007f8161014360201b60201c565b60c0524660805261008e610174565b60a0525061009f9150839050610143565b6101005260ff1660e052506100b4905061020f565b6001600160a01b0383166101408190526100e1576040516379a6314960e01b815260040160405180910390fd5b6001600160a01b03821661012081905261010e57604051631b973f8d60e11b815260040160405180910390fd5b6001600160a01b03811661016081905261013b5760405163ea0b51bb60e01b815260040160405180910390fd5b5050506104ba565b6000816040516020016101569190610411565b60405160208183030381529060405261016e90610440565b92915050565b60c0516000907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f906101a5906102ad565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6000610219610389565b805490915068010000000000000000900460ff161561024b5760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146102aa5780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b606060005b6020811080156102d357508281602081106102cf576102cf610467565b1a15155b156102e8576102e18161047d565b90506102b2565b6000816001600160401b03811115610302576103026104a4565b6040519080825280601f01601f19166020018201604052801561032c576020820181803683370190505b50905060005b828110156103815784816020811061034c5761034c610467565b1a60f81b82828151811061036257610362610467565b60200101906001600160f81b031916908160001a905350600101610332565b509392505050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061016e565b80516001600160a01b03811681146103c957600080fd5b919050565b6000806000606084860312156103e357600080fd5b6103ec846103b2565b92506103fa602085016103b2565b9150610408604085016103b2565b90509250925092565b6000825160005b818110156104325760208186018101518583015201610418565b506000920191825250919050565b80516020808301519190811015610461576000198160200360031b1b821691505b50919050565b634e487b7160e01b600052603260045260246000fd5b60006001820161049d57634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052604160045260246000fd5b60805160a05160c05160e05161010051610120516101405161016051613a05610549600039600081816106050152610f9b01526000818161040c01528181611a2c0152611a5b0152600081816105cb01526114ce01526000610de20152600061047201526000818161089d01528181610ce6015261143501526000610a0b015260006109db0152613a056000f3fe608060405234801561001057600080fd5b50600436106103365760003560e01c806381399be4116101b2578063a9059cbb116100f9578063d505accf116100a2578063e3ee160e1161007c578063e3ee160e1461082f578063e94a010214610842578063ef55bec614610870578063fc387d5a1461088357600080fd5b8063d505accf146107ca578063d9169487146107dd578063dd62ed3e1461080457600080fd5b8063c634dfaa116100d3578063c634dfaa14610791578063ce5494bb146107a4578063cf092995146107b757600080fd5b8063a9059cbb14610758578063ace150a51461076b578063b7b728991461077e57600080fd5b806395d89b411161015b578063a08cb48b11610135578063a08cb48b14610716578063a0cc6a6814610729578063a36e40fc1461075057600080fd5b806395d89b41146106f35780639f8495f9146106fb5780639fd5a6cf1461070357600080fd5b806388b7ab631161018c57806388b7ab63146106d05780638a75f238146106e35780638fd3ab80146106eb57600080fd5b806381399be41461067657806384af270f1461068957806384b0196e146106b557600080fd5b8063499d1081116102815780635c60da1b1161022a57806374df25581161020457806374df2558146106005780637ecebe00146106275780637f2eecc3146106475780638129fc1c1461066e57600080fd5b80635c60da1b1461059f5780636425666b146105c657806370a08231146105ed57600080fd5b806353d96f2c1161025b57806353d96f2c14610535578063578f2aa0146105705780635a049a701461058c57600080fd5b8063499d1081146104d65780634c57a8fa146104e9578063532992c51461052257600080fd5b80632b20e397116102e35780633644e515116102bd5780633644e515146104a657806340c10f19146104ae57806342966c68146104c357600080fd5b80632b20e3971461040757806330adf81f14610446578063313ce5671461046d57600080fd5b806323b872dd1161031457806323b872dd1461039257806326987b60146103a5578063281b229d146103dc57600080fd5b806306fdde031461033b578063095ea7b31461035957806318160ddd1461037c575b600080fd5b610343610896565b6040516103509190613217565b60405180910390f35b61036c610367366004613246565b6108c6565b6040519015158152602001610350565b6103846108dc565b604051908152602001610350565b61036c6103a0366004613270565b6108fe565b6000546fffffffffffffffffffffffffffffffff165b6040516fffffffffffffffffffffffffffffffff9091168152602001610350565b6004546103ef906001600160f01b031681565b6040516001600160f01b039091168152602001610350565b61042e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610350565b6103847f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6104947f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610350565b6103846109d7565b6104c16104bc366004613246565b610a2d565b005b6104c16104d13660046132ad565b610a43565b6104c16104e43660046132e6565b610a58565b600554610503906dffffffffffffffffffffffffffff1681565b6040516dffffffffffffffffffffffffffff9091168152602001610350565b6104c1610530366004613322565b610a78565b60005461055a90700100000000000000000000000000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610350565b6000546103bb906fffffffffffffffffffffffffffffffff1681565b6104c161059a36600461336c565b610a9d565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5461042e565b61042e7f000000000000000000000000000000000000000000000000000000000000000081565b6103846105fb3660046133ba565b610ac4565b61042e7f000000000000000000000000000000000000000000000000000000000000000081565b6103846106353660046133ba565b60016020526000908152604090205481565b6103847fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6104c1610b23565b6104c16106843660046133ba565b610c8c565b61036c6106973660046133ba565b6001600160a01b031660009081526006602052604090205460ff1690565b6106bd610cd5565b60405161035097969594939291906133d5565b6104c16106de366004613584565b610d81565b6103ef610db0565b6104c1610dcf565b610343610ddb565b6104c1610e06565b6104c1610711366004613609565b610e0f565b6104c161072436600461367b565b610e1f565b6103847f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b6104c1610e49565b61036c610766366004613246565b610efb565b6104c161077936600461367b565b610f08565b6104c161078c3660046136e2565b610f28565b6103ef61079f3660046133ba565b610f40565b6104c16107b23660046133ba565b610f90565b6104c16107c5366004613584565b610ffb565b6104c16107d8366004613739565b61101b565b6103847f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6103846108123660046137a4565b600360209081526000928352604080842090915290825290205481565b6104c161083d3660046137d7565b61102b565b61036c610850366004613246565b600260209081526000928352604080842090915290825290205460ff1681565b6104c161087e3660046137d7565b611056565b6104c1610891366004613858565b611076565b60606108c17f0000000000000000000000000000000000000000000000000000000000000000611087565b905090565b60006108d333848461117c565b50600192915050565b60006108e6610db0565b6004546001600160f01b039081169190910116919050565b6001600160a01b03831660009081526003602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109c15782811015610998576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018490526064015b60405180910390fd5b6001600160a01b0385166000908152600360209081526040808320338452909152902083820390555b6109cc8585856111dd565b506001949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610a08576108c161140d565b507f000000000000000000000000000000000000000000000000000000000000000090565b610a356114c3565b610a3f8282611525565b5050565b610a4b6114c3565b610a5533826116da565b50565b610a606114c3565b610a698161176c565b610a738383611525565b505050565b610a8d84610a868686611864565b84846118d3565b610a9784846118e7565b50505050565b610ab385610aab8787611864565b858585611969565b610abd85856118e7565b5050505050565b6001600160a01b0381166000908152600660205260408120805460ff16610afa57805461010090046001600160f01b0316610b13565b8054610b139061010090046001600160f01b0316611979565b6001600160f01b03169392505050565b6000610b2d6119a5565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015610b5a5750825b905060008267ffffffffffffffff166001148015610b775750303b155b905081158015610b85575080155b15610bbc576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610c1d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610c256119ce565b8315610abd5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b610c9581611a25565b15610ccc576040517f8b19807700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5581611a80565b600060608060008060006060610d0a7f0000000000000000000000000000000000000000000000000000000000000000611087565b6040805160008082526060820183526001602083019081527f3100000000000000000000000000000000000000000000000000000000000000938301939093527f0f000000000000000000000000000000000000000000000000000000000000009b939a5091985046975030965090945092509050565b610d9987610d93898989898989611c26565b83611ca1565b610da7878787878787611cee565b50505050505050565b6005546000906108c1906dffffffffffffffffffffffffffff16611979565b610dd96000611d57565b565b60606108c17f0000000000000000000000000000000000000000000000000000000000000000611087565b610dd933611a80565b610abd85610d9387878787611f41565b610e3188610a868a8a8a8a8a8a611c26565b610e3f888888888888611cee565b5050505050505050565b610e5233611a25565b610e88576040517fdd93dca800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64e8d4a51000610ea96000546fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1603610ef2576040517f5dcd0a3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd933611fdc565b60006108d33384846111dd565b610f1a88610a868a8a8a8a8a8a61215b565b610e3f8888888888886121cb565b610f3683610d938585611864565b610a7383836118e7565b6001600160a01b0381166000908152600660205260408120805460ff16610f68576000610f79565b805461010090046001600160f01b03165b6dffffffffffffffffffffffffffff169392505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610ff2576040517f51e2625e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5581611d57565b61100d87610d9389898989898961215b565b610da78787878787876121cb565b610da787610aab89898989611f41565b61103d89610aab8b8b8b8b8b8b61215b565b61104b8989898989896121cb565b505050505050505050565b61106889610aab8b8b8b8b8b8b611c26565b61104b898989898989611cee565b61107e6114c3565b610a558161176c565b606060005b6020811080156110ad57508281602081106110a9576110a9613873565b1a15155b156110c2576110bb816138a2565b905061108c565b60008167ffffffffffffffff8111156110dd576110dd613489565b6040519080825280601f01601f191660200182016040528015611107576020820181803683370190505b50905060005b828110156111745784816020811061112757611127613873565b1a60f81b82828151811061113d5761113d613873565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060010161110d565b509392505050565b6001600160a01b0383811660008181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6111e6826122d8565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161122b91815260200190565b60405180910390a3600061123e82612323565b6001600160a01b0380861660009081526006602052604080822054928716825290205491925060ff90811691161515811515036112a357610abd8585836112855784612390565b61128e8561236a565b6dffffffffffffffffffffffffffff16612390565b801561134f576112bb856112b68461236a565b612492565b6001600160a01b038416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290046001600160f01b039081168601811690920217909155600480547fffff00000000000000000000000000000000000000000000000000000000000081169083168501909216919091179055610abd565b61135985836125d4565b610abd84611366846126f1565b6001600160a01b0391909116600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff81166dffffffffffffffffffffffffffff808516610100938490046001600160f01b03908116919091011690920217909155600580547fffffffffffffffffffffffffffffffffffff00000000000000000000000000008116908316909301909116919091179055565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6114597f0000000000000000000000000000000000000000000000000000000000000000611087565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610dd9576040517f3f94bdf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61152e81612717565b611537826122d8565b6040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3600061158382612323565b6004549091506001600160f01b039081168183160111806115e157506004546dffffffffffffffffffffffffffff906115c6906001600160f01b0316830161236a565b6005546dffffffffffffffffffffffffffff90811691160110155b15611618576040517f5165589500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660009081526006602052604090205460ff161561164657610a7383611366836126f1565b6001600160a01b038316600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290046001600160f01b039081168501811690920217909155600480547fffff00000000000000000000000000000000000000000000000000000000000081169083168401909216919091179055505050565b6116e381612717565b6040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a36001600160a01b03821660009081526006602052604090205460ff161561175a57610a3f826112b661175584612323565b61236a565b610a3f8261176783612323565b6125d4565b6000546fffffffffffffffffffffffffffffffff90811690821610156117de576000546040517fecb4124f0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8084166004830152909116602482015260440161098f565b600080546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffff00000000000000000000000000000000000000000090911681177001000000000000000000000000000000004264ffffffffff160217825560405190917fce8d5137687211bba395deb2f8d0635a370c6d4a302be2506a529b0942ce267191a250565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b03841691810191909152606081018290526000906118cc906080015b60405160208183030381529060405280519060200120612754565b9392505050565b610a976118e2858585856127b6565b6127f4565b6118f182826129b9565b6001600160a01b038216600081815260026020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610abd6118e28686868686612a29565b600061199f8261199a6000546fffffffffffffffffffffffffffffffff1690565b612a74565b92915050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061199f565b6119d6612a80565b6000805464ffffffffff4216700100000000000000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009091161764e8d4a51000179055565b6000611a507f0000000000000000000000000000000000000000000000000000000000000000612abe565b8061199f575061199f7f000000000000000000000000000000000000000000000000000000000000000083612af3565b6001600160a01b0381166000908152600660205260409020805460ff16611aa5575050565b6040516001600160a01b038316907f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d90600090a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681556001600160a01b03821660009081526006602052604081205461010090046001600160f01b038116916dffffffffffffffffffffffffffff9091169003611b4557505050565b6000611b5082611979565b6001600160a01b0394909416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166101006001600160f01b038088169190910291909117909155600480547fffff000000000000000000000000000000000000000000000000000000000000811690831690960190911694909417909355600580547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081166dffffffffffffffffffffffffffff91821693909303169190911790555050565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860208201526001600160a01b038089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090611c9690610100016118b1565b979650505050505050565b6000611cae848484612b20565b90506000816005811115611cc457611cc4613901565b03611ccf5750505050565b611cda848484612b67565b15611ce55750505050565b610a97816127f4565b336001600160a01b03861614611d41576040517f1c5939f30000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b038616602482015260440161098f565b611d4f8686868686866121cb565b505050505050565b6001600160a01b038116611d97576040517f0d626a3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b03163b600003611ddb576040517f8d1e7cf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611e057f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b90506000826001600160a01b0316604051600060405180830381855af49150503d8060008114611e51576040519150601f19603f3d011682016040523d82523d6000602084013e611e56565b606091505b5050905080611e91576040517fa27bfda200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ebb7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b9050806001600160a01b0316836001600160a01b0316856001600160a01b03167fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a60405160405180910390a46040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250505050565b6000611f4c82612ca7565b611f5785858561117c565b6001600160a01b038581166000818152600160208181526040928390208054928301905582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918101919091529182019290925291861660608301526080820185905260a082015260c08101839052611fd39060e0016118b1565b95945050505050565b6001600160a01b0381166000908152600660205260409020805460ff1615612002575050565b6040516001600160a01b038316907f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c90600090a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178082556001600160f01b0361010090910416600081900361207d57505050565b6000612088826126f1565b6001600160a01b0394909416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166dffffffffffffffffffffffffffff8087166101000291909117909155600580547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811690831690960190911694909417909355600480547fffff00000000000000000000000000000000000000000000000000000000000081166001600160f01b0391821693909303169190911790555050565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760208201526001600160a01b038089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090611c9690610100016118b1565b82421161220d576040517f498ff9a20000000000000000000000000000000000000000000000000000000081524260048201526024810184905260440161098f565b81421061224f576040517fb3fcd33e0000000000000000000000000000000000000000000000000000000081524260048201526024810183905260440161098f565b61225986826129b9565b6001600160a01b038616600081815260026020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a3611d4f8686866111dd565b6001600160a01b038116610a55576040517f17858bbe0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161098f565b60006001600160f01b03821115612366576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b600061199f8261238b6000546fffffffffffffffffffffffffffffffff1690565b612cea565b6001600160a01b0383166000908152600660205260409020546001600160f01b036101009091048116908216811015612416576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602481018290526001600160f01b038316604482015260640161098f565b506001600160a01b0392831660009081526006602052604080822080546001600160f01b036101008083048216879003821681027fff000000000000000000000000000000000000000000000000000000000000ff93841617909355959096168352912080548281048516909301909316029216919091179055565b6001600160a01b03821660009081526006602052604090205461010090046001600160f01b03166dffffffffffffffffffffffffffff821681101561252b576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526dffffffffffffffffffffffffffff8316604482015260640161098f565b506001600160a01b03909116600090815260066020526040902080546dffffffffffffffffffffffffffff8084166001600160f01b0361010080850482169290920316027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560058054808316939093039091167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000909216919091179055565b6001600160a01b0382166000908152600660205260409020546001600160f01b03610100909104811690821681101561265a576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526001600160f01b038316604482015260640161098f565b506001600160a01b03909116600090815260066020526040902080546001600160f01b0361010080830482168590038216027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560048054808316939093039091167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055565b600061199f826127126000546fffffffffffffffffffffffffffffffff1690565b612cf6565b80600003610a55576040517f77b8dde30000000000000000000000000000000000000000000000000000000081526004810182905260240161098f565b600061275e6109d7565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b60008060006127c6868686612d02565b909250905060008260058111156127df576127df613901565b146127ea5781611c96565b611c968782612d4a565b600081600581111561280857612808613901565b036128105750565b600181600581111561282457612824613901565b0361285b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281600581111561286f5761286f613901565b036128a6576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038160058111156128ba576128ba613901565b036128f1576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481600581111561290557612905613901565b0361293c576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600581600581111561295057612950613901565b03612987576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216600090815260026020908152604080832084845290915290205460ff1615610a3f576040517fd309466d0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024810182905260440161098f565b6000806000612a3a87878787612d75565b90925090506000826005811115612a5357612a53613901565b14612a5e5781612a68565b612a688882612d4a565b98975050505050505050565b60006118cc8383612e7d565b612a88612eb0565b610dd9576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080612aeb837f6561726e6572735f6c6973745f69676e6f726564000000000000000000000000612ecf565b141592915050565b60006118cc837f6561726e6572730000000000000000000000000000000000000000000000000084612f54565b6000806000612b2f8585612fea565b90925090506000826005811115612b4857612b48613901565b14612b535781612b5d565b612b5d8682612d4a565b9695505050505050565b6000806000856001600160a01b03168585604051602401612b89929190613930565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612c0a9190613949565b600060405180830381855afa9150503d8060008114612c45576040519150601f19603f3d011682016040523d82523d6000602084013e612c4a565b606091505b5091509150818015612c5e57506020815110155b8015612b5d575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612c9c9083016020908101908401613965565b149695505050505050565b80421115610a55576040517ff88f04900000000000000000000000000000000000000000000000000000000081526004810182905242602482015260440161098f565b60006118cc8383613031565b60006118cc83836130dd565b600080601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416612d3c87838884612d75565b935093505050935093915050565b6000816001600160a01b0316836001600160a01b031614612d6c5760056118cc565b50600092915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612dac5750600390506000612e74565b8460ff16601b14158015612dc457508460ff16601c14155b15612dd55750600490506000612e74565b60408051600081526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015612e28573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615612e6a57600081612e6f565b600160005b915091505b94509492505050565b600064e8d4a510006fffffffffffffffffffffffffffffffff83166dffffffffffffffffffffffffffff851602046118cc565b6000612eba6119a5565b5468010000000000000000900460ff16919050565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b03841690638eaa6ac090602401602060405180830381865afa158015612f30573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cc9190613965565b6040517fd7d1c1c0000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0382811660248301526000919085169063d7d1c1c090604401602060405180830381865afa158015612fbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fe2919061397e565b949350505050565b6000808251604114613002575060029050600061302a565b60208301516040840151606085015160001a919061302287848484612d75565b945094505050505b9250929050565b6000816fffffffffffffffffffffffffffffffff1660000361307f576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118cc6fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001600160f01b03861664e8d4a5100002820101816130d7576130d76139a0565b0461315f565b6000816fffffffffffffffffffffffffffffffff1660000361312b576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118cc6fffffffffffffffffffffffffffffffff83166001600160f01b03851664e8d4a5100002816130d7576130d76139a0565b60006dffffffffffffffffffffffffffff821115612366576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156131c45781810151838201526020016131ac565b50506000910152565b600081518084526131e58160208601602086016131a9565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006118cc60208301846131cd565b80356001600160a01b038116811461324157600080fd5b919050565b6000806040838503121561325957600080fd5b6132628361322a565b946020939093013593505050565b60008060006060848603121561328557600080fd5b61328e8461322a565b925061329c6020850161322a565b929592945050506040919091013590565b6000602082840312156132bf57600080fd5b5035919050565b80356fffffffffffffffffffffffffffffffff8116811461324157600080fd5b6000806000606084860312156132fb57600080fd5b6133048461322a565b925060208401359150613319604085016132c6565b90509250925092565b6000806000806080858703121561333857600080fd5b6133418561322a565b966020860135965060408601359560600135945092505050565b803560ff8116811461324157600080fd5b600080600080600060a0868803121561338457600080fd5b61338d8661322a565b9450602086013593506133a26040870161335b565b94979396509394606081013594506080013592915050565b6000602082840312156133cc57600080fd5b6118cc8261322a565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061341060e08301896131cd565b828103604084015261342281896131cd565b606084018890526001600160a01b038716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b8181101561347857835183526020938401939092019160010161345a565b50909b9a5050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126134c957600080fd5b813567ffffffffffffffff8111156134e3576134e3613489565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff8211171561354f5761354f613489565b60405281815283820160200185101561356757600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600080600060e0888a03121561359f57600080fd5b6135a88861322a565b96506135b66020890161322a565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff8111156135ee57600080fd5b6135fa8a828b016134b8565b91505092959891949750929550565b600080600080600060a0868803121561362157600080fd5b61362a8661322a565b94506136386020870161322a565b93506040860135925060608601359150608086013567ffffffffffffffff81111561366257600080fd5b61366e888289016134b8565b9150509295509295909350565b600080600080600080600080610100898b03121561369857600080fd5b6136a18961322a565b97506136af60208a0161322a565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b6000806000606084860312156136f757600080fd5b6137008461322a565b925060208401359150604084013567ffffffffffffffff81111561372357600080fd5b61372f868287016134b8565b9150509250925092565b600080600080600080600060e0888a03121561375457600080fd5b61375d8861322a565b965061376b6020890161322a565b955060408801359450606088013593506137876080890161335b565b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156137b757600080fd5b6137c08361322a565b91506137ce6020840161322a565b90509250929050565b60008060008060008060008060006101208a8c0312156137f657600080fd5b6137ff8a61322a565b985061380d60208b0161322a565b975060408a0135965060608a0135955060808a0135945060a08a0135935061383760c08b0161335b565b989b979a50959894979396929550929360e081013593506101000135919050565b60006020828403121561386a57600080fd5b6118cc826132c6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036138fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000612fe260408301846131cd565b6000825161395b8184602087016131a9565b9190910192915050565b60006020828403121561397757600080fd5b5051919050565b60006020828403121561399057600080fd5b815180151581146118cc57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220df783e511c5dd57d9bbd494eb2b5b0a7ccdefc1e768a2e63da033ca5cf1c205a64736f6c634300081a0033000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103365760003560e01c806381399be4116101b2578063a9059cbb116100f9578063d505accf116100a2578063e3ee160e1161007c578063e3ee160e1461082f578063e94a010214610842578063ef55bec614610870578063fc387d5a1461088357600080fd5b8063d505accf146107ca578063d9169487146107dd578063dd62ed3e1461080457600080fd5b8063c634dfaa116100d3578063c634dfaa14610791578063ce5494bb146107a4578063cf092995146107b757600080fd5b8063a9059cbb14610758578063ace150a51461076b578063b7b728991461077e57600080fd5b806395d89b411161015b578063a08cb48b11610135578063a08cb48b14610716578063a0cc6a6814610729578063a36e40fc1461075057600080fd5b806395d89b41146106f35780639f8495f9146106fb5780639fd5a6cf1461070357600080fd5b806388b7ab631161018c57806388b7ab63146106d05780638a75f238146106e35780638fd3ab80146106eb57600080fd5b806381399be41461067657806384af270f1461068957806384b0196e146106b557600080fd5b8063499d1081116102815780635c60da1b1161022a57806374df25581161020457806374df2558146106005780637ecebe00146106275780637f2eecc3146106475780638129fc1c1461066e57600080fd5b80635c60da1b1461059f5780636425666b146105c657806370a08231146105ed57600080fd5b806353d96f2c1161025b57806353d96f2c14610535578063578f2aa0146105705780635a049a701461058c57600080fd5b8063499d1081146104d65780634c57a8fa146104e9578063532992c51461052257600080fd5b80632b20e397116102e35780633644e515116102bd5780633644e515146104a657806340c10f19146104ae57806342966c68146104c357600080fd5b80632b20e3971461040757806330adf81f14610446578063313ce5671461046d57600080fd5b806323b872dd1161031457806323b872dd1461039257806326987b60146103a5578063281b229d146103dc57600080fd5b806306fdde031461033b578063095ea7b31461035957806318160ddd1461037c575b600080fd5b610343610896565b6040516103509190613217565b60405180910390f35b61036c610367366004613246565b6108c6565b6040519015158152602001610350565b6103846108dc565b604051908152602001610350565b61036c6103a0366004613270565b6108fe565b6000546fffffffffffffffffffffffffffffffff165b6040516fffffffffffffffffffffffffffffffff9091168152602001610350565b6004546103ef906001600160f01b031681565b6040516001600160f01b039091168152602001610350565b61042e7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c81565b6040516001600160a01b039091168152602001610350565b6103847f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6104947f000000000000000000000000000000000000000000000000000000000000000681565b60405160ff9091168152602001610350565b6103846109d7565b6104c16104bc366004613246565b610a2d565b005b6104c16104d13660046132ad565b610a43565b6104c16104e43660046132e6565b610a58565b600554610503906dffffffffffffffffffffffffffff1681565b6040516dffffffffffffffffffffffffffff9091168152602001610350565b6104c1610530366004613322565b610a78565b60005461055a90700100000000000000000000000000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610350565b6000546103bb906fffffffffffffffffffffffffffffffff1681565b6104c161059a36600461336c565b610a9d565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5461042e565b61042e7f00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece81565b6103846105fb3660046133ba565b610ac4565b61042e7f000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb81565b6103846106353660046133ba565b60016020526000908152604090205481565b6103847fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6104c1610b23565b6104c16106843660046133ba565b610c8c565b61036c6106973660046133ba565b6001600160a01b031660009081526006602052604090205460ff1690565b6106bd610cd5565b60405161035097969594939291906133d5565b6104c16106de366004613584565b610d81565b6103ef610db0565b6104c1610dcf565b610343610ddb565b6104c1610e06565b6104c1610711366004613609565b610e0f565b6104c161072436600461367b565b610e1f565b6103847f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b6104c1610e49565b61036c610766366004613246565b610efb565b6104c161077936600461367b565b610f08565b6104c161078c3660046136e2565b610f28565b6103ef61079f3660046133ba565b610f40565b6104c16107b23660046133ba565b610f90565b6104c16107c5366004613584565b610ffb565b6104c16107d8366004613739565b61101b565b6103847f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6103846108123660046137a4565b600360209081526000928352604080842090915290825290205481565b6104c161083d3660046137d7565b61102b565b61036c610850366004613246565b600260209081526000928352604080842090915290825290205460ff1681565b6104c161087e3660046137d7565b611056565b6104c1610891366004613858565b611076565b60606108c17f4d206279204d5e30000000000000000000000000000000000000000000000000611087565b905090565b60006108d333848461117c565b50600192915050565b60006108e6610db0565b6004546001600160f01b039081169190910116919050565b6001600160a01b03831660009081526003602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109c15782811015610998576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018490526064015b60405180910390fd5b6001600160a01b0385166000908152600360209081526040808320338452909152902083820390555b6109cc8585856111dd565b506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000e7084614610a08576108c161140d565b507f986055a5a77f4a884f055ed4659511cb4d51e6f4bf1b7688f26804571b2ccf2690565b610a356114c3565b610a3f8282611525565b5050565b610a4b6114c3565b610a5533826116da565b50565b610a606114c3565b610a698161176c565b610a738383611525565b505050565b610a8d84610a868686611864565b84846118d3565b610a9784846118e7565b50505050565b610ab385610aab8787611864565b858585611969565b610abd85856118e7565b5050505050565b6001600160a01b0381166000908152600660205260408120805460ff16610afa57805461010090046001600160f01b0316610b13565b8054610b139061010090046001600160f01b0316611979565b6001600160f01b03169392505050565b6000610b2d6119a5565b805490915060ff68010000000000000000820416159067ffffffffffffffff16600081158015610b5a5750825b905060008267ffffffffffffffff166001148015610b775750303b155b905081158015610b85575080155b15610bbc576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610c1d5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610c256119ce565b8315610abd5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b610c9581611a25565b15610ccc576040517f8b19807700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5581611a80565b600060608060008060006060610d0a7f4d206279204d5e30000000000000000000000000000000000000000000000000611087565b6040805160008082526060820183526001602083019081527f3100000000000000000000000000000000000000000000000000000000000000938301939093527f0f000000000000000000000000000000000000000000000000000000000000009b939a5091985046975030965090945092509050565b610d9987610d93898989898989611c26565b83611ca1565b610da7878787878787611cee565b50505050505050565b6005546000906108c1906dffffffffffffffffffffffffffff16611979565b610dd96000611d57565b565b60606108c17f4d00000000000000000000000000000000000000000000000000000000000000611087565b610dd933611a80565b610abd85610d9387878787611f41565b610e3188610a868a8a8a8a8a8a611c26565b610e3f888888888888611cee565b5050505050505050565b610e5233611a25565b610e88576040517fdd93dca800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64e8d4a51000610ea96000546fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1603610ef2576040517f5dcd0a3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd933611fdc565b60006108d33384846111dd565b610f1a88610a868a8a8a8a8a8a61215b565b610e3f8888888888886121cb565b610f3683610d938585611864565b610a7383836118e7565b6001600160a01b0381166000908152600660205260408120805460ff16610f68576000610f79565b805461010090046001600160f01b03165b6dffffffffffffffffffffffffffff169392505050565b336001600160a01b037f000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb1614610ff2576040517f51e2625e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5581611d57565b61100d87610d9389898989898961215b565b610da78787878787876121cb565b610da787610aab89898989611f41565b61103d89610aab8b8b8b8b8b8b61215b565b61104b8989898989896121cb565b505050505050505050565b61106889610aab8b8b8b8b8b8b611c26565b61104b898989898989611cee565b61107e6114c3565b610a558161176c565b606060005b6020811080156110ad57508281602081106110a9576110a9613873565b1a15155b156110c2576110bb816138a2565b905061108c565b60008167ffffffffffffffff8111156110dd576110dd613489565b6040519080825280601f01601f191660200182016040528015611107576020820181803683370190505b50905060005b828110156111745784816020811061112757611127613873565b1a60f81b82828151811061113d5761113d613873565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060010161110d565b509392505050565b6001600160a01b0383811660008181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6111e6826122d8565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161122b91815260200190565b60405180910390a3600061123e82612323565b6001600160a01b0380861660009081526006602052604080822054928716825290205491925060ff90811691161515811515036112a357610abd8585836112855784612390565b61128e8561236a565b6dffffffffffffffffffffffffffff16612390565b801561134f576112bb856112b68461236a565b612492565b6001600160a01b038416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290046001600160f01b039081168601811690920217909155600480547fffff00000000000000000000000000000000000000000000000000000000000081169083168501909216919091179055610abd565b61135985836125d4565b610abd84611366846126f1565b6001600160a01b0391909116600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff81166dffffffffffffffffffffffffffff808516610100938490046001600160f01b03908116919091011690920217909155600580547fffffffffffffffffffffffffffffffffffff00000000000000000000000000008116908316909301909116919091179055565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6114597f4d206279204d5e30000000000000000000000000000000000000000000000000611087565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b336001600160a01b037f00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece1614610dd9576040517f3f94bdf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61152e81612717565b611537826122d8565b6040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3600061158382612323565b6004549091506001600160f01b039081168183160111806115e157506004546dffffffffffffffffffffffffffff906115c6906001600160f01b0316830161236a565b6005546dffffffffffffffffffffffffffff90811691160110155b15611618576040517f5165589500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660009081526006602052604090205460ff161561164657610a7383611366836126f1565b6001600160a01b038316600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290046001600160f01b039081168501811690920217909155600480547fffff00000000000000000000000000000000000000000000000000000000000081169083168401909216919091179055505050565b6116e381612717565b6040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a36001600160a01b03821660009081526006602052604090205460ff161561175a57610a3f826112b661175584612323565b61236a565b610a3f8261176783612323565b6125d4565b6000546fffffffffffffffffffffffffffffffff90811690821610156117de576000546040517fecb4124f0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8084166004830152909116602482015260440161098f565b600080546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffff00000000000000000000000000000000000000000090911681177001000000000000000000000000000000004264ffffffffff160217825560405190917fce8d5137687211bba395deb2f8d0635a370c6d4a302be2506a529b0942ce267191a250565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b03841691810191909152606081018290526000906118cc906080015b60405160208183030381529060405280519060200120612754565b9392505050565b610a976118e2858585856127b6565b6127f4565b6118f182826129b9565b6001600160a01b038216600081815260026020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610abd6118e28686868686612a29565b600061199f8261199a6000546fffffffffffffffffffffffffffffffff1690565b612a74565b92915050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061199f565b6119d6612a80565b6000805464ffffffffff4216700100000000000000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009091161764e8d4a51000179055565b6000611a507f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c612abe565b8061199f575061199f7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c83612af3565b6001600160a01b0381166000908152600660205260409020805460ff16611aa5575050565b6040516001600160a01b038316907f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d90600090a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001681556001600160a01b03821660009081526006602052604081205461010090046001600160f01b038116916dffffffffffffffffffffffffffff9091169003611b4557505050565b6000611b5082611979565b6001600160a01b0394909416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166101006001600160f01b038088169190910291909117909155600480547fffff000000000000000000000000000000000000000000000000000000000000811690831690960190911694909417909355600580547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081166dffffffffffffffffffffffffffff91821693909303169190911790555050565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860208201526001600160a01b038089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090611c9690610100016118b1565b979650505050505050565b6000611cae848484612b20565b90506000816005811115611cc457611cc4613901565b03611ccf5750505050565b611cda848484612b67565b15611ce55750505050565b610a97816127f4565b336001600160a01b03861614611d41576040517f1c5939f30000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b038616602482015260440161098f565b611d4f8686868686866121cb565b505050505050565b6001600160a01b038116611d97576040517f0d626a3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b03163b600003611ddb576040517f8d1e7cf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611e057f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b90506000826001600160a01b0316604051600060405180830381855af49150503d8060008114611e51576040519150601f19603f3d011682016040523d82523d6000602084013e611e56565b606091505b5050905080611e91576040517fa27bfda200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ebb7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b9050806001600160a01b0316836001600160a01b0316856001600160a01b03167fe1b831b0e6f3aa16b4b1a6bd526b5cdeab4940744ca6e0251f5fe5f8caf1c81a60405160405180910390a46040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250505050565b6000611f4c82612ca7565b611f5785858561117c565b6001600160a01b038581166000818152600160208181526040928390208054928301905582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918101919091529182019290925291861660608301526080820185905260a082015260c08101839052611fd39060e0016118b1565b95945050505050565b6001600160a01b0381166000908152600660205260409020805460ff1615612002575050565b6040516001600160a01b038316907f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c90600090a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178082556001600160f01b0361010090910416600081900361207d57505050565b6000612088826126f1565b6001600160a01b0394909416600090815260066020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166dffffffffffffffffffffffffffff8087166101000291909117909155600580547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811690831690960190911694909417909355600480547fffff00000000000000000000000000000000000000000000000000000000000081166001600160f01b0391821693909303169190911790555050565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760208201526001600160a01b038089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e08101829052600090611c9690610100016118b1565b82421161220d576040517f498ff9a20000000000000000000000000000000000000000000000000000000081524260048201526024810184905260440161098f565b81421061224f576040517fb3fcd33e0000000000000000000000000000000000000000000000000000000081524260048201526024810183905260440161098f565b61225986826129b9565b6001600160a01b038616600081815260026020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a3611d4f8686866111dd565b6001600160a01b038116610a55576040517f17858bbe0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161098f565b60006001600160f01b03821115612366576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b600061199f8261238b6000546fffffffffffffffffffffffffffffffff1690565b612cea565b6001600160a01b0383166000908152600660205260409020546001600160f01b036101009091048116908216811015612416576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602481018290526001600160f01b038316604482015260640161098f565b506001600160a01b0392831660009081526006602052604080822080546001600160f01b036101008083048216879003821681027fff000000000000000000000000000000000000000000000000000000000000ff93841617909355959096168352912080548281048516909301909316029216919091179055565b6001600160a01b03821660009081526006602052604090205461010090046001600160f01b03166dffffffffffffffffffffffffffff821681101561252b576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526dffffffffffffffffffffffffffff8316604482015260640161098f565b506001600160a01b03909116600090815260066020526040902080546dffffffffffffffffffffffffffff8084166001600160f01b0361010080850482169290920316027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560058054808316939093039091167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000909216919091179055565b6001600160a01b0382166000908152600660205260409020546001600160f01b03610100909104811690821681101561265a576040517fdb42144d0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526001600160f01b038316604482015260640161098f565b506001600160a01b03909116600090815260066020526040902080546001600160f01b0361010080830482168590038216027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560048054808316939093039091167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055565b600061199f826127126000546fffffffffffffffffffffffffffffffff1690565b612cf6565b80600003610a55576040517f77b8dde30000000000000000000000000000000000000000000000000000000081526004810182905260240161098f565b600061275e6109d7565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b60008060006127c6868686612d02565b909250905060008260058111156127df576127df613901565b146127ea5781611c96565b611c968782612d4a565b600081600581111561280857612808613901565b036128105750565b600181600581111561282457612824613901565b0361285b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281600581111561286f5761286f613901565b036128a6576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038160058111156128ba576128ba613901565b036128f1576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600481600581111561290557612905613901565b0361293c576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600581600581111561295057612950613901565b03612987576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216600090815260026020908152604080832084845290915290205460ff1615610a3f576040517fd309466d0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024810182905260440161098f565b6000806000612a3a87878787612d75565b90925090506000826005811115612a5357612a53613901565b14612a5e5781612a68565b612a688882612d4a565b98975050505050505050565b60006118cc8383612e7d565b612a88612eb0565b610dd9576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080612aeb837f6561726e6572735f6c6973745f69676e6f726564000000000000000000000000612ecf565b141592915050565b60006118cc837f6561726e6572730000000000000000000000000000000000000000000000000084612f54565b6000806000612b2f8585612fea565b90925090506000826005811115612b4857612b48613901565b14612b535781612b5d565b612b5d8682612d4a565b9695505050505050565b6000806000856001600160a01b03168585604051602401612b89929190613930565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612c0a9190613949565b600060405180830381855afa9150503d8060008114612c45576040519150601f19603f3d011682016040523d82523d6000602084013e612c4a565b606091505b5091509150818015612c5e57506020815110155b8015612b5d575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612c9c9083016020908101908401613965565b149695505050505050565b80421115610a55576040517ff88f04900000000000000000000000000000000000000000000000000000000081526004810182905242602482015260440161098f565b60006118cc8383613031565b60006118cc83836130dd565b600080601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416612d3c87838884612d75565b935093505050935093915050565b6000816001600160a01b0316836001600160a01b031614612d6c5760056118cc565b50600092915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612dac5750600390506000612e74565b8460ff16601b14158015612dc457508460ff16601c14155b15612dd55750600490506000612e74565b60408051600081526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015612e28573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615612e6a57600081612e6f565b600160005b915091505b94509492505050565b600064e8d4a510006fffffffffffffffffffffffffffffffff83166dffffffffffffffffffffffffffff851602046118cc565b6000612eba6119a5565b5468010000000000000000900460ff16919050565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b03841690638eaa6ac090602401602060405180830381865afa158015612f30573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cc9190613965565b6040517fd7d1c1c0000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0382811660248301526000919085169063d7d1c1c090604401602060405180830381865afa158015612fbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fe2919061397e565b949350505050565b6000808251604114613002575060029050600061302a565b60208301516040840151606085015160001a919061302287848484612d75565b945094505050505b9250929050565b6000816fffffffffffffffffffffffffffffffff1660000361307f576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118cc6fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001600160f01b03861664e8d4a5100002820101816130d7576130d76139a0565b0461315f565b6000816fffffffffffffffffffffffffffffffff1660000361312b576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118cc6fffffffffffffffffffffffffffffffff83166001600160f01b03851664e8d4a5100002816130d7576130d76139a0565b60006dffffffffffffffffffffffffffff821115612366576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156131c45781810151838201526020016131ac565b50506000910152565b600081518084526131e58160208601602086016131a9565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006118cc60208301846131cd565b80356001600160a01b038116811461324157600080fd5b919050565b6000806040838503121561325957600080fd5b6132628361322a565b946020939093013593505050565b60008060006060848603121561328557600080fd5b61328e8461322a565b925061329c6020850161322a565b929592945050506040919091013590565b6000602082840312156132bf57600080fd5b5035919050565b80356fffffffffffffffffffffffffffffffff8116811461324157600080fd5b6000806000606084860312156132fb57600080fd5b6133048461322a565b925060208401359150613319604085016132c6565b90509250925092565b6000806000806080858703121561333857600080fd5b6133418561322a565b966020860135965060408601359560600135945092505050565b803560ff8116811461324157600080fd5b600080600080600060a0868803121561338457600080fd5b61338d8661322a565b9450602086013593506133a26040870161335b565b94979396509394606081013594506080013592915050565b6000602082840312156133cc57600080fd5b6118cc8261322a565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061341060e08301896131cd565b828103604084015261342281896131cd565b606084018890526001600160a01b038716608085015260a0840186905283810360c08501528451808252602080870193509091019060005b8181101561347857835183526020938401939092019160010161345a565b50909b9a5050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126134c957600080fd5b813567ffffffffffffffff8111156134e3576134e3613489565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff8211171561354f5761354f613489565b60405281815283820160200185101561356757600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600080600060e0888a03121561359f57600080fd5b6135a88861322a565b96506135b66020890161322a565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff8111156135ee57600080fd5b6135fa8a828b016134b8565b91505092959891949750929550565b600080600080600060a0868803121561362157600080fd5b61362a8661322a565b94506136386020870161322a565b93506040860135925060608601359150608086013567ffffffffffffffff81111561366257600080fd5b61366e888289016134b8565b9150509295509295909350565b600080600080600080600080610100898b03121561369857600080fd5b6136a18961322a565b97506136af60208a0161322a565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b6000806000606084860312156136f757600080fd5b6137008461322a565b925060208401359150604084013567ffffffffffffffff81111561372357600080fd5b61372f868287016134b8565b9150509250925092565b600080600080600080600060e0888a03121561375457600080fd5b61375d8861322a565b965061376b6020890161322a565b955060408801359450606088013593506137876080890161335b565b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156137b757600080fd5b6137c08361322a565b91506137ce6020840161322a565b90509250929050565b60008060008060008060008060006101208a8c0312156137f657600080fd5b6137ff8a61322a565b985061380d60208b0161322a565b975060408a0135965060608a0135955060808a0135945060a08a0135935061383760c08b0161335b565b989b979a50959894979396929550929360e081013593506101000135919050565b60006020828403121561386a57600080fd5b6118cc826132c6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036138fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b828152604060208201526000612fe260408301846131cd565b6000825161395b8184602087016131a9565b9190910192915050565b60006020828403121561397757600080fd5b5051919050565b60006020828403121561399057600080fd5b815180151581146118cc57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220df783e511c5dd57d9bbd494eb2b5b0a7ccdefc1e768a2e63da033ca5cf1c205a64736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb
-----Decoded View---------------
Arg [0] : registrar_ (address): 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c
Arg [1] : portal_ (address): 0x36f586A30502AE3afb555b8aA4dCc05d233c2ecE
Arg [2] : migrationAdmin_ (address): 0xF2f1ACbe0BA726fEE8d75f3E32900526874740BB
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c
Arg [1] : 00000000000000000000000036f586a30502ae3afb555b8aa4dcc05d233c2ece
Arg [2] : 000000000000000000000000f2f1acbe0ba726fee8d75f3e32900526874740bb
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$1,757.15
Net Worth in ETH
Token Allocations
WETH
100.00%
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $1,996.55 | 0.8801 | $1,757.15 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.