Overview
Max Total Supply
706,913,776.345186 M
Holders
76 (0.00%)
Transfers
-
68 ( 38.78%)
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 6 Decimals)
Loading...
Loading
Loading...
Loading
Loading...
Loading
| # | Exchange | Pair | Price | 24H Volume | % Volume |
|---|
Contract Name:
MToken
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 999999 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { ERC20Extended } from "../lib/common/src/ERC20Extended.sol";
import { UIntMath } from "../lib/common/src/libs/UIntMath.sol";
import { IERC20 } from "../lib/common/src/interfaces/IERC20.sol";
import { TTGRegistrarReader } from "./libs/TTGRegistrarReader.sol";
import { IContinuousIndexing } from "./interfaces/IContinuousIndexing.sol";
import { IMToken } from "./interfaces/IMToken.sol";
import { IRateModel } from "./interfaces/IRateModel.sol";
import { ContinuousIndexing } from "./abstract/ContinuousIndexing.sol";
import { ContinuousIndexingMath } from "./libs/ContinuousIndexingMath.sol";
/*
███╗ ███╗ ████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗
████╗ ████║ ╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║
██╔████╔██║ ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║
██║╚██╔╝██║ ██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║
██║ ╚═╝ ██║ ██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝
-->> M is for Money. <<--
*/
/**
* @title MToken
* @author M^0 Labs
* @notice ERC20 M Token.
*/
contract MToken is IMToken, ContinuousIndexing, ERC20Extended {
/* ============ 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 minterGateway;
/// @inheritdoc IMToken
address public immutable ttgRegistrar;
/// @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 Minter Gateway.
modifier onlyMinterGateway() {
if (msg.sender != minterGateway) revert NotMinterGateway();
_;
}
/* ============ Constructor ============ */
/**
* @notice Constructs the M Token contract.
* @param ttgRegistrar_ The address of the TTG Registrar contract.
* @param minterGateway_ The address of Minter Gateway.
*/
constructor(address ttgRegistrar_, address minterGateway_) ContinuousIndexing() ERC20Extended("M by M^0", "M", 6) {
if ((ttgRegistrar = ttgRegistrar_) == address(0)) revert ZeroTTGRegistrar();
if ((minterGateway = minterGateway_) == address(0)) revert ZeroMinterGateway();
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IMToken
function mint(address account_, uint256 amount_) external onlyMinterGateway {
_mint(account_, amount_);
}
/// @inheritdoc IMToken
function burn(address account_, uint256 amount_) external onlyMinterGateway {
_burn(account_, amount_);
}
/// @inheritdoc IMToken
function startEarning() external {
if (!_isApprovedEarner(msg.sender)) revert NotApprovedEarner();
_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_);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IMToken
function rateModel() public view returns (address rateModel_) {
return TTGRegistrarReader.getEarnerRateModel(ttgRegistrar);
}
/// @inheritdoc IMToken
function earnerRate() public view returns (uint32 earnerRate_) {
return _latestRate;
}
/// @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) {
// NOTE: Safe to use unchecked here, since `block.timestamp` is always greater than `latestUpdateTimestamp`.
unchecked {
return
// NOTE: Cap the index to `type(uint128).max` to prevent overflow in present value math.
UIntMath.bound128(
ContinuousIndexingMath.multiplyIndicesDown(
latestIndex,
ContinuousIndexingMath.getContinuousIndex(
ContinuousIndexingMath.convertFromBasisPoints(_latestRate),
uint32(block.timestamp - latestUpdateTimestamp)
)
)
);
}
}
/* ============ 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_)));
updateIndex();
} 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_));
updateIndex();
} 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_;
}
updateIndex();
}
/**
* @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_;
}
updateIndex();
}
/**
* @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_));
}
updateIndex();
}
/**
* @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 TTG.
* @param account_ The account to check.
* @return True if approved, false otherwise.
*/
function _isApprovedEarner(address account_) internal view returns (bool) {
return
TTGRegistrarReader.isEarnersListIgnored(ttgRegistrar) ||
TTGRegistrarReader.isApprovedEarner(ttgRegistrar, account_);
}
/**
* @dev Gets the current earner rate from TTG approved rate model contract.
* @return rate_ The current earner rate.
*/
function _rate() internal view override returns (uint32 rate_) {
(bool success_, bytes memory returnData_) = rateModel().staticcall(
abi.encodeWithSelector(IRateModel.rate.selector)
);
rate_ = (success_ && returnData_.length >= 32) ? UIntMath.bound32(abi.decode(returnData_, (uint256))) : 0;
}
/**
* @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_);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IERC20 } from "./interfaces/IERC20.sol";
import { IERC20Extended } from "./interfaces/IERC20Extended.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;
/// @inheritdoc IERC20
string public 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 = 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 name_) {
return _name;
}
/* ============ 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.23;
/**
* @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 given uint256 value to a uint16,
* ensuring that it is less than or equal to the maximum uint16 value.
* @param n The value to check.
* @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 given uint256 value to a uint40,
* ensuring that it is less than or equal to the maximum uint40 value.
* @param n The value to check.
* @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 given uint256 value to a uint48,
* ensuring that it is less than or equal to the maximum uint48 value.
* @param n The value to check.
* @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 given uint256 value to a uint112,
* ensuring that it is less than or equal to the maximum uint112 value.
* @param n The value to check.
* @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 given uint256 value to a uint128,
* ensuring that it is less than or equal to the maximum uint128 value.
* @param n The value to check.
* @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 given uint256 value to a uint240,
* ensuring that it is less than or equal to the maximum uint240 value.
* @param n The value to check.
* @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 given uint256 value to the maximum uint32 value.
* @param n The value to check.
* @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 given uint256 value to the maximum uint112 value.
* @param n The value to check.
* @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 given uint256 value to the maximum uint128 value.
* @param n The value to check.
* @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 given uint256 value to the maximum uint240 value.
* @param n The value to check.
* @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 check.
* @param b_ Value to check.
* @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 check.
* @param b_ Value to check.
* @return The larger value.
*/
function max40(uint40 a_, uint40 b_) internal pure returns (uint40) {
return a_ > b_ ? a_ : b_;
}
/**
* @notice Compares two uint32 values and returns the lesser one.
* @param a_ Value to check.
* @param b_ Value to check.
* @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 check.
* @param b_ Value to check.
* @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 check.
* @param b_ Value to check.
* @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 check.
* @param b_ Value to check.
* @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 check.
* @param b_ Value to check.
* @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.23;
/**
* @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.23;
import { ITTGRegistrar } from "../interfaces/ITTGRegistrar.sol";
/**
* @title Library to read TTG (Two Token Governance) Registrar contract parameters.
* @author M^0 Labs
*/
library TTGRegistrarReader {
/* ============ Variables ============ */
/// @notice The name of parameter in TTG that defines the earner rate model contract.
bytes32 internal constant EARNER_RATE_MODEL = "earner_rate_model";
/// @notice The parameter name in TTG that defines the earners list.
bytes32 internal constant EARNERS_LIST = "earners";
/// @notice The parameter name in TTG that defines whether to ignore the earners list or not.
bytes32 internal constant EARNERS_LIST_IGNORED = "earners_list_ignored";
/// @notice The parameter name in TTG that defines the time to wait for mint request to be processed.
bytes32 internal constant MINT_DELAY = "mint_delay";
/// @notice The parameter name in TTG that defines the mint ratio.
bytes32 internal constant MINT_RATIO = "mint_ratio"; // bps
/// @notice The parameter name in TTG that defines the time while mint request can still be processed.
bytes32 internal constant MINT_TTL = "mint_ttl";
/// @notice The parameter name in TTG that defines the time to freeze minter.
bytes32 internal constant MINTER_FREEZE_TIME = "minter_freeze_time";
/// @notice The parameter name in TTG that defines the minter rate model contract.
bytes32 internal constant MINTER_RATE_MODEL = "minter_rate_model";
/// @notice The parameter name in TTG that defines the minters list.
bytes32 internal constant MINTERS_LIST = "minters";
/// @notice The parameter name in TTG that defines the penalty rate.
bytes32 internal constant PENALTY_RATE = "penalty_rate"; // bps
/// @notice The parameter name in TTG that defines the required interval to update collateral.
bytes32 internal constant UPDATE_COLLATERAL_INTERVAL = "update_collateral_interval";
/// @notice The parameter name that defines number of signatures required for successful collateral update.
bytes32 internal constant UPDATE_COLLATERAL_VALIDATOR_THRESHOLD = "update_collateral_threshold";
/// @notice The parameter name in TTG that defines the validators list.
bytes32 internal constant VALIDATORS_LIST = "validators";
/* ============ Internal View/Pure Functions ============ */
/// @notice Gets the earner rate model contract address.
function getEarnerRateModel(address registrar_) internal view returns (address) {
return toAddress(_get(registrar_, EARNER_RATE_MODEL));
}
/// @notice Gets the mint delay.
function getMintDelay(address registrar_) internal view returns (uint256) {
return uint256(_get(registrar_, MINT_DELAY));
}
/// @notice Gets the minter freeze time.
function getMinterFreezeTime(address registrar_) internal view returns (uint256) {
return uint256(_get(registrar_, MINTER_FREEZE_TIME));
}
/// @notice Gets the minter rate model contract address.
function getMinterRateModel(address registrar_) internal view returns (address) {
return toAddress(_get(registrar_, MINTER_RATE_MODEL));
}
/// @notice Gets the mint TTL.
function getMintTTL(address registrar_) internal view returns (uint256) {
return uint256(_get(registrar_, MINT_TTL));
}
/// @notice Gets the mint ratio.
function getMintRatio(address registrar_) internal view returns (uint256) {
return uint256(_get(registrar_, MINT_RATIO));
}
/// @notice Gets the update collateral interval.
function getUpdateCollateralInterval(address registrar_) internal view returns (uint256) {
return uint256(_get(registrar_, UPDATE_COLLATERAL_INTERVAL));
}
/// @notice Gets the update collateral validator threshold.
function getUpdateCollateralValidatorThreshold(address registrar_) internal view returns (uint256) {
return uint256(_get(registrar_, UPDATE_COLLATERAL_VALIDATOR_THRESHOLD));
}
/// @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 Checks if the given minter is approved.
function isApprovedMinter(address registrar_, address minter_) internal view returns (bool) {
return _contains(registrar_, MINTERS_LIST, minter_);
}
/// @notice Checks if the given validator is approved.
function isApprovedValidator(address registrar_, address validator_) internal view returns (bool) {
return _contains(registrar_, VALIDATORS_LIST, validator_);
}
/// @notice Gets the penalty rate.
function getPenaltyRate(address registrar_) internal view returns (uint256) {
return uint256(_get(registrar_, PENALTY_RATE));
}
/// @notice Gets the vault contract address.
function getVault(address registrar_) internal view returns (address) {
return ITTGRegistrar(registrar_).vault();
}
/// @notice Converts given bytes32 to address.
function toAddress(bytes32 input_) internal pure returns (address) {
return address(uint160(uint256(input_)));
}
/// @notice Checks if the given list contains the given account.
function _contains(address registrar_, bytes32 listName_, address account_) private view returns (bool) {
return ITTGRegistrar(registrar_).listContains(listName_, account_);
}
/// @notice Gets the value of the given key.
function _get(address registrar_, bytes32 key_) private view returns (bytes32) {
return ITTGRegistrar(registrar_).get(key_);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
/**
* @title Continuous Indexing Interface.
* @author M^0 Labs
*/
interface IContinuousIndexing {
/* ============ Events ============ */
/**
* @notice Emitted when the index is updated.
* @param index The new index.
* @param rate The current rate.
*/
event IndexUpdated(uint128 indexed index, uint32 indexed rate);
/* ============ Interactive Functions ============ */
/**
* @notice Updates the latest index and latest accrual time in storage.
* @return index The new stored index for computing present amounts from principal amounts.
*/
function updateIndex() external returns (uint128);
/* ============ View/Pure Functions ============ */
/// @notice The current index that would be written to storage if `updateIndex` is 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.23;
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 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 TTG.
error IsApprovedEarner();
/// @notice Emitted when calling `startEarning` for an account not approved as earner by TTG.
error NotApprovedEarner();
/// @notice Emitted when calling `mint`, `burn` not by Minter Gateway.
error NotMinterGateway();
/// @notice Emitted when principal of total supply (earning and non-earning) will overflow a `type(uint112).max`.
error OverflowsPrincipalOfTotalSupply();
/// @notice Emitted in constructor if Minter Gateway is 0x0.
error ZeroMinterGateway();
/// @notice Emitted in constructor if TTG Registrar is 0x0.
error ZeroTTGRegistrar();
/* ============ Interactive Functions ============ */
/**
* @notice Mints tokens.
* @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 tokens.
* @param account The address of account to burn from.
* @param amount The amount of M Token to burn.
*/
function burn(address account, uint256 amount) 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 TTG Registrar.
* @param account The account to stop earning for.
*/
function stopEarning(address account) external;
/* ============ View/Pure Functions ============ */
/// @notice The address of the Minter Gateway contract.
function minterGateway() external view returns (address);
/// @notice The address of the TTG Registrar contract.
function ttgRegistrar() external view returns (address);
/// @notice The address of TTG approved earner rate model.
function rateModel() external view returns (address);
/// @notice The current value of earner rate in basis points.
function earnerRate() external view returns (uint32);
/**
* @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.23;
/**
* @title Rate Model Interface.
* @author M^0 Labs
*/
interface IRateModel {
/**
* @notice Returns the current yearly rate in BPS.
* This value does not account for the compounding interest.
*/
function rate() external view returns (uint256);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
import { IContinuousIndexing } from "../interfaces/IContinuousIndexing.sol";
import { ContinuousIndexingMath } from "../libs/ContinuousIndexingMath.sol";
/**
* @title Abstract Continuous Indexing Contract to handle rate/index updates in inheriting contracts.
* @author M^0 Labs
*/
abstract contract ContinuousIndexing is IContinuousIndexing {
/* ============ Variables ============ */
/// @inheritdoc IContinuousIndexing
uint128 public latestIndex;
/// @dev The latest updated rate.
uint32 internal _latestRate;
/// @inheritdoc IContinuousIndexing
uint40 public latestUpdateTimestamp;
/* ============ Constructor ============ */
/// @notice Constructs the ContinuousIndexing contract.
constructor() {
latestIndex = ContinuousIndexingMath.EXP_SCALED_ONE;
latestUpdateTimestamp = uint40(block.timestamp);
}
/* ============ Interactive Functions ============ */
/// @inheritdoc IContinuousIndexing
function updateIndex() public virtual returns (uint128 currentIndex_) {
// NOTE: `_rate()` can depend indirectly on `latestIndex` and `latestUpdateTimestamp`, if the RateModel
// depends on earning balances/supply, which depends on `currentIndex()`, so only update them after this.
uint32 rate_ = _rate();
if (latestUpdateTimestamp == block.timestamp && _latestRate == rate_) return latestIndex;
// NOTE: `currentIndex()` depends on `_latestRate`, so only update it after this.
latestIndex = currentIndex_ = currentIndex();
_latestRate = rate_;
latestUpdateTimestamp = uint40(block.timestamp);
emit IndexUpdated(currentIndex_, rate_);
}
/* ============ View/Pure Functions ============ */
/// @inheritdoc IContinuousIndexing
function currentIndex() public view virtual returns (uint128);
/* ============ 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_);
}
/// @dev To be overridden by the inheriting contract to return the current rate.
function _rate() internal view virtual returns (uint32);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
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 number of seconds in a year.
uint32 internal constant SECONDS_PER_YEAR = 31_536_000;
/// @notice 100% in basis points.
uint16 internal constant BPS_SCALED_ONE = 1e4;
/// @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);
}
}
/**
* @notice Helper function to calculate `(index * deltaIndex) / EXP_SCALED_ONE`, rounded down.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function multiplyIndicesDown(uint128 index, uint48 deltaIndex) internal pure returns (uint144 z) {
unchecked {
return uint144((uint256(index) * deltaIndex) / EXP_SCALED_ONE);
}
}
/**
* @notice Helper function to calculate `(index * deltaIndex) / EXP_SCALED_ONE`, rounded up.
* @dev Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
*/
function multiplyIndicesUp(uint128 index, uint48 deltaIndex) internal pure returns (uint144 z) {
unchecked {
return uint144((uint256(index) * deltaIndex + (EXP_SCALED_ONE - 1)) / EXP_SCALED_ONE);
}
}
/**
* @notice Helper function to calculate e^rt (continuous compounding formula).
* @dev `uint64 yearlyRate` can accommodate 1000% interest per year.
* @dev `uint32 time` can accommodate 100 years.
* @dev `type(uint64).max * type(uint32).max / SECONDS_PER_YEAR` fits in a `uint72`.
*/
function getContinuousIndex(uint64 yearlyRate, uint32 time) internal pure returns (uint48 index) {
unchecked {
// NOTE: Casting `uint256(yearlyRate) * time` to a `uint72` is safe because the largest value is
// `type(uint64).max * type(uint32).max / SECONDS_PER_YEAR`, which is less than `type(uint72).max`.
return exponent(uint72((uint256(yearlyRate) * time) / SECONDS_PER_YEAR));
}
}
/**
* @notice Helper function to calculate y = e^x using R(4,4) Padé approximation:
* e(x) = (1 + x/2 + 3(x^2)/28 + x^3/84 + x^4/1680) / (1 - x/2 + 3(x^2)/28 - x^3/84 + x^4/1680)
* See: https://en.wikipedia.org/wiki/Pad%C3%A9_table
* See: https://www.wolframalpha.com/input?i=PadeApproximant%5Bexp%5Bx%5D%2C%7Bx%2C0%2C%7B4%2C+4%7D%7D%5D
* Despite itself being a whole number, `x` represents a real number scaled by `EXP_SCALED_ONE`, thus
* allowing for y = e^x where x is a real number.
* @dev Output `y` for a `uint72` input `x` will fit in `uint48`
*/
function exponent(uint72 x) internal pure returns (uint48 y) {
// NOTE: This can be done unchecked even for `x = type(uint72).max`.
// Verify by removing `unchecked` and running `test_exponent()`.
unchecked {
uint256 x2 = uint256(x) * x;
// `additiveTerms` is `(1 + 3(x^2)/28 + x^4/1680)`, and scaled by `84e27`.
// NOTE: `84e27` the cleanest and largest scalar, given the various intermediate overflow possibilities.
// NOTE: The resulting `(x2 * x2) / 20e21` term has been split up in order to avoid overflow of `x2 * x2`.
uint256 additiveTerms = 84e27 + (9e3 * x2) + ((x2 / 2e11) * (x2 / 1e11));
// `differentTerms` is `(- x/2 - x^3/84)`, but positive (will be subtracted later), and scaled by `84e27`.
uint256 differentTerms = uint256(x) * (42e15 + (x2 / 1e9));
// Result needs to be scaled by `1e12`.
// NOTE: Can cast to `uint48` because contents can never be larger than `type(uint48).max` for any `x`.
// Max `y` is ~200e12, before falling off. See links above for reference.
return uint48(((additiveTerms + differentTerms) * 1e12) / (additiveTerms - differentTerms));
}
}
/**
* @notice Helper function to convert 12-decimal representation to basis points.
* @param input The input in 12-decimal representation.
* @return The output in basis points.
*/
function convertToBasisPoints(uint64 input) internal pure returns (uint40) {
unchecked {
return uint40((uint256(input) * BPS_SCALED_ONE) / EXP_SCALED_ONE);
}
}
/**
* @notice Helper function to convert basis points to 12-decimal representation.
* @param input The input in basis points.
* @return The output in 12-decimal representation.
*/
function convertFromBasisPoints(uint32 input) internal pure returns (uint64) {
unchecked {
return uint64((uint256(input) * EXP_SCALED_ONE) / BPS_SCALED_ONE);
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
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 block number 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 block number 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.23;
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.23;
/**
* @title TTG (Two Token Governance) Registrar interface.
* @author M^0 Labs
*/
interface ITTGRegistrar {
/**
* @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 True if the list contains the account, false otherwise.
*/
function listContains(bytes32 list, address account) external view returns (bool);
/// @notice Returns the vault contract address.
function vault() external view returns (address);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;
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.23;
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.23;
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.23;
import { IERC712 } from "./interfaces/IERC712.sol";
import { IERC712Extended } from "./interfaces/IERC712Extended.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.
string internal _name;
/* ============ Constructor ============ */
/**
* @notice Constructs the EIP-712 domain separator.
* @param name_ The name of the contract.
*/
constructor(string memory name_) {
_name = 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
_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(_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.23;
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.23;
/**
* @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 deadline of the signature.
* @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.23;
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.23;
/**
* @title Standard Signature Validation Method for Contracts via EIP-1271.
* @author M^0 Labs
* @dev The interface as defined by EIP-1271: https://eips.ethereum.org/EIPS/eip-1271
*/
interface IERC1271 {
/**
* @dev Returns a specific magic value if the provided signature is valid for the provided digest.
* @param digest Hash of the data purported to have been signed.
* @param signature Signature byte array associated with the digest.
* @return magicValue Magic value 0x1626ba7e if the signature is valid.
*/
function isValidSignature(bytes32 digest, bytes memory signature) external view returns (bytes4 magicValue);
}{
"remappings": [
"common/=lib/protocol/lib/common/src/",
"contract-test-utils/=lib/ttg/lib/erc20-helper/lib/contract-test-utils/contracts/",
"ds-test/=lib/protocol/lib/solmate/lib/ds-test/src/",
"erc20-helper/=lib/ttg/lib/erc20-helper/src/",
"forge-std/=lib/forge-std/src/",
"protocol/=lib/protocol/",
"solmate/=lib/protocol/lib/solmate/src/",
"ttg/=lib/ttg/"
],
"optimizer": {
"enabled": true,
"runs": 999999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"ttgRegistrar_","type":"address"},{"internalType":"address","name":"minterGateway_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"authorizer","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationAlreadyUsed","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"}],"name":"AuthorizationExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"}],"name":"AuthorizationNotYetValid","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"payee","type":"address"}],"name":"CallerMustBePayee","type":"error"},{"inputs":[],"name":"DivisionByZero","type":"error"},{"inputs":[{"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":[{"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":"NotApprovedEarner","type":"error"},{"inputs":[],"name":"NotMinterGateway","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":"ZeroMinterGateway","type":"error"},{"inputs":[],"name":"ZeroTTGRegistrar","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"},{"indexed":true,"internalType":"uint32","name":"rate","type":"uint32"}],"name":"IndexUpdated","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"},{"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":"address","name":"account_","type":"address"},{"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":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"earnerRate","outputs":[{"internalType":"uint32","name":"earnerRate_","type":"uint32"}],"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":[{"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":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minterGateway","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"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":[{"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":[],"name":"rateModel","outputs":[{"internalType":"address","name":"rateModel_","type":"address"}],"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":"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":[],"name":"ttgRegistrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateIndex","outputs":[{"internalType":"uint128","name":"currentIndex_","type":"uint128"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
61012060405234801562000011575f80fd5b50604051620040ee380380620040ee833981016040819052620000349162000210565b604080518082018252600881526704d206279204d5e360c41b60208083019190915282518084019093526001808452604d60f81b918401919091525f805464ffffffffff4216600160a01b027fffffffffffffff0000000000ffffffff000000000000000000000000000000009091161764e8d4a51000179055909190600690839081908190620000c68282620002e4565b5046608052620000d562000157565b60a0525060049150620000eb90508382620002e4565b5060ff1660c05250506001600160a01b038216610100819052620001225760405163b8eb034b60e01b815260040160405180910390fd5b6001600160a01b03811660e08190526200014f57604051630205ebf760e31b815260040160405180910390fd5b50506200042a565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b60016040516200018c9190620003b0565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80516001600160a01b03811681146200020b575f80fd5b919050565b5f806040838503121562000222575f80fd5b6200022d83620001f4565b91506200023d60208401620001f4565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806200026f57607f821691505b6020821081036200028e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620002df57805f5260205f20601f840160051c81016020851015620002bb5750805b601f840160051c820191505b81811015620002dc575f8155600101620002c7565b50505b505050565b81516001600160401b0381111562000300576200030062000246565b62000318816200031184546200025a565b8462000294565b602080601f8311600181146200034e575f8415620003365750858301515b5f19600386901b1c1916600185901b178555620003a8565b5f85815260208120601f198616915b828110156200037e578886015182559484019460019091019084016200035d565b50858210156200039c57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f808354620003bf816200025a565b60018281168015620003da5760018114620003f0576200041e565b60ff19841687528215158302870194506200041e565b875f526020805f205f5b85811015620004155781548a820152908401908201620003fa565b50505082870194505b50929695505050505050565b60805160a05160c05160e05161010051613c636200048b5f395f81816106c901528181610f8a01528181611a090152611a3801525f818161046b01528181610b070152610ede01525f61041501525f610acd01525f610a9d0152613c635ff3fe608060405234801561000f575f80fd5b50600436106102f9575f3560e01c80638a75f23811610192578063ace150a5116100e8578063d505accf11610093578063e3ee160e1161006e578063e3ee160e146107e7578063e94a0102146107fa578063ef55bec614610827575f80fd5b8063d505accf14610783578063d916948714610796578063dd62ed3e146107bd575f80fd5b8063c23465b3116100c3578063c23465b31461072c578063c634dfaa1461075d578063cf09299514610770575f80fd5b8063ace150a5146106fe578063b7b7289914610711578063b9f412b014610724575f80fd5b8063a08cb48b11610148578063a36e40fc11610123578063a36e40fc146106bc578063a6ce63cd146106c4578063a9059cbb146106eb575f80fd5b8063a08cb48b1461067a578063a0cc6a681461068d578063a1088459146106b4575f80fd5b80639dc29fac116101785780639dc29fac1461064c5780639f8495f91461065f5780639fd5a6cf14610667575f80fd5b80638a75f2381461063c57806395d89b4114610644575f80fd5b80634c57a8fa116102525780637ecebe00116101fd57806384af270f116101d857806384af270f146105d657806384b0196e1461060e57806388b7ab6314610629575f80fd5b80637ecebe001461057d5780637f2eecc31461059c57806381399be4146105c3575f80fd5b8063578f2aa01161022d578063578f2aa01461053c5780635a049a701461055757806370a082311461056a575f80fd5b80634c57a8fa146104b2578063532992c5146104eb57806353d96f2c146104fe575f80fd5b8063281b229d116102b25780633644e5151161028d5780633644e5151461044957806340c10f191461045157806348545a3c14610466575f80fd5b8063281b229d1461039057806330adf81f146103e9578063313ce56714610410575f80fd5b806318160ddd116102e257806318160ddd1461033e57806323b872dd1461035457806326987b6014610367575f80fd5b806306fdde03146102fd578063095ea7b31461031b575b5f80fd5b61030561083a565b60405161031291906134bd565b60405180910390f35b61032e6103293660046134f7565b6108ca565b6040519015158152602001610312565b6103466108df565b604051908152602001610312565b61032e61036236600461351f565b610917565b61036f610a08565b6040516fffffffffffffffffffffffffffffffff9091168152602001610312565b6006546103ba907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091168152602001610312565b6103467f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6104377f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610312565b610346610a9a565b61046461045f3660046134f7565b610aef565b005b61048d7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610312565b6007546104cc906dffffffffffffffffffffffffffff1681565b6040516dffffffffffffffffffffffffffff9091168152602001610312565b6104646104f9366004613558565b610b6c565b5f546105269074010000000000000000000000000000000000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610312565b5f5461036f906fffffffffffffffffffffffffffffffff1681565b61046461056536600461359e565b610b91565b6103466105783660046135e8565b610bb8565b61034661058b3660046135e8565b60026020525f908152604090205481565b6103467fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6104646105d13660046135e8565b610c68565b61032e6105e43660046135e8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526008602052604090205460ff1690565b610616610cb4565b6040516103129796959493929190613601565b610464610637366004613795565b610ded565b6103ba610e1c565b610305610e3a565b61046461065a3660046134f7565b610ec6565b610464610f3f565b610464610675366004613814565b610f4a565b610464610688366004613881565b610f5a565b6103467f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b61048d610f84565b610464610fae565b61048d7f000000000000000000000000000000000000000000000000000000000000000081565b61032e6106f93660046134f7565b610ff6565b61046461070c366004613881565b611002565b61046461071f3660046138e3565b611022565b61036f61103f565b5f54700100000000000000000000000000000000900463ffffffff1660405163ffffffff9091168152602001610312565b6103ba61076b3660046135e8565b61118f565b61046461077e366004613795565b611201565b610464610791366004613936565b611221565b6103467f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6103466107cb36600461399b565b600560209081525f928352604080842090915290825290205481565b6104646107f53660046139cc565b611231565b61032e6108083660046134f7565b600360209081525f928352604080842090915290825290205460ff1681565b6104646108353660046139cc565b61125c565b60606001805461084990613a44565b80601f016020809104026020016040519081016040528092919081815260200182805461087590613a44565b80156108c05780601f10610897576101008083540402835291602001916108c0565b820191905f5260205f20905b8154815290600101906020018083116108a357829003601f168201915b5050505050905090565b5f6108d633848461127c565b50600192915050565b5f6108e8610e1c565b6006547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081169190910116919050565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526005602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109f257828110156109bd576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018490526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f908152600560209081526040808320338452909152902083820390555b6109fd8585856112e9565b506001949350505050565b5f8054610a9590610a7c906fffffffffffffffffffffffffffffffff811690610a7790612710700100000000000000000000000000000000820463ffffffff1664e8d4a5100002049074010000000000000000000000000000000000000000900464ffffffffff164203611594565b6115bd565b71ffffffffffffffffffffffffffffffffffff166115ea565b905090565b5f7f00000000000000000000000000000000000000000000000000000000000000004614610aca57610a9561160b565b507f000000000000000000000000000000000000000000000000000000000000000090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610b5e576040517f65774a1c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b6882826116a6565b5050565b610b8184610b7a86866118cb565b848461193f565b610b8b8484611953565b50505050565b610ba785610b9f87876118cb565b8585856119e1565b610bb18585611953565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260408120805460ff16610c1157805461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16610c41565b8054610c419061010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166119f1565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169392505050565b610c7181611a03565b15610ca8576040517f8b19807700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cb181611a5d565b50565b5f60608082808083600146308480604051908082528060200260200182016040528015610ceb578160200160208202803683370190505b507f0f000000000000000000000000000000000000000000000000000000000000009493929190848054610d1e90613a44565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4a90613a44565b8015610d955780601f10610d6c57610100808354040283529160200191610d95565b820191905f5260205f20905b815481529060010190602001808311610d7857829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b610e0587610dff898989898989611c5f565b83611ce6565b610e13878787878787611d31565b50505050505050565b6007545f90610a95906dffffffffffffffffffffffffffff166119f1565b60048054610e4790613a44565b80601f0160208091040260200160405190810160405280929190818152602001828054610e7390613a44565b8015610ebe5780601f10610e9557610100808354040283529160200191610ebe565b820191905f5260205f20905b815481529060010190602001808311610ea157829003601f168201915b505050505081565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f35576040517f65774a1c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b688282611dac565b610f4833611a5d565b565b610bb185610dff87878787611e5e565b610f6c88610b7a8a8a8a8a8a8a611c5f565b610f7a888888888888611d31565b5050505050505050565b5f610a957f0000000000000000000000000000000000000000000000000000000000000000611f09565b610fb733611a03565b610fed576040517fdd93dca800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f4833611f3a565b5f6108d63384846112e9565b61101488610b7a8a8a8a8a8a8a612109565b610f7a888888888888612185565b61103083610dff85856118cb565b61103a8383611953565b505050565b5f8061104961229e565b5f5490915064ffffffffff74010000000000000000000000000000000000000000909104164214801561109957505f5463ffffffff82811670010000000000000000000000000000000090920416145b156110b75750505f546fffffffffffffffffffffffffffffffff1690565b6110bf610a08565b5f80546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811770010000000000000000000000000000000063ffffffff8616908102919091177fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004264ffffffffff16021783556040519395509290917f8f9a1730052b867fdeb484b52fbc51e9bb62830781805ac95c382bbf8ea717a291a35090565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260408120805460ff166111c2575f6111ea565b805461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff165b6dffffffffffffffffffffffffffff169392505050565b61121387610dff898989898989612109565b610e13878787878787612185565b610e1387610b9f89898989611e5e565b61124389610b9f8b8b8b8b8b8b612109565b611251898989898989612185565b505050505050505050565b61126e89610b9f8b8b8b8b8b8b611c5f565b611251898989898989611d31565b73ffffffffffffffffffffffffffffffffffffffff8381165f8181526005602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6112f2826123a3565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161135191815260200190565b60405180910390a35f61136382612408565b73ffffffffffffffffffffffffffffffffffffffff8086165f9081526008602052604080822054928716825290205491925060ff90811691161515811515036113d457610bb18585836113b65784612477565b6113bf85612465565b6dffffffffffffffffffffffffffff16612477565b80156114a3576113ec856113e784612465565b6125e3565b73ffffffffffffffffffffffffffffffffffffffff84165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081168601811690920217909155600680547fffff00000000000000000000000000000000000000000000000000000000000081169083168501909216919091179055611584565b6114ad8583612778565b611584846114ba846128ff565b73ffffffffffffffffffffffffffffffffffffffff919091165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff81166dffffffffffffffffffffffffffff808516610100938490047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908116919091011690920217909155600780547fffffffffffffffffffffffffffffffffffff00000000000000000000000000008116908316909301909116919091179055565b61158c61103f565b505050505050565b5f6115b66301e1338067ffffffffffffffff851663ffffffff85160204612911565b9392505050565b5f64e8d4a510006fffffffffffffffffffffffffffffffff841665ffffffffffff8416025b049392505050565b5f611605826fffffffffffffffffffffffffffffffff61298a565b92915050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405161163e9190613ac2565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6116af8161299f565b6116b8826123a3565b60405181815273ffffffffffffffffffffffffffffffffffffffff8316905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35f61170f82612408565b6006549091507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90811681831601118061179b57506006546dffffffffffffffffffffffffffff90611780907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168301612465565b6007546dffffffffffffffffffffffffffff90811691160110155b156117d2576040517f5165589500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f9081526008602052604090205460ff16156118145761180c836114ba836128ff565b610b8b61103f565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081168501811690920217909155600680547fffff00000000000000000000000000000000000000000000000000000000000081169083168401909216919091179055505050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429602082015273ffffffffffffffffffffffffffffffffffffffff841691810191909152606081018290525f906115b6906080015b604051602081830303815290604052805190602001206129db565b610b8b61194e85858585612a3c565b612a77565b61195d8282612c3b565b73ffffffffffffffffffffffffffffffffffffffff82165f81815260036020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610bb161194e8686868686612cc4565b5f611605826119fe610a08565b612d0c565b5f611a2d7f0000000000000000000000000000000000000000000000000000000000000000612d17565b8061160557506116057f000000000000000000000000000000000000000000000000000000000000000083612d4b565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260409020805460ff16611a8e575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d905f90a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016815573ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604081205461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116916dffffffffffffffffffffffffffff9091169003611b5d57505050565b5f611b67826119f1565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260086020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808416610100027fff000000000000000000000000000000000000000000000000000000000000ff909216919091179091556006805480831684019092167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055600780546dffffffffffffffffffffffffffff808216869003167fffffffffffffffffffffffffffffffffffff00000000000000000000000000009091161790559050610bb161103f565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e081018290525f90611cdb9061010001611924565b979650505050505050565b5f611cf2848484612d77565b90505f816005811115611d0757611d07613b97565b03611d125750505050565b611d1d848484612dbb565b15611d285750505050565b610b8b81612a77565b3373ffffffffffffffffffffffffffffffffffffffff861614611d9e576040517f1c5939f300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff861660248201526044016109b4565b61158c868686868686612185565b611db58161299f565b6040518181525f9073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604090205460ff1615611e4c57611e44826113e7611e3f84612408565b612465565b61103a61103f565b610b6882611e5983612408565b612778565b5f611e6882612f03565b611e7385858561127c565b73ffffffffffffffffffffffffffffffffffffffff8581165f8181526002602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928101929092529181019290925291861660608201526080810185905260a081019190915260c08101839052611f009060e001611924565b95945050505050565b5f611605611f37837f6561726e65725f726174655f6d6f64656c000000000000000000000000000000612f46565b90565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260409020805460ff1615611f6c575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c905f90a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178082557dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100909104165f81900361200957505050565b5f612013826128ff565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166dffffffffffffffffffffffffffff8381166101000291909117909155600780547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081169083168401909216919091179055600680547fffff00000000000000000000000000000000000000000000000000000000000081167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9182168690039091161790559050610bb161103f565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e081018290525f90611cdb9061010001611924565b8242116121c7576040517f498ff9a2000000000000000000000000000000000000000000000000000000008152426004820152602481018490526044016109b4565b814210612209576040517fb3fcd33e000000000000000000000000000000000000000000000000000000008152426004820152602481018390526044016109b4565b6122138682612c3b565b73ffffffffffffffffffffffffffffffffffffffff86165f81815260036020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a361158c8686866112e9565b5f805f6122a9610f84565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2c4e722e00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff92909216916123259190613bc4565b5f60405180830381855afa9150503d805f811461235d576040519150601f19603f3d011682016040523d82523d5f602084013e612362565b606091505b509150915081801561237657506020815110155b612380575f61239c565b61239c818060200190518101906123979190613bdf565b612fd5565b9250505090565b73ffffffffffffffffffffffffffffffffffffffff8116610cb1576040517f17858bbe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016109b4565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612461576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b5f61160582612472610a08565b612fe4565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600860205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116908216811015612544576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602481018290527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9283165f9081526008602052604080822080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101008083048216879003821681027fff000000000000000000000000000000000000000000000000000000000000ff93841617909355959096168352912080548281048516909301909316029216919091179055565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604090205461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff82168110156126ac576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018290526dffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9091165f90815260086020526040902080546dffffffffffffffffffffffffffff8084167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080850482169290920316027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560078054808316939093039091167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000909216919091179055565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600860205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116908216811015612845576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018290527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9091165f90815260086020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080830482168590038216027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560068054808316939093039091167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055565b5f6116058261290c610a08565b612fef565b5f68ffffffffffffffffff821680026123288102642e90edd000820464174876e800830402016c010f6b2be4706a13fc200000000182633b9aca008304669536c708910000018568ffffffffffffffffff1602905080820381830164e8d4a51000028161298057612980613a95565b0495945050505050565b5f81831061299857816115b6565b5090919050565b805f03610cb1576040517f77b8dde3000000000000000000000000000000000000000000000000000000008152600481018290526024016109b4565b5f6129e4610a9a565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f805f612a4a868686612ffa565b90925090505f826005811115612a6257612a62613b97565b14612a6d5781611cdb565b611cdb8782613041565b5f816005811115612a8a57612a8a613b97565b03612a925750565b6001816005811115612aa657612aa6613b97565b03612add576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002816005811115612af157612af1613b97565b03612b28576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003816005811115612b3c57612b3c613b97565b03612b73576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004816005811115612b8757612b87613b97565b03612bbe576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005816005811115612bd257612bd2613b97565b03612c09576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82165f90815260036020908152604080832084845290915290205460ff1615610b68576040517fd309466d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602481018290526044016109b4565b5f805f612cd387878787613084565b90925090505f826005811115612ceb57612ceb613b97565b14612cf65781612d00565b612d008882613041565b98975050505050505050565b5f6115b68383613191565b5f80612d43837f6561726e6572735f6c6973745f69676e6f726564000000000000000000000000612f46565b141592915050565b5f6115b6837f6561726e657273000000000000000000000000000000000000000000000000008461319c565b5f805f612d84858561323c565b90925090505f826005811115612d9c57612d9c613b97565b14612da75781612db1565b612db18682613041565b9695505050505050565b5f805f8573ffffffffffffffffffffffffffffffffffffffff168585604051602401612de8929190613bf6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612e699190613bc4565b5f60405180830381855afa9150503d805f8114612ea1576040519150601f19603f3d011682016040523d82523d5f602084013e612ea6565b606091505b5091509150818015612eba57506020815110155b8015612db1575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612ef89083016020908101908401613bdf565b149695505050505050565b80421115610cb1576040517ff88f0490000000000000000000000000000000000000000000000000000000008152600481018290524260248201526044016109b4565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290525f9073ffffffffffffffffffffffffffffffffffffffff841690638eaa6ac090602401602060405180830381865afa158015612fb1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115b69190613bdf565b5f6116058263ffffffff61298a565b5f6115b68383613280565b5f6115b68383613341565b5f80601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841661303387838884613084565b935093505050935093915050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461307c5760056115b6565b505f92915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156130b95750600390505f613188565b8460ff16601b141580156130d157508460ff16601c14155b156130e15750600490505f613188565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613131573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161561317f575f81613183565b60015f5b915091505b94509492505050565b5f6115b683836133d8565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301525f919085169063d7d1c1c090604401602060405180830381865afa158015613210573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132349190613c0e565b949350505050565b5f8082516041146132525750600290505f613279565b6020830151604084015160608501515f1a919061327187848484613084565b945094505050505b9250929050565b5f816fffffffffffffffffffffffffffffffff165f036132cc576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b66fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff861664e8d4a51000028201018161333b5761333b613a95565b04613409565b5f816fffffffffffffffffffffffffffffffff165f0361338d576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b66fffffffffffffffffffffffffffffffff83167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851664e8d4a51000028161333b5761333b613a95565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff8416026115e2565b5f6dffffffffffffffffffffffffffff821115612461576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561346c578181015183820152602001613454565b50505f910152565b5f815180845261348b816020860160208601613452565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f6115b66020830184613474565b803573ffffffffffffffffffffffffffffffffffffffff811681146134f2575f80fd5b919050565b5f8060408385031215613508575f80fd5b613511836134cf565b946020939093013593505050565b5f805f60608486031215613531575f80fd5b61353a846134cf565b9250613548602085016134cf565b9150604084013590509250925092565b5f805f806080858703121561356b575f80fd5b613574856134cf565b966020860135965060408601359560600135945092505050565b803560ff811681146134f2575f80fd5b5f805f805f60a086880312156135b2575f80fd5b6135bb866134cf565b9450602086013593506135d06040870161358e565b94979396509394606081013594506080013592915050565b5f602082840312156135f8575f80fd5b6115b6826134cf565b7fff00000000000000000000000000000000000000000000000000000000000000881681525f602060e0602084015261363d60e084018a613474565b838103604085015261364f818a613474565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825260208088019350909101905f5b818110156136af57835183529284019291840191600101613693565b50909c9b505050505050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f8301126136fd575f80fd5b813567ffffffffffffffff80821115613718576137186136c1565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561375e5761375e6136c1565b81604052838152866020858801011115613776575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f805f805f805f60e0888a0312156137ab575f80fd5b6137b4886134cf565b96506137c2602089016134cf565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff8111156137f9575f80fd5b6138058a828b016136ee565b91505092959891949750929550565b5f805f805f60a08688031215613828575f80fd5b613831866134cf565b945061383f602087016134cf565b93506040860135925060608601359150608086013567ffffffffffffffff811115613868575f80fd5b613874888289016136ee565b9150509295509295909350565b5f805f805f805f80610100898b031215613899575f80fd5b6138a2896134cf565b97506138b060208a016134cf565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b5f805f606084860312156138f5575f80fd5b6138fe846134cf565b925060208401359150604084013567ffffffffffffffff811115613920575f80fd5b61392c868287016136ee565b9150509250925092565b5f805f805f805f60e0888a03121561394c575f80fd5b613955886134cf565b9650613963602089016134cf565b9550604088013594506060880135935061397f6080890161358e565b925060a0880135915060c0880135905092959891949750929550565b5f80604083850312156139ac575f80fd5b6139b5836134cf565b91506139c3602084016134cf565b90509250929050565b5f805f805f805f805f6101208a8c0312156139e5575f80fd5b6139ee8a6134cf565b98506139fc60208b016134cf565b975060408a0135965060608a0135955060808a0135945060a08a01359350613a2660c08b0161358e565b925060e08a013591506101008a013590509295985092959850929598565b600181811c90821680613a5857607f821691505b602082108103613a8f577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f8083545f60018260011c91506001831680613adf57607f831692505b60208084108203613b17577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b818015613b2b5760018114613b5e57613b89565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650613b89565b5f8a8152602090205f5b86811015613b815781548b820152908501908301613b68565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f8251613bd5818460208701613452565b9190910192915050565b5f60208284031215613bef575f80fd5b5051919050565b828152604060208201525f6132346040830184613474565b5f60208284031215613c1e575f80fd5b815180151581146115b6575f80fdfea2646970667358221220c687d1c508879cda1ed165e77e2f28932b2c0a517d37ea99537bab765838dc6564736f6c63430008170033000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106102f9575f3560e01c80638a75f23811610192578063ace150a5116100e8578063d505accf11610093578063e3ee160e1161006e578063e3ee160e146107e7578063e94a0102146107fa578063ef55bec614610827575f80fd5b8063d505accf14610783578063d916948714610796578063dd62ed3e146107bd575f80fd5b8063c23465b3116100c3578063c23465b31461072c578063c634dfaa1461075d578063cf09299514610770575f80fd5b8063ace150a5146106fe578063b7b7289914610711578063b9f412b014610724575f80fd5b8063a08cb48b11610148578063a36e40fc11610123578063a36e40fc146106bc578063a6ce63cd146106c4578063a9059cbb146106eb575f80fd5b8063a08cb48b1461067a578063a0cc6a681461068d578063a1088459146106b4575f80fd5b80639dc29fac116101785780639dc29fac1461064c5780639f8495f91461065f5780639fd5a6cf14610667575f80fd5b80638a75f2381461063c57806395d89b4114610644575f80fd5b80634c57a8fa116102525780637ecebe00116101fd57806384af270f116101d857806384af270f146105d657806384b0196e1461060e57806388b7ab6314610629575f80fd5b80637ecebe001461057d5780637f2eecc31461059c57806381399be4146105c3575f80fd5b8063578f2aa01161022d578063578f2aa01461053c5780635a049a701461055757806370a082311461056a575f80fd5b80634c57a8fa146104b2578063532992c5146104eb57806353d96f2c146104fe575f80fd5b8063281b229d116102b25780633644e5151161028d5780633644e5151461044957806340c10f191461045157806348545a3c14610466575f80fd5b8063281b229d1461039057806330adf81f146103e9578063313ce56714610410575f80fd5b806318160ddd116102e257806318160ddd1461033e57806323b872dd1461035457806326987b6014610367575f80fd5b806306fdde03146102fd578063095ea7b31461031b575b5f80fd5b61030561083a565b60405161031291906134bd565b60405180910390f35b61032e6103293660046134f7565b6108ca565b6040519015158152602001610312565b6103466108df565b604051908152602001610312565b61032e61036236600461351f565b610917565b61036f610a08565b6040516fffffffffffffffffffffffffffffffff9091168152602001610312565b6006546103ba907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091168152602001610312565b6103467f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6104377f000000000000000000000000000000000000000000000000000000000000000681565b60405160ff9091168152602001610312565b610346610a9a565b61046461045f3660046134f7565b610aef565b005b61048d7f000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e81565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610312565b6007546104cc906dffffffffffffffffffffffffffff1681565b6040516dffffffffffffffffffffffffffff9091168152602001610312565b6104646104f9366004613558565b610b6c565b5f546105269074010000000000000000000000000000000000000000900464ffffffffff1681565b60405164ffffffffff9091168152602001610312565b5f5461036f906fffffffffffffffffffffffffffffffff1681565b61046461056536600461359e565b610b91565b6103466105783660046135e8565b610bb8565b61034661058b3660046135e8565b60026020525f908152604090205481565b6103467fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6104646105d13660046135e8565b610c68565b61032e6105e43660046135e8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526008602052604090205460ff1690565b610616610cb4565b6040516103129796959493929190613601565b610464610637366004613795565b610ded565b6103ba610e1c565b610305610e3a565b61046461065a3660046134f7565b610ec6565b610464610f3f565b610464610675366004613814565b610f4a565b610464610688366004613881565b610f5a565b6103467f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b61048d610f84565b610464610fae565b61048d7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c81565b61032e6106f93660046134f7565b610ff6565b61046461070c366004613881565b611002565b61046461071f3660046138e3565b611022565b61036f61103f565b5f54700100000000000000000000000000000000900463ffffffff1660405163ffffffff9091168152602001610312565b6103ba61076b3660046135e8565b61118f565b61046461077e366004613795565b611201565b610464610791366004613936565b611221565b6103467f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6103466107cb36600461399b565b600560209081525f928352604080842090915290825290205481565b6104646107f53660046139cc565b611231565b61032e6108083660046134f7565b600360209081525f928352604080842090915290825290205460ff1681565b6104646108353660046139cc565b61125c565b60606001805461084990613a44565b80601f016020809104026020016040519081016040528092919081815260200182805461087590613a44565b80156108c05780601f10610897576101008083540402835291602001916108c0565b820191905f5260205f20905b8154815290600101906020018083116108a357829003601f168201915b5050505050905090565b5f6108d633848461127c565b50600192915050565b5f6108e8610e1c565b6006547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081169190910116919050565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526005602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109f257828110156109bd576040517f192b9e4e00000000000000000000000000000000000000000000000000000000815233600482015260248101829052604481018490526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f908152600560209081526040808320338452909152902083820390555b6109fd8585856112e9565b506001949350505050565b5f8054610a9590610a7c906fffffffffffffffffffffffffffffffff811690610a7790612710700100000000000000000000000000000000820463ffffffff1664e8d4a5100002049074010000000000000000000000000000000000000000900464ffffffffff164203611594565b6115bd565b71ffffffffffffffffffffffffffffffffffff166115ea565b905090565b5f7f00000000000000000000000000000000000000000000000000000000000000014614610aca57610a9561160b565b507fc91b8161082e3755d12edaa80f168579bff06d439dd704fb764dfa4abd84134290565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e1614610b5e576040517f65774a1c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b6882826116a6565b5050565b610b8184610b7a86866118cb565b848461193f565b610b8b8484611953565b50505050565b610ba785610b9f87876118cb565b8585856119e1565b610bb18585611953565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260408120805460ff16610c1157805461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16610c41565b8054610c419061010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166119f1565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169392505050565b610c7181611a03565b15610ca8576040517f8b19807700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cb181611a5d565b50565b5f60608082808083600146308480604051908082528060200260200182016040528015610ceb578160200160208202803683370190505b507f0f000000000000000000000000000000000000000000000000000000000000009493929190848054610d1e90613a44565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4a90613a44565b8015610d955780601f10610d6c57610100808354040283529160200191610d95565b820191905f5260205f20905b815481529060010190602001808311610d7857829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b610e0587610dff898989898989611c5f565b83611ce6565b610e13878787878787611d31565b50505050505050565b6007545f90610a95906dffffffffffffffffffffffffffff166119f1565b60048054610e4790613a44565b80601f0160208091040260200160405190810160405280929190818152602001828054610e7390613a44565b8015610ebe5780601f10610e9557610100808354040283529160200191610ebe565b820191905f5260205f20905b815481529060010190602001808311610ea157829003601f168201915b505050505081565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e1614610f35576040517f65774a1c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b688282611dac565b610f4833611a5d565b565b610bb185610dff87878787611e5e565b610f6c88610b7a8a8a8a8a8a8a611c5f565b610f7a888888888888611d31565b5050505050505050565b5f610a957f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c611f09565b610fb733611a03565b610fed576040517fdd93dca800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f4833611f3a565b5f6108d63384846112e9565b61101488610b7a8a8a8a8a8a8a612109565b610f7a888888888888612185565b61103083610dff85856118cb565b61103a8383611953565b505050565b5f8061104961229e565b5f5490915064ffffffffff74010000000000000000000000000000000000000000909104164214801561109957505f5463ffffffff82811670010000000000000000000000000000000090920416145b156110b75750505f546fffffffffffffffffffffffffffffffff1690565b6110bf610a08565b5f80546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811770010000000000000000000000000000000063ffffffff8616908102919091177fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004264ffffffffff16021783556040519395509290917f8f9a1730052b867fdeb484b52fbc51e9bb62830781805ac95c382bbf8ea717a291a35090565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260408120805460ff166111c2575f6111ea565b805461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff165b6dffffffffffffffffffffffffffff169392505050565b61121387610dff898989898989612109565b610e13878787878787612185565b610e1387610b9f89898989611e5e565b61124389610b9f8b8b8b8b8b8b612109565b611251898989898989612185565b505050505050505050565b61126e89610b9f8b8b8b8b8b8b611c5f565b611251898989898989611d31565b73ffffffffffffffffffffffffffffffffffffffff8381165f8181526005602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6112f2826123a3565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161135191815260200190565b60405180910390a35f61136382612408565b73ffffffffffffffffffffffffffffffffffffffff8086165f9081526008602052604080822054928716825290205491925060ff90811691161515811515036113d457610bb18585836113b65784612477565b6113bf85612465565b6dffffffffffffffffffffffffffff16612477565b80156114a3576113ec856113e784612465565b6125e3565b73ffffffffffffffffffffffffffffffffffffffff84165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081168601811690920217909155600680547fffff00000000000000000000000000000000000000000000000000000000000081169083168501909216919091179055611584565b6114ad8583612778565b611584846114ba846128ff565b73ffffffffffffffffffffffffffffffffffffffff919091165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff81166dffffffffffffffffffffffffffff808516610100938490047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908116919091011690920217909155600780547fffffffffffffffffffffffffffffffffffff00000000000000000000000000008116908316909301909116919091179055565b61158c61103f565b505050505050565b5f6115b66301e1338067ffffffffffffffff851663ffffffff85160204612911565b9392505050565b5f64e8d4a510006fffffffffffffffffffffffffffffffff841665ffffffffffff8416025b049392505050565b5f611605826fffffffffffffffffffffffffffffffff61298a565b92915050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405161163e9190613ac2565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6116af8161299f565b6116b8826123a3565b60405181815273ffffffffffffffffffffffffffffffffffffffff8316905f907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35f61170f82612408565b6006549091507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90811681831601118061179b57506006546dffffffffffffffffffffffffffff90611780907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff168301612465565b6007546dffffffffffffffffffffffffffff90811691160110155b156117d2576040517f5165589500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f9081526008602052604090205460ff16156118145761180c836114ba836128ff565b610b8b61103f565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff8116610100918290047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081168501811690920217909155600680547fffff00000000000000000000000000000000000000000000000000000000000081169083168401909216919091179055505050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429602082015273ffffffffffffffffffffffffffffffffffffffff841691810191909152606081018290525f906115b6906080015b604051602081830303815290604052805190602001206129db565b610b8b61194e85858585612a3c565b612a77565b61195d8282612c3b565b73ffffffffffffffffffffffffffffffffffffffff82165f81815260036020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8191a35050565b610bb161194e8686868686612cc4565b5f611605826119fe610a08565b612d0c565b5f611a2d7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c612d17565b8061160557506116057f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c83612d4b565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260409020805460ff16611a8e575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f9467bac89b535c15fcd73b0e7b12e123a045fd17124952dfa868dfdf5e42d48d905f90a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016815573ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604081205461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116916dffffffffffffffffffffffffffff9091169003611b5d57505050565b5f611b67826119f1565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260086020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808416610100027fff000000000000000000000000000000000000000000000000000000000000ff909216919091179091556006805480831684019092167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055600780546dffffffffffffffffffffffffffff808216869003167fffffffffffffffffffffffffffffffffffff00000000000000000000000000009091161790559050610bb161103f565b604080517fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e081018290525f90611cdb9061010001611924565b979650505050505050565b5f611cf2848484612d77565b90505f816005811115611d0757611d07613b97565b03611d125750505050565b611d1d848484612dbb565b15611d285750505050565b610b8b81612a77565b3373ffffffffffffffffffffffffffffffffffffffff861614611d9e576040517f1c5939f300000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff861660248201526044016109b4565b61158c868686868686612185565b611db58161299f565b6040518181525f9073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604090205460ff1615611e4c57611e44826113e7611e3f84612408565b612465565b61103a61103f565b610b6882611e5983612408565b612778565b5f611e6882612f03565b611e7385858561127c565b73ffffffffffffffffffffffffffffffffffffffff8581165f8181526002602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9928101929092529181019290925291861660608201526080810185905260a081019190915260c08101839052611f009060e001611924565b95945050505050565b5f611605611f37837f6561726e65725f726174655f6d6f64656c000000000000000000000000000000612f46565b90565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600860205260409020805460ff1615611f6c575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907f8fbc5add0c3fc76c7a869df537ee9250843681f6bbc2ea9735d40c6dc259414c905f90a280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178082557dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100909104165f81900361200957505050565b5f612013826128ff565b73ffffffffffffffffffffffffffffffffffffffff85165f90815260086020526040902080547fff000000000000000000000000000000000000000000000000000000000000ff166dffffffffffffffffffffffffffff8381166101000291909117909155600780547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081169083168401909216919091179055600680547fffff00000000000000000000000000000000000000000000000000000000000081167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9182168690039091161790559050610bb161103f565b604080517f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267602082015273ffffffffffffffffffffffffffffffffffffffff8089169282019290925290861660608201526080810185905260a0810184905260c0810183905260e081018290525f90611cdb9061010001611924565b8242116121c7576040517f498ff9a2000000000000000000000000000000000000000000000000000000008152426004820152602481018490526044016109b4565b814210612209576040517fb3fcd33e000000000000000000000000000000000000000000000000000000008152426004820152602481018390526044016109b4565b6122138682612c3b565b73ffffffffffffffffffffffffffffffffffffffff86165f81815260036020908152604080832085845290915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055518392917f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a591a361158c8686866112e9565b5f805f6122a9610f84565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2c4e722e00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff92909216916123259190613bc4565b5f60405180830381855afa9150503d805f811461235d576040519150601f19603f3d011682016040523d82523d5f602084013e612362565b606091505b509150915081801561237657506020815110155b612380575f61239c565b61239c818060200190518101906123979190613bdf565b612fd5565b9250505090565b73ffffffffffffffffffffffffffffffffffffffff8116610cb1576040517f17858bbe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016109b4565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612461576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b5f61160582612472610a08565b612fe4565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600860205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116908216811015612544576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602481018290527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9283165f9081526008602052604080822080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101008083048216879003821681027fff000000000000000000000000000000000000000000000000000000000000ff93841617909355959096168352912080548281048516909301909316029216919091179055565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526008602052604090205461010090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff82168110156126ac576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018290526dffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9091165f90815260086020526040902080546dffffffffffffffffffffffffffff8084167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080850482169290920316027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560078054808316939093039091167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000909216919091179055565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600860205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101009091048116908216811015612845576040517fdb42144d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602481018290527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660448201526064016109b4565b5073ffffffffffffffffffffffffffffffffffffffff9091165f90815260086020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010080830482168590038216027fff000000000000000000000000000000000000000000000000000000000000ff9092169190911790915560068054808316939093039091167fffff000000000000000000000000000000000000000000000000000000000000909216919091179055565b5f6116058261290c610a08565b612fef565b5f68ffffffffffffffffff821680026123288102642e90edd000820464174876e800830402016c010f6b2be4706a13fc200000000182633b9aca008304669536c708910000018568ffffffffffffffffff1602905080820381830164e8d4a51000028161298057612980613a95565b0495945050505050565b5f81831061299857816115b6565b5090919050565b805f03610cb1576040517f77b8dde3000000000000000000000000000000000000000000000000000000008152600481018290526024016109b4565b5f6129e4610a9a565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f805f612a4a868686612ffa565b90925090505f826005811115612a6257612a62613b97565b14612a6d5781611cdb565b611cdb8782613041565b5f816005811115612a8a57612a8a613b97565b03612a925750565b6001816005811115612aa657612aa6613b97565b03612add576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002816005811115612af157612af1613b97565b03612b28576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003816005811115612b3c57612b3c613b97565b03612b73576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004816005811115612b8757612b87613b97565b03612bbe576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005816005811115612bd257612bd2613b97565b03612c09576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82165f90815260036020908152604080832084845290915290205460ff1615610b68576040517fd309466d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602481018290526044016109b4565b5f805f612cd387878787613084565b90925090505f826005811115612ceb57612ceb613b97565b14612cf65781612d00565b612d008882613041565b98975050505050505050565b5f6115b68383613191565b5f80612d43837f6561726e6572735f6c6973745f69676e6f726564000000000000000000000000612f46565b141592915050565b5f6115b6837f6561726e657273000000000000000000000000000000000000000000000000008461319c565b5f805f612d84858561323c565b90925090505f826005811115612d9c57612d9c613b97565b14612da75781612db1565b612db18682613041565b9695505050505050565b5f805f8573ffffffffffffffffffffffffffffffffffffffff168585604051602401612de8929190613bf6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612e699190613bc4565b5f60405180830381855afa9150503d805f8114612ea1576040519150601f19603f3d011682016040523d82523d5f602084013e612ea6565b606091505b5091509150818015612eba57506020815110155b8015612db1575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612ef89083016020908101908401613bdf565b149695505050505050565b80421115610cb1576040517ff88f0490000000000000000000000000000000000000000000000000000000008152600481018290524260248201526044016109b4565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290525f9073ffffffffffffffffffffffffffffffffffffffff841690638eaa6ac090602401602060405180830381865afa158015612fb1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115b69190613bdf565b5f6116058263ffffffff61298a565b5f6115b68383613280565b5f6115b68383613341565b5f80601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841661303387838884613084565b935093505050935093915050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461307c5760056115b6565b505f92915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156130b95750600390505f613188565b8460ff16601b141580156130d157508460ff16601c14155b156130e15750600490505f613188565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613131573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161561317f575f81613183565b60015f5b915091505b94509492505050565b5f6115b683836133d8565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301525f919085169063d7d1c1c090604401602060405180830381865afa158015613210573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132349190613c0e565b949350505050565b5f8082516041146132525750600290505f613279565b6020830151604084015160608501515f1a919061327187848484613084565b945094505050505b9250929050565b5f816fffffffffffffffffffffffffffffffff165f036132cc576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b66fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff861664e8d4a51000028201018161333b5761333b613a95565b04613409565b5f816fffffffffffffffffffffffffffffffff165f0361338d576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115b66fffffffffffffffffffffffffffffffff83167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851664e8d4a51000028161333b5761333b613a95565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff8416026115e2565b5f6dffffffffffffffffffffffffffff821115612461576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561346c578181015183820152602001613454565b50505f910152565b5f815180845261348b816020860160208601613452565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f6115b66020830184613474565b803573ffffffffffffffffffffffffffffffffffffffff811681146134f2575f80fd5b919050565b5f8060408385031215613508575f80fd5b613511836134cf565b946020939093013593505050565b5f805f60608486031215613531575f80fd5b61353a846134cf565b9250613548602085016134cf565b9150604084013590509250925092565b5f805f806080858703121561356b575f80fd5b613574856134cf565b966020860135965060408601359560600135945092505050565b803560ff811681146134f2575f80fd5b5f805f805f60a086880312156135b2575f80fd5b6135bb866134cf565b9450602086013593506135d06040870161358e565b94979396509394606081013594506080013592915050565b5f602082840312156135f8575f80fd5b6115b6826134cf565b7fff00000000000000000000000000000000000000000000000000000000000000881681525f602060e0602084015261363d60e084018a613474565b838103604085015261364f818a613474565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825260208088019350909101905f5b818110156136af57835183529284019291840191600101613693565b50909c9b505050505050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f8301126136fd575f80fd5b813567ffffffffffffffff80821115613718576137186136c1565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561375e5761375e6136c1565b81604052838152866020858801011115613776575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f805f805f805f60e0888a0312156137ab575f80fd5b6137b4886134cf565b96506137c2602089016134cf565b955060408801359450606088013593506080880135925060a0880135915060c088013567ffffffffffffffff8111156137f9575f80fd5b6138058a828b016136ee565b91505092959891949750929550565b5f805f805f60a08688031215613828575f80fd5b613831866134cf565b945061383f602087016134cf565b93506040860135925060608601359150608086013567ffffffffffffffff811115613868575f80fd5b613874888289016136ee565b9150509295509295909350565b5f805f805f805f80610100898b031215613899575f80fd5b6138a2896134cf565b97506138b060208a016134cf565b979a9799505050506040860135956060810135956080820135955060a0820135945060c0820135935060e0909101359150565b5f805f606084860312156138f5575f80fd5b6138fe846134cf565b925060208401359150604084013567ffffffffffffffff811115613920575f80fd5b61392c868287016136ee565b9150509250925092565b5f805f805f805f60e0888a03121561394c575f80fd5b613955886134cf565b9650613963602089016134cf565b9550604088013594506060880135935061397f6080890161358e565b925060a0880135915060c0880135905092959891949750929550565b5f80604083850312156139ac575f80fd5b6139b5836134cf565b91506139c3602084016134cf565b90509250929050565b5f805f805f805f805f6101208a8c0312156139e5575f80fd5b6139ee8a6134cf565b98506139fc60208b016134cf565b975060408a0135965060608a0135955060808a0135945060a08a01359350613a2660c08b0161358e565b925060e08a013591506101008a013590509295985092959850929598565b600181811c90821680613a5857607f821691505b602082108103613a8f577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f8083545f60018260011c91506001831680613adf57607f831692505b60208084108203613b17577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b818015613b2b5760018114613b5e57613b89565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650613b89565b5f8a8152602090205f5b86811015613b815781548b820152908501908301613b68565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f8251613bd5818460208701613452565b9190910192915050565b5f60208284031215613bef575f80fd5b5051919050565b828152604060208201525f6132346040830184613474565b5f60208284031215613c1e575f80fd5b815180151581146115b6575f80fdfea2646970667358221220c687d1c508879cda1ed165e77e2f28932b2c0a517d37ea99537bab765838dc6564736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e
-----Decoded View---------------
Arg [0] : ttgRegistrar_ (address): 0x119FbeeDD4F4f4298Fb59B720d5654442b81ae2c
Arg [1] : minterGateway_ (address): 0xf7f9638cb444D65e5A40bF5ff98ebE4ff319F04E
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c
Arg [1] : 000000000000000000000000f7f9638cb444d65e5a40bf5ff98ebe4ff319f04e
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.
Add Token to MetaMask (Web3)