Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 1,506 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Update Collatera... | 8532466 | 5 days ago | IN | 0 ETH | 0.00021671 | ||||
Burn M | 8490658 | 11 days ago | IN | 0 ETH | 0.00012299 | ||||
Mint M | 8489884 | 11 days ago | IN | 0 ETH | 0.000138 | ||||
Propose Mint | 8489846 | 11 days ago | IN | 0 ETH | 0.00009618 | ||||
Update Collatera... | 8489305 | 11 days ago | IN | 0 ETH | 0.00016078 | ||||
Update Collatera... | 8488272 | 11 days ago | IN | 0 ETH | 0.00016619 | ||||
Update Collatera... | 8468190 | 14 days ago | IN | 0 ETH | 0.00015065 | ||||
Mint M | 8466372 | 14 days ago | IN | 0 ETH | 0.000138 | ||||
Propose Mint | 8466354 | 14 days ago | IN | 0 ETH | 0.00009618 | ||||
Mint M | 8466325 | 14 days ago | IN | 0 ETH | 0.0001551 | ||||
Propose Mint | 8466305 | 14 days ago | IN | 0 ETH | 0.00014437 | ||||
Propose Retrieva... | 8466269 | 14 days ago | IN | 0 ETH | 0.00007933 | ||||
Mint M | 8466252 | 14 days ago | IN | 0 ETH | 0.000138 | ||||
Propose Mint | 8466206 | 14 days ago | IN | 0 ETH | 0.00009618 | ||||
Mint M | 8466099 | 14 days ago | IN | 0 ETH | 0.000138 | ||||
Propose Mint | 8466080 | 14 days ago | IN | 0 ETH | 0.00005918 | ||||
Burn M | 8466071 | 14 days ago | IN | 0 ETH | 0.00012299 | ||||
Propose Mint | 8466069 | 14 days ago | IN | 0 ETH | 0.00006198 | ||||
Update Collatera... | 8466060 | 14 days ago | IN | 0 ETH | 0.00016078 | ||||
Update Collatera... | 8460388 | 15 days ago | IN | 0 ETH | 0.00016077 | ||||
Update Collatera... | 8460163 | 15 days ago | IN | 0 ETH | 0.00016078 | ||||
Update Collatera... | 8460162 | 15 days ago | IN | 0 ETH | 0.00016619 | ||||
Update Collatera... | 8423110 | 20 days ago | IN | 0 ETH | 0.00015817 | ||||
Burn M | 8417792 | 21 days ago | IN | 0 ETH | 0.00012423 | ||||
Update Collatera... | 8416397 | 21 days ago | IN | 0 ETH | 0.00016163 |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
MinterGateway
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 { SignatureChecker } from "../lib/common/src/libs/SignatureChecker.sol"; import { ERC712Extended } from "../lib/common/src/ERC712Extended.sol"; import { UIntMath } from "../lib/common/src/libs/UIntMath.sol"; import { TTGRegistrarReader } from "./libs/TTGRegistrarReader.sol"; import { IContinuousIndexing } from "./interfaces/IContinuousIndexing.sol"; import { IMToken } from "./interfaces/IMToken.sol"; import { IMinterGateway } from "./interfaces/IMinterGateway.sol"; import { IRateModel } from "./interfaces/IRateModel.sol"; import { ContinuousIndexing } from "./abstract/ContinuousIndexing.sol"; import { ContinuousIndexingMath } from "./libs/ContinuousIndexingMath.sol"; /* ███╗ ███╗██╗███╗ ██╗████████╗███████╗██████╗ ██████╗ █████╗ ████████╗███████╗██╗ ██╗ █████╗ ██╗ ██╗ ████╗ ████║██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗ ██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝██║ ██║██╔══██╗╚██╗ ██╔╝ ██╔████╔██║██║██╔██╗ ██║ ██║ █████╗ ██████╔╝ ██║ ███╗███████║ ██║ █████╗ ██║ █╗ ██║███████║ ╚████╔╝ ██║╚██╔╝██║██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗ ██║ ██║██╔══██║ ██║ ██╔══╝ ██║███╗██║██╔══██║ ╚██╔╝ ██║ ╚═╝ ██║██║██║ ╚████║ ██║ ███████╗██║ ██║ ╚██████╔╝██║ ██║ ██║ ███████╗╚███╔███╔╝██║ ██║ ██║ ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ -->> Where money is born. <<-- */ /** * @title MinterGateway * @author M^0 Labs * @notice Minting Gateway of M Token for all approved by TTG and activated minters. */ contract MinterGateway is IMinterGateway, ContinuousIndexing, ERC712Extended { /* ============ Structs ============ */ /** * @notice Mint proposal struct. * @param id The unique ID of the mint proposal. * @param createdAt The timestamp at which the mint proposal was created. * @param destination The address to mint M to. * @param amount The amount of M to mint. */ struct MintProposal { // 1st slot uint48 id; uint40 createdAt; address destination; // 2nd slot uint240 amount; } /** * @notice Minter state struct. * @param isActive Whether the minter is active or not. * @param isDeactivated Whether the minter is deactivated or not. * @param collateral The amount of collateral the minter has. * @param totalPendingRetrievals The total amount of pending retrievals. * @param updateTimestamp The timestamp at which the minter last updated their collateral. * @param penalizedUntilTimestamp The timestamp until which the minter is penalized. * @param frozenUntilTimestamp The timestamp until which the minter is frozen. */ struct MinterState { // 1st slot bool isActive; bool isDeactivated; uint240 collateral; // 2nd slot uint240 totalPendingRetrievals; // 3rd slot uint40 updateTimestamp; uint40 penalizedUntilTimestamp; uint40 frozenUntilTimestamp; uint40 latestProposedRetrievalTimestamp; } /* ============ Variables ============ */ /// @inheritdoc IMinterGateway uint16 public constant ONE = 10_000; /// @inheritdoc IMinterGateway uint32 public constant MAX_MINT_RATIO = 65_000; /// @notice IMinterGateway uint32 public constant MIN_UPDATE_COLLATERAL_INTERVAL = 3_600; // solhint-disable-next-line max-line-length /// @dev keccak256("UpdateCollateral(address minter,uint256 collateral,uint256[] retrievalIds,bytes32 metadataHash,uint256 timestamp)") /// @inheritdoc IMinterGateway bytes32 public constant UPDATE_COLLATERAL_TYPEHASH = 0x22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e; /// @inheritdoc IMinterGateway address public immutable ttgRegistrar; /// @inheritdoc IMinterGateway address public immutable ttgVault; /// @inheritdoc IMinterGateway address public immutable mToken; /// @inheritdoc IMinterGateway uint240 public totalInactiveOwedM; /// @inheritdoc IMinterGateway uint112 public principalOfTotalActiveOwedM; /// @dev Nonce used to generate unique mint proposal IDs. uint48 internal _mintNonce; /// @dev Nonce used to generate unique retrieval proposal IDs. uint48 internal _retrievalNonce; /// @dev The state of each minter, their collaterals, relevant timestamps, and total pending retrievals. mapping(address minter => MinterState state) internal _minterStates; /// @dev The mint proposals of minter (mint ID, creation timestamp, destination, amount). mapping(address minter => MintProposal proposal) internal _mintProposals; /// @dev The owed M of active and inactive minters (principal of active, inactive). mapping(address minter => uint240 rawOwedM) internal _rawOwedM; /// @dev The pending collateral retrievals of minter (retrieval ID, amount). mapping(address minter => mapping(uint48 retrievalId => uint240 amount)) internal _pendingCollateralRetrievals; /// @dev The last update signature timestamp of each validator for each minter. mapping(address minter => mapping(address validator => uint256 timestamp)) internal _lastSignatureTimestamp; /* ============ Modifiers ============ */ /** * @notice Only allow active minter to call function. * @param minter_ The address of the minter to check. */ modifier onlyActiveMinter(address minter_) { _revertIfInactiveMinter(minter_); _; } /// @notice Only allow approved validator in TTG to call function. modifier onlyApprovedValidator() { _revertIfNotApprovedValidator(msg.sender); _; } /// @notice Only allow unfrozen minter to call function. modifier onlyUnfrozenMinter() { _revertIfFrozenMinter(msg.sender); _; } /* ============ Constructor ============ */ /** * @notice Constructor. * @param ttgRegistrar_ The address of the TTG Registrar contract. * @param mToken_ The address of the M Token. */ constructor(address ttgRegistrar_, address mToken_) ContinuousIndexing() ERC712Extended("MinterGateway") { if ((ttgRegistrar = ttgRegistrar_) == address(0)) revert ZeroTTGRegistrar(); if ((ttgVault = TTGRegistrarReader.getVault(ttgRegistrar_)) == address(0)) revert ZeroTTGVault(); if ((mToken = mToken_) == address(0)) revert ZeroMToken(); } /* ============ Interactive Functions ============ */ /// @inheritdoc IMinterGateway function updateCollateral( uint256 collateral_, uint256[] calldata retrievalIds_, bytes32 metadataHash_, address[] calldata validators_, uint256[] calldata timestamps_, bytes[] calldata signatures_ ) external onlyActiveMinter(msg.sender) returns (uint40 minTimestamp_) { if (validators_.length != signatures_.length || signatures_.length != timestamps_.length) { revert SignatureArrayLengthsMismatch(); } // Verify that enough valid signatures are provided, and get the minimum timestamp across all valid signatures. minTimestamp_ = _verifyValidatorSignatures( msg.sender, collateral_, retrievalIds_, metadataHash_, validators_, timestamps_, signatures_ ); uint240 safeCollateral_ = UIntMath.safe240(collateral_); uint240 totalResolvedCollateralRetrieval_ = _resolvePendingRetrievals(msg.sender, retrievalIds_); emit CollateralUpdated( msg.sender, safeCollateral_, totalResolvedCollateralRetrieval_, metadataHash_, minTimestamp_ ); _imposePenaltyIfMissedCollateralUpdates(msg.sender); _imposePenaltyIfUndercollateralized(msg.sender, minTimestamp_); _updateCollateral(msg.sender, safeCollateral_, minTimestamp_); // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the collateral // update can result in a new rate, we should update the index here to lock in that rate. updateIndex(); } /// @inheritdoc IMinterGateway function proposeRetrieval(uint256 collateral_) external onlyActiveMinter(msg.sender) returns (uint48 retrievalId_) { if (collateral_ == 0) revert ZeroRetrievalAmount(); unchecked { retrievalId_ = ++_retrievalNonce; } MinterState storage minterState_ = _minterStates[msg.sender]; uint240 currentCollateral_ = minterState_.collateral; uint240 safeCollateral_ = UIntMath.safe240(collateral_); uint240 updatedTotalPendingRetrievals_ = minterState_.totalPendingRetrievals + safeCollateral_; // NOTE: Revert if collateral is less than sum of all pending retrievals even if there is no owed M by minter. if (currentCollateral_ < updatedTotalPendingRetrievals_) { revert RetrievalsExceedCollateral(updatedTotalPendingRetrievals_, currentCollateral_); } minterState_.latestProposedRetrievalTimestamp = uint40(block.timestamp); minterState_.totalPendingRetrievals = updatedTotalPendingRetrievals_; _pendingCollateralRetrievals[msg.sender][retrievalId_] = safeCollateral_; _revertIfUndercollateralized(msg.sender, 0); emit RetrievalCreated(retrievalId_, msg.sender, safeCollateral_); } /// @inheritdoc IMinterGateway function proposeMint( uint256 amount_, address destination_ ) external onlyActiveMinter(msg.sender) onlyUnfrozenMinter returns (uint48 mintId_) { if (amount_ == 0) revert ZeroMintAmount(); if (destination_ == address(0)) revert ZeroMintDestination(); uint240 safeAmount_ = UIntMath.safe240(amount_); _revertIfUndercollateralized(msg.sender, safeAmount_); // Ensure minter remains sufficiently collateralized. unchecked { mintId_ = ++_mintNonce; } _mintProposals[msg.sender] = MintProposal(mintId_, uint40(block.timestamp), destination_, safeAmount_); emit MintProposed(mintId_, msg.sender, safeAmount_, destination_); } /// @inheritdoc IMinterGateway function mintM( uint256 mintId_ ) external onlyActiveMinter(msg.sender) onlyUnfrozenMinter returns (uint112 principalAmount_, uint240 amount_) { MintProposal storage mintProposal_ = _mintProposals[msg.sender]; uint48 id_; uint40 createdAt_; address destination_; (id_, createdAt_, destination_, amount_) = ( mintProposal_.id, mintProposal_.createdAt, mintProposal_.destination, mintProposal_.amount ); if (id_ != mintId_) revert InvalidMintProposal(); unchecked { // Check that mint proposal is executable. uint40 activeAt_ = createdAt_ + mintDelay(); if (block.timestamp < activeAt_) revert PendingMintProposal(activeAt_); uint40 expiresAt_ = activeAt_ + mintTTL(); if (block.timestamp > expiresAt_) revert ExpiredMintProposal(expiresAt_); } _revertIfUndercollateralized(msg.sender, amount_); // Ensure minter remains sufficiently collateralized. delete _mintProposals[msg.sender]; // Delete mint request. // Adjust principal of active owed M for minter. // NOTE: When minting a present amount, round the principal up in favor of the protocol. principalAmount_ = _getPrincipalAmountRoundedUp(amount_); uint112 principalOfTotalActiveOwedM_ = principalOfTotalActiveOwedM; emit MintExecuted(id_, msg.sender, principalAmount_, amount_); unchecked { uint256 newPrincipalOfTotalActiveOwedM_ = uint256(principalOfTotalActiveOwedM_) + principalAmount_; // As an edge case precaution, prevent a mint that, if all owed M (active and inactive) was converted to // a principal active amount, would overflow the `uint112 principalOfTotalActiveOwedM`. if ( // NOTE: Round the principal up for worst case. newPrincipalOfTotalActiveOwedM_ + _getPrincipalAmountRoundedUp(totalInactiveOwedM) >= type(uint112).max ) { revert OverflowsPrincipalOfTotalOwedM(); } principalOfTotalActiveOwedM = uint112(newPrincipalOfTotalActiveOwedM_); _rawOwedM[msg.sender] += principalAmount_; // Treat rawOwedM as principal since minter is active. } IMToken(mToken).mint(destination_, amount_); // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the mint // can result in a new rate, we should update the index here to lock in that rate. updateIndex(); } /// @inheritdoc IMinterGateway function burnM(address minter_, uint256 maxAmount_) external returns (uint112 principalAmount_, uint240 amount_) { (principalAmount_, amount_) = burnM( minter_, _getPrincipalAmountRoundedDown(UIntMath.safe240(maxAmount_)), maxAmount_ ); } /// @inheritdoc IMinterGateway function burnM( address minter_, uint256 maxPrincipalAmount_, uint256 maxAmount_ ) public returns (uint112 principalAmount_, uint240 amount_) { if (maxPrincipalAmount_ == 0 || maxAmount_ == 0) revert ZeroBurnAmount(); MinterState storage minterState_ = _minterStates[minter_]; bool isActive_ = minterState_.isActive; // Revert early if minter has not been activated. if (!isActive_ && !minterState_.isDeactivated) revert InactiveMinter(); if (isActive_) { // NOTE: Penalize only for missed collateral updates, not for undercollateralization. // Undercollateralization within one update interval is forgiven. _imposePenaltyIfMissedCollateralUpdates(minter_); (principalAmount_, amount_) = _repayForActiveMinter( minter_, UIntMath.safe112(maxPrincipalAmount_), UIntMath.safe240(maxAmount_) ); emit BurnExecuted(minter_, principalAmount_, amount_, msg.sender); } else { amount_ = _repayForDeactivatedMinter(minter_, UIntMath.safe240(maxAmount_)); emit BurnExecuted(minter_, amount_, msg.sender); } IMToken(mToken).burn(msg.sender, amount_); // Burn actual M tokens // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the burn // can result in a new rate, we should update the index here to lock in that rate. updateIndex(); } /// @inheritdoc IMinterGateway function cancelMint(address minter_, uint256 mintId_) external onlyApprovedValidator { uint48 id_ = _mintProposals[minter_].id; if (id_ != mintId_ || id_ == 0) revert InvalidMintProposal(); delete _mintProposals[minter_]; emit MintCanceled(id_, minter_, msg.sender); } /// @inheritdoc IMinterGateway function freezeMinter(address minter_) external onlyApprovedValidator returns (uint40 frozenUntil_) { unchecked { _minterStates[minter_].frozenUntilTimestamp = frozenUntil_ = uint40(block.timestamp) + minterFreezeTime(); } emit MinterFrozen(minter_, frozenUntil_); } /// @inheritdoc IMinterGateway function activateMinter(address minter_) external { if (!isMinterApproved(minter_)) revert NotApprovedMinter(); MinterState storage minterState_ = _minterStates[minter_]; if (minterState_.isDeactivated) revert DeactivatedMinter(); minterState_.isActive = true; emit MinterActivated(minter_, msg.sender); } /// @inheritdoc IMinterGateway function deactivateMinter(address minter_) external onlyActiveMinter(minter_) returns (uint240 inactiveOwedM_) { if (isMinterApproved(minter_)) revert StillApprovedMinter(); _imposePenaltyIfMissedCollateralUpdates(minter_); uint112 principalOfOwedM_ = principalOfActiveOwedMOf(minter_); inactiveOwedM_ = _getPresentAmount(principalOfOwedM_); unchecked { // Treat rawOwedM as principal since minter is active. principalOfTotalActiveOwedM -= principalOfOwedM_; totalInactiveOwedM += inactiveOwedM_; } emit MinterDeactivated(minter_, inactiveOwedM_, msg.sender); // Reset reasonable aspects of minter's state delete _minterStates[minter_]; delete _mintProposals[minter_]; // Deactivate minter. _minterStates[minter_].isDeactivated = true; _rawOwedM[minter_] = inactiveOwedM_; // Treat rawOwedM as inactive owed M since minter is now inactive. // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the // deactivation can result in a new rate, we should update the index here to lock in that rate. updateIndex(); } /// @inheritdoc IContinuousIndexing function updateIndex() public override(IContinuousIndexing, ContinuousIndexing) returns (uint128 index_) { // NOTE: Since the currentIndex of the Minter Gateway and mToken are constant through this context's execution // (the block.timestamp is not changing) we can compute excessOwedM without updating the mToken index. uint240 excessOwedM_ = excessOwedM(); if (excessOwedM_ > 0) IMToken(mToken).mint(ttgVault, excessOwedM_); // Mint M to TTG Vault. // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the collateral // update can result in a new rate, we should update the index here to lock in that rate. // NOTE: With the current rate models, the minter rate does not depend on anything in the Minter Gateway // or mToken, so we can update the minter rate and index here. index_ = super.updateIndex(); // Update minter index and rate. // NOTE: Given the current implementation of the mToken transfers and its rate model, while it is possible for // the above mint to already have updated the mToken index if M was minted to an earning account, we want // to ensure the rate provided by the mToken's rate model is locked in. IMToken(mToken).updateIndex(); // Update earning index and rate. } /* ============ View/Pure Functions ============ */ /// @inheritdoc IMinterGateway function totalActiveOwedM() public view returns (uint240) { return _getPresentAmount(principalOfTotalActiveOwedM); } /// @inheritdoc IMinterGateway function totalOwedM() external view returns (uint240) { unchecked { // NOTE: This can never overflow since the `mint` functions caps the principal of total owed M (active and // inactive) to `type(uint112).max`. Thus, there can never be enough inactive owed M (which is an // accumulations principal of active owed M values converted to present values at previous and lower // indices) or active owed M to overflow this. return totalActiveOwedM() + totalInactiveOwedM; } } /// @inheritdoc IMinterGateway function excessOwedM() public view returns (uint240 excessOwedM_) { // NOTE: Can safely cast to `uint240` since we know M Token totalSupply constraints. uint240 totalMSupply_ = uint240(IMToken(mToken).totalSupply()); uint240 totalOwedM_ = _getPresentAmountRoundedDown(principalOfTotalActiveOwedM, currentIndex()) + totalInactiveOwedM; unchecked { if (totalOwedM_ > totalMSupply_) return totalOwedM_ - totalMSupply_; } } /// @inheritdoc IMinterGateway function minterRate() external view returns (uint32) { return _latestRate; } /// @inheritdoc IMinterGateway function isActiveMinter(address minter_) external view returns (bool) { return _minterStates[minter_].isActive; } /// @inheritdoc IMinterGateway function isDeactivatedMinter(address minter_) external view returns (bool) { return _minterStates[minter_].isDeactivated; } /// @inheritdoc IMinterGateway function isFrozenMinter(address minter_) external view returns (bool) { return block.timestamp < _minterStates[minter_].frozenUntilTimestamp; } /// @inheritdoc IMinterGateway function principalOfActiveOwedMOf(address minter_) public view returns (uint112) { // NOTE: This should also include the principal value of unavoidable penalities. But then it would be very, if // not impossible, to determine the `principalOfTotalActiveOwedM` to the same standards. return _minterStates[minter_].isActive ? uint112(_rawOwedM[minter_]) // Treat rawOwedM as principal since minter is active. : 0; } /// @inheritdoc IMinterGateway function activeOwedMOf(address minter_) public view returns (uint240) { // NOTE: This should also include the present value of unavoidable penalities. But then it would be very, if // not impossible, to determine the `totalActiveOwedM` to the same standards. return _minterStates[minter_].isActive ? _getPresentAmount(uint112(_rawOwedM[minter_])) // Treat rawOwedM as principal since minter is active. : 0; } /// @inheritdoc IMinterGateway function maxAllowedActiveOwedMOf(address minter_) public view returns (uint256) { // NOTE: Since `mintRatio()` is capped at 650% (i.e. 65_000) this cannot overflow. unchecked { return _minterStates[minter_].isActive ? (uint256(collateralOf(minter_)) * mintRatio()) / ONE : 0; } } /// @inheritdoc IMinterGateway function inactiveOwedMOf(address minter_) public view returns (uint240) { // Treat rawOwedM as present amount since minter is inactive. return _minterStates[minter_].isActive ? 0 : _rawOwedM[minter_]; } /// @inheritdoc IMinterGateway function collateralOf(address minter_) public view returns (uint240) { // If collateral was not updated by the deadline, assume that minter's collateral is zero. if (block.timestamp >= collateralExpiryTimestampOf(minter_)) return 0; MinterState storage minterState_ = _minterStates[minter_]; uint240 totalPendingRetrievals_ = minterState_.totalPendingRetrievals; uint240 collateral_ = minterState_.collateral; // If the minter's total pending retrievals is greater than their collateral, then their collateral is zero. if (totalPendingRetrievals_ >= collateral_) return 0; unchecked { return collateral_ - totalPendingRetrievals_; } } /// @inheritdoc IMinterGateway function collateralUpdateTimestampOf(address minter_) external view returns (uint40) { return _minterStates[minter_].updateTimestamp; } /// @inheritdoc IMinterGateway function collateralPenaltyDeadlineOf(address minter_) external view returns (uint40) { MinterState storage minterState_ = _minterStates[minter_]; uint32 updateCollateralInterval_ = updateCollateralInterval(); (, uint40 missedUntil_) = _getMissedCollateralUpdateParameters( minterState_.updateTimestamp, minterState_.penalizedUntilTimestamp, updateCollateralInterval_ ); return missedUntil_ + updateCollateralInterval_; } /// @inheritdoc IMinterGateway function collateralExpiryTimestampOf(address minter_) public view returns (uint40) { unchecked { return _minterStates[minter_].updateTimestamp + updateCollateralInterval(); } } /// @inheritdoc IMinterGateway function penalizedUntilOf(address minter_) external view returns (uint40) { return _minterStates[minter_].penalizedUntilTimestamp; } /// @inheritdoc IMinterGateway function latestProposedRetrievalTimestampOf(address minter_) external view returns (uint40) { return _minterStates[minter_].latestProposedRetrievalTimestamp; } /// @inheritdoc IMinterGateway function getLastSignatureTimestamp(address minter_, address validator_) external view returns (uint256) { return _lastSignatureTimestamp[minter_][validator_]; } /// @inheritdoc IMinterGateway function getUpdateCollateralDigest( address minter_, uint256 collateral_, uint256[] calldata retrievalIds_, bytes32 metadataHash_, uint256 timestamp_ ) external view returns (bytes32) { return _getUpdateCollateralDigest(minter_, collateral_, retrievalIds_, metadataHash_, timestamp_); } /// @inheritdoc IMinterGateway function mintProposalOf( address minter_ ) external view returns (uint48 mintId_, uint40 createdAt_, address destination_, uint240 amount_) { mintId_ = _mintProposals[minter_].id; createdAt_ = _mintProposals[minter_].createdAt; destination_ = _mintProposals[minter_].destination; amount_ = _mintProposals[minter_].amount; } /// @inheritdoc IMinterGateway function pendingCollateralRetrievalOf(address minter_, uint256 retrievalId_) external view returns (uint240) { return _minterStates[minter_].isDeactivated ? 0 : _pendingCollateralRetrievals[minter_][UIntMath.safe48(retrievalId_)]; } /// @inheritdoc IMinterGateway function totalPendingCollateralRetrievalOf(address minter_) external view returns (uint240) { return _minterStates[minter_].isDeactivated ? 0 : _minterStates[minter_].totalPendingRetrievals; } /// @inheritdoc IMinterGateway function frozenUntilOf(address minter_) external view returns (uint40) { return _minterStates[minter_].frozenUntilTimestamp; } /* ============ TTG Registrar Reader Functions ============ */ /// @inheritdoc IMinterGateway function isMinterApproved(address minter_) public view returns (bool) { return TTGRegistrarReader.isApprovedMinter(ttgRegistrar, minter_); } /// @inheritdoc IMinterGateway function isValidatorApproved(address validator_) public view returns (bool) { return TTGRegistrarReader.isApprovedValidator(ttgRegistrar, validator_); } /// @inheritdoc IMinterGateway function updateCollateralInterval() public view returns (uint32) { return UIntMath.max32( UIntMath.bound32(TTGRegistrarReader.getUpdateCollateralInterval(ttgRegistrar)), MIN_UPDATE_COLLATERAL_INTERVAL ); } /// @inheritdoc IMinterGateway function updateCollateralValidatorThreshold() public view returns (uint256) { return TTGRegistrarReader.getUpdateCollateralValidatorThreshold(ttgRegistrar); } /// @inheritdoc IMinterGateway function mintRatio() public view returns (uint32) { // NOTE: It is possible for the mint ratio to be greater than 100%, but capped at 650%. return UIntMath.min32(MAX_MINT_RATIO, UIntMath.bound32(TTGRegistrarReader.getMintRatio(ttgRegistrar))); } /// @inheritdoc IMinterGateway function mintDelay() public view returns (uint32) { return UIntMath.bound32(TTGRegistrarReader.getMintDelay(ttgRegistrar)); } /// @inheritdoc IMinterGateway function mintTTL() public view returns (uint32) { return UIntMath.bound32(TTGRegistrarReader.getMintTTL(ttgRegistrar)); } /// @inheritdoc IMinterGateway function minterFreezeTime() public view returns (uint32) { return UIntMath.bound32(TTGRegistrarReader.getMinterFreezeTime(ttgRegistrar)); } /// @inheritdoc IMinterGateway function penaltyRate() public view returns (uint32) { return UIntMath.bound32(TTGRegistrarReader.getPenaltyRate(ttgRegistrar)); } /// @inheritdoc IMinterGateway function rateModel() public view returns (address) { return TTGRegistrarReader.getMinterRateModel(ttgRegistrar); } /// @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.multiplyIndicesUp( latestIndex, ContinuousIndexingMath.getContinuousIndex( ContinuousIndexingMath.convertFromBasisPoints(_latestRate), uint32(block.timestamp - latestUpdateTimestamp) ) ) ); } } /* ============ Internal Interactive Functions ============ */ /** * @dev Imposes penalty on an active minter. Calling this for an inactive minter will break accounting. * @param minter_ The address of the minter. * @param principalOfPenaltyBase_ The principal of the base for penalization. * @return The principal of the imposed penalty. */ function _imposePenalty(address minter_, uint152 principalOfPenaltyBase_) internal returns (uint112) { if (principalOfPenaltyBase_ == 0) return 0; uint32 penaltyRate_ = penaltyRate(); if (penaltyRate_ == 0) return 0; unchecked { uint256 penaltyPrincipal_ = (uint256(principalOfPenaltyBase_) * penaltyRate_) / ONE; // As an edge case precaution, cap the penalty principal such that the resulting principal of total active // owed M plus the penalty principal is not greater than the max uint112. uint256 newPrincipalOfTotalActiveOwedM_ = principalOfTotalActiveOwedM + penaltyPrincipal_; if (newPrincipalOfTotalActiveOwedM_ > type(uint112).max) { penaltyPrincipal_ = type(uint112).max - principalOfTotalActiveOwedM; newPrincipalOfTotalActiveOwedM_ = type(uint112).max; } // Calculate and add penalty principal to total minter's principal of active owed M principalOfTotalActiveOwedM = uint112(newPrincipalOfTotalActiveOwedM_); _rawOwedM[minter_] += uint112(penaltyPrincipal_); // Treat rawOwedM as principal since minter is active. return uint112(penaltyPrincipal_); } } /** * @dev Imposes penalty if minter missed collateral updates. * @param minter_ The address of the minter. */ function _imposePenaltyIfMissedCollateralUpdates(address minter_) internal { MinterState storage minterState_ = _minterStates[minter_]; (uint40 missedIntervals_, uint40 missedUntil_) = _getMissedCollateralUpdateParameters( minterState_.updateTimestamp, minterState_.penalizedUntilTimestamp, updateCollateralInterval() ); if (missedIntervals_ == 0) return; // Save until when the minter has been penalized for missed intervals to prevent double penalizing them. minterState_.penalizedUntilTimestamp = missedUntil_; uint112 principalOfActiveOwedM_ = principalOfActiveOwedMOf(minter_); if (principalOfActiveOwedM_ == 0) return; uint112 penaltyPrincipal_ = _imposePenalty(minter_, uint152(principalOfActiveOwedM_) * missedIntervals_); if (penaltyPrincipal_ == 0) return; emit MissedIntervalsPenaltyImposed(minter_, missedIntervals_, _getPresentAmount(penaltyPrincipal_)); } /** * @dev Imposes penalty if minter is undercollateralized. * @param minter_ The address of the minter. * @param newTimestamp_ The timestamp of the collateral update. */ function _imposePenaltyIfUndercollateralized(address minter_, uint40 newTimestamp_) internal { uint112 principalOfActiveOwedM_ = principalOfActiveOwedMOf(minter_); if (principalOfActiveOwedM_ == 0) return; uint256 maxAllowedActiveOwedM_ = maxAllowedActiveOwedMOf(minter_); // If the minter's max allowed active owed M is greater than `type(uint240).max`, then it's definitely greater // than the max possible active owed M for the minter, which is capped at `type(uint240).max`. if (maxAllowedActiveOwedM_ >= type(uint240).max) return; // NOTE: Round the principal down in favor of the protocol since this is a max applied to the minter. uint112 principalOfMaxAllowedActiveOwedM_ = _getPrincipalAmountRoundedDown(uint240(maxAllowedActiveOwedM_)); // If the minter is not undercollateralized, then no penalty is imposed. if (principalOfMaxAllowedActiveOwedM_ >= principalOfActiveOwedM_) return; MinterState storage minterState_ = _minterStates[minter_]; uint40 penalizeFrom_ = UIntMath.max40(minterState_.updateTimestamp, minterState_.penalizedUntilTimestamp); if (newTimestamp_ <= penalizeFrom_) return; unchecked { uint40 timeSpan_ = newTimestamp_ - penalizeFrom_; uint112 principalOfExcessOwedM_ = principalOfActiveOwedM_ - principalOfMaxAllowedActiveOwedM_; // NOTE: `newTimestamp_ - penalizeFrom_` will never be larger than `updateCollateralInterval_` since this // function is only called after `_imposePenaltyIfMissedCollateralUpdates`, which ensures that the // `penalizedUntilTimestamp` is within one `updateCollateralInterval_` of the `newTimestamp_`. // // NOTE: `updateCollateralInterval()` never equals 0, so the division is safe. // Its minimum is capped at `MIN_UPDATE_COLLATERAL_INTERVAL`. uint112 penaltyPrincipal_ = _imposePenalty( minter_, (principalOfExcessOwedM_ * timeSpan_) / updateCollateralInterval() ); if (penaltyPrincipal_ == 0) return; emit UndercollateralizedPenaltyImposed( minter_, _getPresentAmount(principalOfExcessOwedM_), timeSpan_, _getPresentAmount(penaltyPrincipal_) ); } } /** * @dev Repays active minter's owed M. * @param minter_ The address of the minter. * @param maxPrincipalAmount_ The maximum principal amount of active owed M to repay. * @param maxAmount_ The maximum amount of active owed M to repay. * @return principalAmount_ The principal amount of active owed M that was actually repaid. * @return amount_ The amount of active owed M that was actually repaid. */ function _repayForActiveMinter( address minter_, uint112 maxPrincipalAmount_, uint240 maxAmount_ ) internal returns (uint112 principalAmount_, uint240 amount_) { principalAmount_ = UIntMath.min112(principalOfActiveOwedMOf(minter_), maxPrincipalAmount_); amount_ = _getPresentAmount(principalAmount_); if (amount_ > maxAmount_) revert ExceedsMaxRepayAmount(amount_, maxAmount_); unchecked { // Treat rawOwedM as principal since `principalAmount_` would only be non-zero for an active minter. _rawOwedM[minter_] -= principalAmount_; principalOfTotalActiveOwedM -= principalAmount_; } } /** * @dev Repays deactivated minter's owed M. * @param minter_ The address of the minter. * @param maxAmount_ The maximum amount of inactive owed M to repay. * @return amount_ The amount of inactive owed M that was actually repaid. */ function _repayForDeactivatedMinter(address minter_, uint240 maxAmount_) internal returns (uint240 amount_) { amount_ = UIntMath.min240(inactiveOwedMOf(minter_), maxAmount_); unchecked { // Treat rawOwedM as present amount since `amount_` would only be non-zero for an inactive minter. _rawOwedM[minter_] -= amount_; totalInactiveOwedM -= amount_; } } /** * @dev Resolves the collateral retrieval IDs and updates the total pending collateral retrieval amount. * @param minter_ The address of the minter. * @param retrievalIds_ The list of outstanding collateral retrieval IDs to resolve. * @return totalResolvedCollateralRetrieval_ The total amount of collateral retrieval resolved. */ function _resolvePendingRetrievals( address minter_, uint256[] calldata retrievalIds_ ) internal returns (uint240 totalResolvedCollateralRetrieval_) { for (uint256 index_; index_ < retrievalIds_.length; ++index_) { uint48 retrievalId_ = UIntMath.safe48(retrievalIds_[index_]); uint240 pendingCollateralRetrieval_ = _pendingCollateralRetrievals[minter_][retrievalId_]; if (pendingCollateralRetrieval_ == 0) continue; unchecked { // NOTE: The `proposeRetrieval` function already ensures that the sum of all // `_pendingCollateralRetrievals` is not larger than `type(uint240).max`. totalResolvedCollateralRetrieval_ += pendingCollateralRetrieval_; } delete _pendingCollateralRetrievals[minter_][retrievalId_]; emit RetrievalResolved(retrievalId_, minter_); } unchecked { // NOTE: The `proposeRetrieval` function already ensures that `totalPendingRetrievals` is the sum of all // `_pendingCollateralRetrievals`. _minterStates[minter_].totalPendingRetrievals -= totalResolvedCollateralRetrieval_; } } /** * @dev Updates the collateral amount and update timestamp for the minter. * @param minter_ The address of the minter. * @param amount_ The amount of collateral. * @param newTimestamp_ The timestamp of the collateral update. */ function _updateCollateral(address minter_, uint240 amount_, uint40 newTimestamp_) internal { MinterState storage minterState_ = _minterStates[minter_]; // The earliest allowed timestamp for a collateral update is the maximum of: // - the last update timestamp, // - the latest proposed retrieval timestamp, and // - the current timestamp minus the update collateral interval. unchecked { // NOTE: Cannot underflow since `min40` is applied when `updateCollateralInterval()` > `block.timestamp`. uint40 earliestAllowedTimestamp_ = UIntMath.max40( UIntMath.max40(minterState_.updateTimestamp, minterState_.latestProposedRetrievalTimestamp), uint40(block.timestamp) - UIntMath.min40(updateCollateralInterval(), uint40(block.timestamp)) ); if (newTimestamp_ <= earliestAllowedTimestamp_) { revert StaleCollateralUpdate(newTimestamp_, earliestAllowedTimestamp_); } } minterState_.collateral = amount_; minterState_.updateTimestamp = newTimestamp_; } /* ============ Internal View/Pure Functions ============ */ /** * @dev Returns the penalization base and the penalized until timestamp. * @param lastUpdateTimestamp_ The last timestamp at which the minter updated their collateral. * @param lastPenalizedUntil_ The timestamp before which the minter shouldn't be penalized for missed updates. * @param updateInterval_ The update collateral interval. * @return missedIntervals_ The number of missed update intervals. * @return missedUntil_ The timestamp until which `missedIntervals_` covers, * even if `missedIntervals_` is 0. */ function _getMissedCollateralUpdateParameters( uint40 lastUpdateTimestamp_, uint40 lastPenalizedUntil_, uint32 updateInterval_ ) internal view returns (uint40 missedIntervals_, uint40 missedUntil_) { uint40 penalizeFrom_ = UIntMath.max40(lastUpdateTimestamp_, lastPenalizedUntil_); // If brand new minter then there is no missed interval charge at all. if (lastUpdateTimestamp_ == 0) return (0, penalizeFrom_); uint40 timeElapsed_ = uint40(block.timestamp) - penalizeFrom_; if (timeElapsed_ < updateInterval_) return (0, penalizeFrom_); unchecked { // NOTE: `updateInterval_` never equals 0, so the division is safe. // Its minimum is capped at `MIN_UPDATE_COLLATERAL_INTERVAL`. missedIntervals_ = timeElapsed_ / updateInterval_; // NOTE: Cannot really overflow a `uint40` since `missedIntervals_ * updateInterval_ <= timeElapsed_`. missedUntil_ = penalizeFrom_ + (missedIntervals_ * updateInterval_); } } /** * @dev Returns the present amount (rounded up) given the principal amount, using the current index. * All present amounts are rounded up in favor of the protocol, since they are owed. * @param principalAmount_ The principal amount. * @return The present amount. */ function _getPresentAmount(uint112 principalAmount_) internal view returns (uint240) { return _getPresentAmountRoundedUp(principalAmount_, currentIndex()); } /** * @dev Returns the EIP-712 digest for updateCollateral method. * @param minter_ The address of the minter. * @param collateral_ The amount of collateral. * @param retrievalIds_ The list of outstanding collateral retrieval IDs to resolve. * @param metadataHash_ The hash of metadata of the collateral update, reserved for future informational use. * @param timestamp_ The timestamp of the collateral update. * @return The EIP-712 digest. */ function _getUpdateCollateralDigest( address minter_, uint256 collateral_, uint256[] calldata retrievalIds_, bytes32 metadataHash_, uint256 timestamp_ ) internal view returns (bytes32) { return _getDigest( keccak256( abi.encode( UPDATE_COLLATERAL_TYPEHASH, minter_, collateral_, keccak256(abi.encodePacked(retrievalIds_)), metadataHash_, timestamp_ ) ) ); } /// @dev Returns the current rate from the rate model contract. 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 minter is frozen by validator. * @param minter_ The address of the minter */ function _revertIfFrozenMinter(address minter_) internal view { if (block.timestamp < _minterStates[minter_].frozenUntilTimestamp) revert FrozenMinter(); } /** * @dev Reverts if minter is inactive. * @param minter_ The address of the minter */ function _revertIfInactiveMinter(address minter_) internal view { if (!_minterStates[minter_].isActive) revert InactiveMinter(); } /** * @dev Reverts if validator is not approved. * @param validator_ The address of the validator */ function _revertIfNotApprovedValidator(address validator_) internal view { if (!isValidatorApproved(validator_)) revert NotApprovedValidator(validator_); } /** * @dev Reverts if minter position will be undercollateralized after changes. * @param minter_ The address of the minter * @param additionalOwedM_ The amount of additional owed M the action will add to minter's position */ function _revertIfUndercollateralized(address minter_, uint240 additionalOwedM_) internal view { uint256 maxAllowedActiveOwedM_ = maxAllowedActiveOwedMOf(minter_); // If the minter's max allowed active owed M is greater than the max uint240, then it's definitely greater than // the max possible active owed M for the minter, which is capped at the max uint240. if (maxAllowedActiveOwedM_ >= type(uint240).max) return; unchecked { uint256 finalActiveOwedM_ = uint256(activeOwedMOf(minter_)) + additionalOwedM_; if (finalActiveOwedM_ > maxAllowedActiveOwedM_) { revert Undercollateralized(finalActiveOwedM_, maxAllowedActiveOwedM_); } } } /** * @dev Checks that enough valid unique signatures were provided. * @param minter_ The address of the minter. * @param collateral_ The amount of collateral. * @param retrievalIds_ The list of outstanding collateral retrieval IDs to resolve. * @param metadataHash_ The hash of metadata of the collateral update, reserved for future informational use. * @param validators_ The list of validators. * @param timestamps_ The list of validator timestamps for the collateral update signatures. * @param signatures_ The list of signatures. * @return minTimestamp_ The minimum timestamp across all valid timestamps with valid signatures. */ function _verifyValidatorSignatures( address minter_, uint256 collateral_, uint256[] calldata retrievalIds_, bytes32 metadataHash_, address[] calldata validators_, uint256[] calldata timestamps_, bytes[] calldata signatures_ ) internal returns (uint40 minTimestamp_) { minTimestamp_ = uint40(block.timestamp); uint256 validCount_; for (uint256 index_; index_ < signatures_.length; ++index_) { unchecked { // Check that validator address is unique and not accounted for // NOTE: We revert here because this failure is entirely within the minter's control. if (index_ > 0 && validators_[index_] <= validators_[index_ - 1]) revert InvalidSignatureOrder(); } if ( !_verifyValidatorSignature( minter_, collateral_, retrievalIds_, metadataHash_, validators_[index_], timestamps_[index_], signatures_[index_] ) ) continue; // Find minimum between all valid timestamps for valid signatures. minTimestamp_ = UIntMath.min40(minTimestamp_, uint40(timestamps_[index_])); unchecked { ++validCount_; } } uint256 requiredThreshold_ = updateCollateralValidatorThreshold(); if (validCount_ < requiredThreshold_) revert NotEnoughValidSignatures(validCount_, requiredThreshold_); } /** * @dev Checks that a signature is a valid validator signature. * @param minter_ The address of the minter. * @param collateral_ The amount of collateral. * @param retrievalIds_ The list of outstanding collateral retrieval IDs to resolve. * @param metadataHash_ The hash of metadata of the collateral update, reserved for future informational use. * @param validator_ The address of a validator. * @param timestamp_ The timestamp for the collateral update signature. * @param signature_ The signature from the validator. * @return Whether the signature is a valid validator signature or not. */ function _verifyValidatorSignature( address minter_, uint256 collateral_, uint256[] calldata retrievalIds_, bytes32 metadataHash_, address validator_, uint256 timestamp_, bytes calldata signature_ ) internal returns (bool) { // Check that the timestamp is not 0. // NOTE: Revert here because this failure is entirely within the minter's control. if (timestamp_ == 0) revert ZeroTimestamp(); // Check that the timestamp is not in the future. // NOTE: Revert here because this failure is entirely within the minter's control. if (timestamp_ > uint40(block.timestamp)) revert FutureTimestamp(); uint256 lastTimestamp_ = _lastSignatureTimestamp[minter_][validator_]; // Check that the timestamp is not older than the last signature timestamp. // NOTE: Revert here because this failure is entirely within the minter's control. if (timestamp_ <= lastTimestamp_) revert OutdatedValidatorTimestamp(validator_, timestamp_, lastTimestamp_); // Check that validator is approved by TTG. if (!isValidatorApproved(validator_)) return false; // Check that ECDSA or ERC1271 signatures for given digest are valid. if ( !SignatureChecker.isValidSignature( validator_, _getUpdateCollateralDigest(minter_, collateral_, retrievalIds_, metadataHash_, timestamp_), signature_ ) ) return false; // Save the last signature timestamp for the minter and validator combination. _lastSignatureTimestamp[minter_][validator_] = timestamp_; return true; } }
// 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; 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; /** * @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; 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; import { IERC712 } from "../../lib/common/src/interfaces/IERC712.sol"; import { IContinuousIndexing } from "./IContinuousIndexing.sol"; /** * @title Minter Gateway Interface. * @author M^0 Labs */ interface IMinterGateway is IContinuousIndexing, IERC712 { /* ============ Events ============ */ /** * @notice Emitted when M tokens are burned and an inactive minter's owed M balance decreased. * @param minter The address of the minter. * @param amount The amount of M tokens burned. * @param payer The address of the payer. */ event BurnExecuted(address indexed minter, uint240 amount, address indexed payer); /** * @notice Emitted when M tokens are burned and an active minter's owed M balance decreased. * @param minter The address of the minter. * @param principalAmount The principal amount of M tokens burned. * @param amount The amount of M tokens burned. * @param payer The address of the payer. */ event BurnExecuted(address indexed minter, uint112 principalAmount, uint240 amount, address indexed payer); /** * @notice Emitted when a minter's collateral is updated. * @param minter Address of the minter * @param collateral The latest amount of collateral * @param totalResolvedCollateralRetrieval The total collateral amount of outstanding retrievals resolved. * @param metadataHash The hash of some metadata reserved for future informational use. * @param timestamp The timestamp of the collateral update, * minimum of given validators' signatures. */ event CollateralUpdated( address indexed minter, uint240 collateral, uint240 totalResolvedCollateralRetrieval, bytes32 indexed metadataHash, uint40 timestamp ); /** * @notice Emitted when a minter is activated. * @param minter Address of the minter that was activated * @param caller Address who called the function */ event MinterActivated(address indexed minter, address indexed caller); /** * @notice Emitted when a minter is deactivated. * @param minter Address of the minter that was deactivated. * @param inactiveOwedM Amount of M tokens owed by the minter (in an inactive state). * @param caller Address who called the function. */ event MinterDeactivated(address indexed minter, uint240 inactiveOwedM, address indexed caller); /** * @notice Emitted when a minter is frozen. * @param minter Address of the minter that was frozen * @param frozenUntil Timestamp until the minter is frozen */ event MinterFrozen(address indexed minter, uint40 frozenUntil); /** * @notice Emitted when a mint proposal is canceled. * @param mintId The id of the canceled mint proposal. * @param minter The address of the minter for which the mint was canceled. * @param canceller The address of the validator who canceled the mint proposal. */ event MintCanceled(uint48 indexed mintId, address indexed minter, address indexed canceller); /** * @notice Emitted when a mint proposal is executed. * @param mintId The id of the executed mint proposal. * @param minter The address of the minter that executed the mint. * @param principalAmount The principal amount of M tokens minted. * @param amount The amount of M tokens minted. */ event MintExecuted(uint48 indexed mintId, address indexed minter, uint112 principalAmount, uint240 amount); /** * @notice Emitted when a mint proposal is created. * @param mintId The id of mint proposal. * @param minter The address of the minter that proposed the mint. * @param amount The amount of M tokens to mint. * @param destination The address to mint to. */ event MintProposed(uint48 indexed mintId, address indexed minter, uint240 amount, address indexed destination); /** * @notice Emitted when a penalty is imposed on `minter` for missed update collateral intervals. * @param minter The address of the minter. * @param missedIntervals The number of update intervals missed. * @param penaltyAmount The present amount of penalty charge. */ event MissedIntervalsPenaltyImposed(address indexed minter, uint40 missedIntervals, uint240 penaltyAmount); /** * @notice Emitted when a penalty is imposed on `minter` for undercollateralization. * @param minter The address of the minter. * @param excessOwedM The present amount of owed M in excess of allowed owed M. * @param timeSpan The span of time over which the undercollateralization penalty was applied. * @param penaltyAmount The present amount of penalty charge. */ event UndercollateralizedPenaltyImposed( address indexed minter, uint240 excessOwedM, uint40 timeSpan, uint240 penaltyAmount ); /** * @notice Emitted when a collateral retrieval proposal is created. * @param retrievalId The id of retrieval proposal. * @param minter The address of the minter. * @param amount The amount of collateral to retrieve. */ event RetrievalCreated(uint48 indexed retrievalId, address indexed minter, uint240 amount); /** * @notice Emitted when a collateral retrieval proposal is resolved. * @param retrievalId The id of retrieval proposal. * @param minter The address of the minter. */ event RetrievalResolved(uint48 indexed retrievalId, address indexed minter); /* ============ Custom Errors ============ */ /// @notice Emitted when calling `activateMinter` with a minter who was previously deactivated. error DeactivatedMinter(); /// @notice Emitted when repay will burn more M than the repay specified. error ExceedsMaxRepayAmount(uint240 amount, uint240 maxAmount); /// @notice Emitted when calling `mintM` with a proposal that was created more than `mintDelay + mintTTL` time ago. error ExpiredMintProposal(uint40 deadline); /// @notice Emitted when calling `mintM` or `proposeMint` by a minter who was frozen by validator. error FrozenMinter(); /// @notice Emitted when calling `updateCollateral` with any validator timestamp in the future. error FutureTimestamp(); /// @notice Emitted when calling a function only allowed for active minters. error InactiveMinter(); /// @notice Emitted when calling `cancelMint` or `mintM` with invalid `mintId`. error InvalidMintProposal(); /// @notice Emitted when calling `updateCollateral` if `validators` addresses are not ordered in ascending order. error InvalidSignatureOrder(); /// @notice Emitted when calling `activateMinter` if minter was not approved by TTG. error NotApprovedMinter(); /// @notice Emitted when calling `cancelMint` or `freezeMinter` if `validator` was not approved by TTG. error NotApprovedValidator(address validator); /// @notice Emitted when calling `updateCollateral` if `validatorThreshold` of signatures was not reached. error NotEnoughValidSignatures(uint256 validSignatures, uint256 requiredThreshold); /// @notice Emitted when principal of total owed M (active and inactive) will overflow a `type(uint112).max`. error OverflowsPrincipalOfTotalOwedM(); /// @notice Emitted when calling `mintM` if `mintDelay` time has not passed yet. error PendingMintProposal(uint40 activeTimestamp); /// @notice Emitted when calling `proposeRetrieval` if sum of all outstanding retrievals /// Plus new proposed retrieval amount is greater than collateral. error RetrievalsExceedCollateral(uint240 totalPendingRetrievals, uint240 collateral); /// @notice Emitted when calling `updateCollateral` /// If `validators`, `signatures`, `timestamps` lengths do not match. error SignatureArrayLengthsMismatch(); /// @notice Emitted when updating collateral with a timestamp earlier than allowed. error StaleCollateralUpdate(uint40 newTimestamp, uint40 earliestAllowedTimestamp); /// @notice Emitted when calling `updateCollateral` with any validator timestamp older than the last signature /// timestamp for that minter and validator. error OutdatedValidatorTimestamp(address validator, uint256 timestamp, uint256 lastSignatureTimestamp); /// @notice Emitted when calling `deactivateMinter` with a minter still approved in TTG Registrar. error StillApprovedMinter(); /** * @notice Emitted when calling `proposeMint`, `mintM`, `proposeRetrieval` * If minter position becomes undercollateralized. * @dev `activeOwedM` is a `uint256` because it may represent some resulting owed M from computations. */ error Undercollateralized(uint256 activeOwedM, uint256 maxAllowedOwedM); /// @notice Emitted when calling `burnM` if amount is 0. error ZeroBurnAmount(); /// @notice Emitted in constructor if M Token is 0x0. error ZeroMToken(); /// @notice Emitted when calling `proposeMint` if amount is 0. error ZeroMintAmount(); /// @notice Emitted when calling `proposeMint` if destination is 0x0. error ZeroMintDestination(); /// @notice Emitted when calling `proposeRetrieval` if collateral is 0. error ZeroRetrievalAmount(); /// @notice Emitted in constructor if TTG Registrar is 0x0. error ZeroTTGRegistrar(); /// @notice Emitted in constructor if TTG Distribution Vault is set to 0x0 in TTG Registrar. error ZeroTTGVault(); /// @notice Emitted when calling `updateCollateral` with any validator timestamp of 0. error ZeroTimestamp(); /* ============ Interactive Functions ============ */ /** * @notice Updates collateral for minters * @param collateral The amount of collateral * @param retrievalIds The list of active proposeRetrieval requests to close * @param metadataHash The hash of metadata of the collateral update, reserved for future informational use * @param validators The list of validators * @param timestamps The list of timestamps of validators' signatures * @param signatures The list of signatures * @return minTimestamp The minimum timestamp of all validators' signatures */ function updateCollateral( uint256 collateral, uint256[] calldata retrievalIds, bytes32 metadataHash, address[] calldata validators, uint256[] calldata timestamps, bytes[] calldata signatures ) external returns (uint40 minTimestamp); /** * @notice Proposes retrieval of minter's off-chain collateral * @param collateral The amount of collateral to retrieve * @return retrievalId The unique id of created retrieval proposal */ function proposeRetrieval(uint256 collateral) external returns (uint48 retrievalId); /** * @notice Proposes minting of M tokens * @param amount The amount of M tokens to mint * @param destination The address to mint to * @return mintId The unique id of created mint proposal */ function proposeMint(uint256 amount, address destination) external returns (uint48 mintId); /** * @notice Executes minting of M tokens * @param mintId The id of outstanding mint proposal for minter * @return principalAmount The amount of principal of owed M minted. * @return amount The amount of M tokens minted. */ function mintM(uint256 mintId) external returns (uint112 principalAmount, uint240 amount); /** * @notice Burns M tokens * @dev If amount to burn is greater than minter's owedM including penalties, burn all up to owedM. * @param minter The address of the minter to burn M tokens for. * @param maxAmount The max amount of M tokens to burn. * @return principalAmount The amount of principal of owed M burned. * @return amount The amount of M tokens burned. */ function burnM(address minter, uint256 maxAmount) external returns (uint112 principalAmount, uint240 amount); /** * @notice Burns M tokens * @dev If amount to burn is greater than minter's owedM including penalties, burn all up to owedM. * @param minter The address of the minter to burn M tokens for. * @param maxPrincipalAmount The max amount of principal of owed M to burn. * @param maxAmount The max amount of M tokens to burn. * @return principalAmount The amount of principal of owed M burned. * @return amount The amount of M tokens burned. */ function burnM( address minter, uint256 maxPrincipalAmount, uint256 maxAmount ) external returns (uint112 principalAmount, uint240 amount); /** * @notice Cancels minting request for selected minter by validator * @param minter The address of the minter to cancelMint minting request for * @param mintId The id of outstanding mint request */ function cancelMint(address minter, uint256 mintId) external; /** * @notice Freezes minter * @param minter The address of the minter to freeze * @return frozenUntil The timestamp until which minter is frozen */ function freezeMinter(address minter) external returns (uint40 frozenUntil); /** * @notice Activate an approved minter. * @dev MUST revert if `minter` is not recorded as an approved minter in TTG Registrar. * @dev MUST revert if `minter` has been deactivated. * @param minter The address of the minter to activate */ function activateMinter(address minter) external; /** * @notice Deactivates an active minter. * @dev MUST revert if the minter is still approved. * @dev MUST revert if the minter is not active. * @param minter The address of the minter to deactivate. * @return inactiveOwedM The inactive owed M for the deactivated minter. */ function deactivateMinter(address minter) external returns (uint240 inactiveOwedM); /* ============ View/Pure Functions ============ */ /// @notice The address of M token function mToken() external view returns (address); /// @notice The address of TTG Registrar contract. function ttgRegistrar() external view returns (address); /// @notice The address of TTG Vault contract. function ttgVault() external view returns (address); /// @notice The last saved value of Minter rate. function minterRate() external view returns (uint32); /// @notice The principal of total owed M for all active minters. function principalOfTotalActiveOwedM() external view returns (uint112); /// @notice The total owed M for all active minters. function totalActiveOwedM() external view returns (uint240); /// @notice The total owed M for all inactive minters. function totalInactiveOwedM() external view returns (uint240); /// @notice The total owed M for all minters. function totalOwedM() external view returns (uint240); /// @notice The difference between total owed M and M token total supply. function excessOwedM() external view returns (uint240); /// @notice The principal of active owed M of minter. function principalOfActiveOwedMOf(address minter_) external view returns (uint112); /// @notice The active owed M of minter. function activeOwedMOf(address minter) external view returns (uint240); /** * @notice The max allowed active owed M of minter taking into account collateral amount and retrieval proposals. * @dev This is the only present value that requires a `uint256` since it is the result of a multiplication * between a `uint240` and a value that has a max of `65,000` (the mint ratio). */ function maxAllowedActiveOwedMOf(address minter) external view returns (uint256); /// @notice The inactive owed M of deactivated minter. function inactiveOwedMOf(address minter) external view returns (uint240); /// @notice The collateral of a given minter. function collateralOf(address minter) external view returns (uint240); /// @notice The timestamp of the last collateral update of minter. function collateralUpdateTimestampOf(address minter) external view returns (uint40); /// @notice The timestamp after which an additional penalty for a missed update interval will be charged. function collateralPenaltyDeadlineOf(address minter) external view returns (uint40); /// @notice The timestamp after which the minter's collateral is assumed to be 0 due to a missed update. function collateralExpiryTimestampOf(address minter) external view returns (uint40); /// @notice The timestamp until which minter is already penalized for missed collateral updates. function penalizedUntilOf(address minter) external view returns (uint40); /// @notice The timestamp when `minter` created their latest retrieval proposal. function latestProposedRetrievalTimestampOf(address minter) external view returns (uint40); /** * @notice Returns the last signature timestamp used by `validator` to update collateral for `minter`. * @param minter The address of the minter. * @param validator The address of the validator. * @return The last signature timestamp used. */ function getLastSignatureTimestamp(address minter, address validator) external view returns (uint256); /** * @notice Returns the EIP-712 digest for updateCollateral method. * @param minter The address of the minter. * @param collateral The amount of collateral. * @param retrievalIds The list of outstanding collateral retrieval IDs to resolve. * @param metadataHash The hash of metadata of the collateral update, reserved for future informational use. * @param timestamp The timestamp of the collateral update. */ function getUpdateCollateralDigest( address minter, uint256 collateral, uint256[] calldata retrievalIds, bytes32 metadataHash, uint256 timestamp ) external view returns (bytes32); /// @notice The mint proposal of minters, only 1 active proposal per minter function mintProposalOf( address minter ) external view returns (uint48 mintId, uint40 createdAt, address destination, uint240 amount); /// @notice The amount of a pending retrieval request for an active minter. function pendingCollateralRetrievalOf(address minter, uint256 retrievalId) external view returns (uint240); /// @notice The total amount of pending retrieval requests for an active minter. function totalPendingCollateralRetrievalOf(address minter) external view returns (uint240); /// @notice The timestamp when minter becomes unfrozen after being frozen by validator. function frozenUntilOf(address minter) external view returns (uint40); /// @notice Checks if minter was activated after approval by TTG function isActiveMinter(address minter) external view returns (bool); /// @notice Checks if minter was deactivated after removal by TTG function isDeactivatedMinter(address minter) external view returns (bool); /// @notice Checks if minter was frozen by validator function isFrozenMinter(address minter) external view returns (bool); /// @notice Checks if minter was approved by TTG function isMinterApproved(address minter) external view returns (bool); /// @notice Checks if validator was approved by TTG function isValidatorApproved(address validator) external view returns (bool); /// @notice The delay between mint proposal creation and its earliest execution. function mintDelay() external view returns (uint32); /// @notice The time while mint request can still be processed before it is considered expired. function mintTTL() external view returns (uint32); /// @notice The freeze time for minter. function minterFreezeTime() external view returns (uint32); /// @notice The allowed activeOwedM to collateral ratio. function mintRatio() external view returns (uint32); /// @notice The % that defines penalty amount for missed collateral updates or excessive owedM value function penaltyRate() external view returns (uint32); /// @notice The smart contract that defines the minter rate. function rateModel() external view returns (address); /// @notice The interval that defines the required frequency of collateral updates. function updateCollateralInterval() external view returns (uint32); /// @notice The number of signatures required for successful collateral update. function updateCollateralValidatorThreshold() external view returns (uint256); /// @notice Descaler for variables in basis points. Effectively, 100% in basis points. function ONE() external pure returns (uint16); /// @notice Mint ratio cap. 650% in basis points. function MAX_MINT_RATIO() external pure returns (uint32); /// @notice Update collateral interval lower cap in seconds. function MIN_UPDATE_COLLATERAL_INTERVAL() external pure returns (uint32); /// @notice The EIP-712 typehash for the `updateCollateral` method. function UPDATE_COLLATERAL_TYPEHASH() external pure returns (bytes32); }
// 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; /** * @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); }
// 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 { 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 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 { 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; /** * @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 { 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 { 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); }
{ "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 ABI
API[{"inputs":[{"internalType":"address","name":"ttgRegistrar_","type":"address"},{"internalType":"address","name":"mToken_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DeactivatedMinter","type":"error"},{"inputs":[],"name":"DivisionByZero","type":"error"},{"inputs":[{"internalType":"uint240","name":"amount","type":"uint240"},{"internalType":"uint240","name":"maxAmount","type":"uint240"}],"name":"ExceedsMaxRepayAmount","type":"error"},{"inputs":[{"internalType":"uint40","name":"deadline","type":"uint40"}],"name":"ExpiredMintProposal","type":"error"},{"inputs":[],"name":"FrozenMinter","type":"error"},{"inputs":[],"name":"FutureTimestamp","type":"error"},{"inputs":[],"name":"InactiveMinter","type":"error"},{"inputs":[],"name":"InvalidMintProposal","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSignatureLength","type":"error"},{"inputs":[],"name":"InvalidSignatureOrder","type":"error"},{"inputs":[],"name":"InvalidSignatureS","type":"error"},{"inputs":[],"name":"InvalidSignatureV","type":"error"},{"inputs":[],"name":"InvalidUInt112","type":"error"},{"inputs":[],"name":"InvalidUInt240","type":"error"},{"inputs":[],"name":"InvalidUInt48","type":"error"},{"inputs":[],"name":"NotApprovedMinter","type":"error"},{"inputs":[{"internalType":"address","name":"validator","type":"address"}],"name":"NotApprovedValidator","type":"error"},{"inputs":[{"internalType":"uint256","name":"validSignatures","type":"uint256"},{"internalType":"uint256","name":"requiredThreshold","type":"uint256"}],"name":"NotEnoughValidSignatures","type":"error"},{"inputs":[{"internalType":"address","name":"validator","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"lastSignatureTimestamp","type":"uint256"}],"name":"OutdatedValidatorTimestamp","type":"error"},{"inputs":[],"name":"OverflowsPrincipalOfTotalOwedM","type":"error"},{"inputs":[{"internalType":"uint40","name":"activeTimestamp","type":"uint40"}],"name":"PendingMintProposal","type":"error"},{"inputs":[{"internalType":"uint240","name":"totalPendingRetrievals","type":"uint240"},{"internalType":"uint240","name":"collateral","type":"uint240"}],"name":"RetrievalsExceedCollateral","type":"error"},{"inputs":[],"name":"SignatureArrayLengthsMismatch","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":[{"internalType":"uint40","name":"newTimestamp","type":"uint40"},{"internalType":"uint40","name":"earliestAllowedTimestamp","type":"uint40"}],"name":"StaleCollateralUpdate","type":"error"},{"inputs":[],"name":"StillApprovedMinter","type":"error"},{"inputs":[{"internalType":"uint256","name":"activeOwedM","type":"uint256"},{"internalType":"uint256","name":"maxAllowedOwedM","type":"uint256"}],"name":"Undercollateralized","type":"error"},{"inputs":[],"name":"ZeroBurnAmount","type":"error"},{"inputs":[],"name":"ZeroMToken","type":"error"},{"inputs":[],"name":"ZeroMintAmount","type":"error"},{"inputs":[],"name":"ZeroMintDestination","type":"error"},{"inputs":[],"name":"ZeroRetrievalAmount","type":"error"},{"inputs":[],"name":"ZeroTTGRegistrar","type":"error"},{"inputs":[],"name":"ZeroTTGVault","type":"error"},{"inputs":[],"name":"ZeroTimestamp","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"},{"indexed":true,"internalType":"address","name":"payer","type":"address"}],"name":"BurnExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint112","name":"principalAmount","type":"uint112"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"},{"indexed":true,"internalType":"address","name":"payer","type":"address"}],"name":"BurnExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"collateral","type":"uint240"},{"indexed":false,"internalType":"uint240","name":"totalResolvedCollateralRetrieval","type":"uint240"},{"indexed":true,"internalType":"bytes32","name":"metadataHash","type":"bytes32"},{"indexed":false,"internalType":"uint40","name":"timestamp","type":"uint40"}],"name":"CollateralUpdated","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":"uint48","name":"mintId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":true,"internalType":"address","name":"canceller","type":"address"}],"name":"MintCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"mintId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint112","name":"principalAmount","type":"uint112"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"}],"name":"MintExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"mintId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"},{"indexed":true,"internalType":"address","name":"destination","type":"address"}],"name":"MintProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"MinterActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"inactiveOwedM","type":"uint240"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"MinterDeactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint40","name":"frozenUntil","type":"uint40"}],"name":"MinterFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint40","name":"missedIntervals","type":"uint40"},{"indexed":false,"internalType":"uint240","name":"penaltyAmount","type":"uint240"}],"name":"MissedIntervalsPenaltyImposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"retrievalId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"amount","type":"uint240"}],"name":"RetrievalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"retrievalId","type":"uint48"},{"indexed":true,"internalType":"address","name":"minter","type":"address"}],"name":"RetrievalResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint240","name":"excessOwedM","type":"uint240"},{"indexed":false,"internalType":"uint40","name":"timeSpan","type":"uint40"},{"indexed":false,"internalType":"uint240","name":"penaltyAmount","type":"uint240"}],"name":"UndercollateralizedPenaltyImposed","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_MINT_RATIO","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_UPDATE_COLLATERAL_INTERVAL","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPDATE_COLLATERAL_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"activateMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"activeOwedMOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"maxPrincipalAmount_","type":"uint256"},{"internalType":"uint256","name":"maxAmount_","type":"uint256"}],"name":"burnM","outputs":[{"internalType":"uint112","name":"principalAmount_","type":"uint112"},{"internalType":"uint240","name":"amount_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"maxAmount_","type":"uint256"}],"name":"burnM","outputs":[{"internalType":"uint112","name":"principalAmount_","type":"uint112"},{"internalType":"uint240","name":"amount_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"mintId_","type":"uint256"}],"name":"cancelMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"collateralExpiryTimestampOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"collateralOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"collateralPenaltyDeadlineOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"collateralUpdateTimestampOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"deactivateMinter","outputs":[{"internalType":"uint240","name":"inactiveOwedM_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields_","type":"bytes1"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"version_","type":"string"},{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"address","name":"verifyingContract_","type":"address"},{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"internalType":"uint256[]","name":"extensions_","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"excessOwedM","outputs":[{"internalType":"uint240","name":"excessOwedM_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"freezeMinter","outputs":[{"internalType":"uint40","name":"frozenUntil_","type":"uint40"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"frozenUntilOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"address","name":"validator_","type":"address"}],"name":"getLastSignatureTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"collateral_","type":"uint256"},{"internalType":"uint256[]","name":"retrievalIds_","type":"uint256[]"},{"internalType":"bytes32","name":"metadataHash_","type":"bytes32"},{"internalType":"uint256","name":"timestamp_","type":"uint256"}],"name":"getUpdateCollateralDigest","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"inactiveOwedMOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"isActiveMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"isDeactivatedMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"isFrozenMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"isMinterApproved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"validator_","type":"address"}],"name":"isValidatorApproved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestIndex","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"latestProposedRetrievalTimestampOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestUpdateTimestamp","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"maxAllowedActiveOwedMOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintDelay","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintId_","type":"uint256"}],"name":"mintM","outputs":[{"internalType":"uint112","name":"principalAmount_","type":"uint112"},{"internalType":"uint240","name":"amount_","type":"uint240"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"mintProposalOf","outputs":[{"internalType":"uint48","name":"mintId_","type":"uint48"},{"internalType":"uint40","name":"createdAt_","type":"uint40"},{"internalType":"address","name":"destination_","type":"address"},{"internalType":"uint240","name":"amount_","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintRatio","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintTTL","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterFreezeTime","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterRate","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"penalizedUntilOf","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyRate","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"},{"internalType":"uint256","name":"retrievalId_","type":"uint256"}],"name":"pendingCollateralRetrievalOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"principalOfActiveOwedMOf","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"principalOfTotalActiveOwedM","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"destination_","type":"address"}],"name":"proposeMint","outputs":[{"internalType":"uint48","name":"mintId_","type":"uint48"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"}],"name":"proposeRetrieval","outputs":[{"internalType":"uint48","name":"retrievalId_","type":"uint48"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rateModel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalActiveOwedM","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalInactiveOwedM","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalOwedM","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter_","type":"address"}],"name":"totalPendingCollateralRetrievalOf","outputs":[{"internalType":"uint240","name":"","type":"uint240"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ttgRegistrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ttgVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateral_","type":"uint256"},{"internalType":"uint256[]","name":"retrievalIds_","type":"uint256[]"},{"internalType":"bytes32","name":"metadataHash_","type":"bytes32"},{"internalType":"address[]","name":"validators_","type":"address[]"},{"internalType":"uint256[]","name":"timestamps_","type":"uint256[]"},{"internalType":"bytes[]","name":"signatures_","type":"bytes[]"}],"name":"updateCollateral","outputs":[{"internalType":"uint40","name":"minTimestamp_","type":"uint40"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateCollateralInterval","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateCollateralValidatorThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateIndex","outputs":[{"internalType":"uint128","name":"index_","type":"uint128"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
61012060405234801562000011575f80fd5b506040516200583638038062005836833981016040819052620000349162000277565b60408051808201909152600d81526c4d696e7465724761746577617960981b60208201525f805464ffffffffff4216600160a01b027fffffffffffffff0000000000ffffffff000000000000000000000000000000009091161764e8d4a510001790556001620000a582826200034b565b5046608052620000b462000154565b60a052506001600160a01b03821660c0819052620000e55760405163b8eb034b60e01b815260040160405180910390fd5b5f620000f183620001f1565b6001600160a01b031660e0819052036200011e576040516331c1de8560e01b815260040160405180910390fd5b6001600160a01b0381166101008190526200014c5760405163b01d5e2b60e01b815260040160405180910390fd5b5050620004b4565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405162000189919062000417565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f816001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200022f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000255919062000491565b92915050565b80516001600160a01b038116811462000272575f80fd5b919050565b5f806040838503121562000289575f80fd5b62000294836200025b565b9150620002a4602084016200025b565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680620002d657607f821691505b602082108103620002f557634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200034657805f5260205f20601f840160051c81016020851015620003225750805b601f840160051c820191505b8181101562000343575f81556001016200032e565b50505b505050565b81516001600160401b03811115620003675762000367620002ad565b6200037f81620003788454620002c1565b84620002fb565b602080601f831160018114620003b5575f84156200039d5750858301515b5f19600386901b1c1916600185901b1785556200040f565b5f85815260208120601f198616915b82811015620003e557888601518255948401946001909101908401620003c4565b50858210156200040357878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f8083546200042681620002c1565b60018281168015620004415760018114620004575762000485565b60ff198416875282151583028701945062000485565b875f526020805f205f5b858110156200047c5781548a82015290840190820162000461565b50505082870194505b50929695505050505050565b5f60208284031215620004a2575f80fd5b620004ad826200025b565b9392505050565b60805160a05160c05160e051610100516152d4620005625f395f81816108d601528181610f34015281816119c601528181611fbf01528181612405015261246901525f818161070101526123b701525f81816107d301528181610b9201528181611344015281816113c3015281816113fd0152818161166701528181611b0501528181611ba701528181612324015281816127f30152612a3c01525f61139b01525f61136b01526152d45ff3fe608060405234801561000f575f80fd5b506004361061037d575f3560e01c8063a1088459116101d4578063c8a7d5bf11610109578063e62aa759116100a9578063f20eb87d11610079578063f20eb87d14610b48578063f5abed3214610b5b578063f7a31df614610b6e578063f962a44b14610b81575f80fd5b8063e62aa75914610a96578063e806250a14610ad3578063eda1599a14610b22578063f00c280c14610b35575f80fd5b8063d4084620116100e4578063d408462014610971578063d69527111461097a578063d6b7494f14610994578063e1ebf1ad1461099c575f80fd5b8063c8a7d5bf146108f8578063c8da88e11461093d578063cbf062f714610950575f80fd5b8063aab1d86f11610174578063b9f412b01161014f578063b9f412b01461085b578063c107634c14610863578063c2ee3a08146108b5578063c3b6f939146108d1575f80fd5b8063aab1d86f1461082d578063af9979c914610840578063b599105c14610853575f80fd5b8063a49c8461116101af578063a49c846114610773578063a59b9a35146107bb578063a6ce63cd146107ce578063a8c01961146107f5575f80fd5b8063a108845914610750578063a178094414610758578063a29b67ce1461076b575f80fd5b80634be1c1cd116102b557806374aaf5e91161025557806384b0196e1161022557806384b0196e146106d95780638fb7faf2146106f45780639675adb0146106fc57806399799bbd14610748575f80fd5b806374aaf5e9146106275780637572840e1461063a5780637ad636761461064d5780637efb685b1461069a575f80fd5b8063578f2aa011610290578063578f2aa0146105a7578063663485d7146105c25780636850a999146105cb57806371f8ffe5146105fd575f80fd5b80634be1c1cd146105595780635130406b1461056c57806353d96f2c1461057f575f80fd5b80633644e51511610320578063433ae061116102fb578063433ae061146104ee57806343dc2cad14610517578063452b9fd81461052a57806346f97d0b14610532575f80fd5b80633644e515146104ad5780633b547ae5146104c35780633f9bcc6c146104e6575f80fd5b806314bc32e81161035b57806314bc32e8146104105780631aefb1071461043a57806326987b601461047c57806334636e8e146104a5575f80fd5b80630ab18476146103815780630b88f09c146103a35780630ec06104146103b8575b5f80fd5b610389610b89565b60405163ffffffff90911681526020015b60405180910390f35b6103b66103b1366004614b56565b610bc0565b005b6103cb6103c6366004614b7e565b610ced565b604080516dffffffffffffffffffffffffffff90931683527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911660208301520161039a565b61042361041e366004614bae565b610fb3565b60405165ffffffffffff909116815260200161039a565b61044d610448366004614bd8565b611222565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b6104846112af565b6040516fffffffffffffffffffffffffffffffff909116815260200161039a565b61038961133b565b6104b5611368565b60405190815260200161039a565b6104d66104d1366004614bd8565b6113bd565b604051901515815260200161039a565b6103896113ee565b6105016104fc366004614c32565b611426565b60405164ffffffffff909116815260200161039a565b6103b6610525366004614bd8565b61154b565b61038961165e565b6104b57f22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e81565b61044d610567366004614bd8565b61168b565b6104b561057a366004614d02565b61170b565b5f546105019074010000000000000000000000000000000000000000900464ffffffffff1681565b5f54610484906fffffffffffffffffffffffffffffffff1681565b610389610e1081565b6105de6105d9366004614bd8565b611725565b6040516dffffffffffffffffffffffffffff909116815260200161039a565b60025461044d907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681565b6104b5610635366004614bd8565b61179f565b610501610648366004614bd8565b611820565b61050161065b366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff1690565b6105016106a8366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090206002015464ffffffffff1690565b6106e161186b565b60405161039a9796959493929190614dd4565b61044d6119a4565b6107237f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b61044d6119c2565b610723611aff565b610501610766366004614bd8565b611b29565b610389611b9b565b610501610781366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090206002015465010000000000900464ffffffffff1690565b6103cb6107c9366004614e94565b611bd3565b6107237f000000000000000000000000000000000000000000000000000000000000000081565b6104d6610803366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090205460ff1690565b61044d61083b366004614bd8565b61202d565b61044d61084e366004614bd8565b6122a1565b6104b561231e565b610484612348565b610501610871366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546f01000000000000000000000000000000900464ffffffffff1690565b6108be61271081565b60405161ffff909116815260200161039a565b6107237f000000000000000000000000000000000000000000000000000000000000000081565b6104b5610906366004614eab565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260086020908152604080832093909416825291909152205490565b61042361094b366004614e94565b6124f5565b5f54700100000000000000000000000000000000900463ffffffff16610389565b61038961fde881565b6003546105de906dffffffffffffffffffffffffffff1681565b6103896127ea565b610a2c6109aa366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff9081165f908152600560205260409020805460019091015465ffffffffffff82169364ffffffffff6601000000000000840416936b010000000000000000000000909304909216917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911690565b6040805165ffffffffffff909516855264ffffffffff909316602085015273ffffffffffffffffffffffffffffffffffffffff909116918301919091527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16606082015260800161039a565b6104d6610aa4366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f90815260046020526040902054610100900460ff1690565b6104d6610ae1366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff16421090565b61044d610b30366004614b56565b612817565b6103cb610b43366004614b56565b6128bc565b610501610b56366004614bd8565b6128f4565b61044d610b69366004614bd8565b6129b8565b6104d6610b7c366004614bd8565b612a36565b61044d612a61565b5f610bbb610bb67f0000000000000000000000000000000000000000000000000000000000000000612a94565b612abf565b905090565b610bc933612ace565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526005602052604090205465ffffffffffff168181141580610c0b575065ffffffffffff8116155b15610c42576040517fd148789900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f8181526005602052604080822080547fff0000000000000000000000000000000000000000000000000000000000000016815560010180547fffff0000000000000000000000000000000000000000000000000000000000001690555133929165ffffffffffff8516917f84fab121b74a9cdfebabf1215a1abbe5fe44ba6c1920780c593aa5102a4062369190a4505050565b5f80831580610cfa575082155b15610d31576040517f97b9d4c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f908152600460205260409020805460ff1680158015610d6f57508154610100900460ff16155b15610da6576040517f7bcd1d8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8015610e5d57610db587612b28565b610dd087610dc288612ccd565b610dcb88612d1a565b612d73565b604080516dffffffffffffffffffffffffffff841681527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83166020820152929650909450339173ffffffffffffffffffffffffffffffffffffffff8a16917f48f6e7b6e5f16208a0eab9aa837aca65cc7ec94c304ed11ed98c2efc418ba50e910160405180910390a3610ee0565b610e6f87610e6a87612d1a565b612efb565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168152909350339073ffffffffffffffffffffffffffffffffffffffff8916907f1391bf1af04eb2e8c6bd62f9c85f621584bc4ed7d2a6def75a0a0835756415429060200160405180910390a35b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841660248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639dc29fac906044015f604051808303815f87803b158015610f8a575f80fd5b505af1158015610f9c573d5f803e3d5ffd5b50505050610fa8612348565b505050935093915050565b5f33610fbe81612fa2565b610fc733613000565b835f03611000576040517f215b990c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff831661104d576040517f785187dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61105785612d1a565b90506110633382613076565b600380547fffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffff81166e0100000000000000000000000000009182900465ffffffffffff908116600190810180831694850293909317909455604080516080810182528481524264ffffffffff908116602080840191825273ffffffffffffffffffffffffffffffffffffffff8d81168587018181527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8d811660608901818152335f818152600589528c90209a518b54995195519d167fffffffffffffffffffffffffffffffffffffffffff00000000000000000000009099169890981766010000000000009490991693909302979097177fff0000000000000000000000000000000000000000ffffffffffffffffffffff166b0100000000000000000000009a9094169990990292909217865590519490990180547fffff00000000000000000000000000000000000000000000000000000000000016949096169390931790945591519182529297509092917fc9b63ed98dd6e3f2536b8c9cd1668dd153c8a9207579556cb74ce9168b9c21d8910160405180910390a4505092915050565b5f61122c82611820565b64ffffffffff16421061124057505f919050565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600181015481547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9182169162010000909104168082106112a757505f949350505050565b039392505050565b5f8054610bbb90611322906fffffffffffffffffffffffffffffffff81169061131d906112f590700100000000000000000000000000000000900463ffffffff16613144565b5f5474010000000000000000000000000000000000000000900464ffffffffff16420361315b565b61317d565b71ffffffffffffffffffffffffffffffffffff166131b1565b5f610bbb610bb67f00000000000000000000000000000000000000000000000000000000000000006131cc565b5f7f0000000000000000000000000000000000000000000000000000000000000000461461139857610bbb6131f7565b507f000000000000000000000000000000000000000000000000000000000000000090565b5f6113e87f000000000000000000000000000000000000000000000000000000000000000083613292565b92915050565b5f610bbb61fde8611421610bb67f00000000000000000000000000000000000000000000000000000000000000006132be565b6132e9565b5f3361143181612fa2565b86831415806114405750828514155b15611477576040517fa68dc7d400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61148a338d8d8d8d8d8d8d8d8d8d61330a565b91505f6114968d612d1a565b90505f6114a4338e8e6134e1565b604080517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85811682528316602082015264ffffffffff87168183015290519192508c9133917f8c7a373ea6d1cedfcb77f0e5520921cc5d5a1a16b960c0c13c0f96b8dc24caa8919081900360600190a361151c33612b28565b611526338561368c565b6115313383866138a8565b611539612348565b505050509a9950505050505050505050565b61155481612a36565b61158a576040517f70b34fc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604090208054610100900460ff16156115ef576040517f30b2dfb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178155604051339073ffffffffffffffffffffffffffffffffffffffff8416907f2531adeb71f8681e6f4644f88cb102c71370151986071c92d43a7e82d217462a905f90a35050565b5f610bbb610bb67f00000000000000000000000000000000000000000000000000000000000000006139ea565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166116bd575f6113e8565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600660205260409020546113e8907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613a15565b5f61171a878787878787613a27565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff16611757575f6113e8565b5073ffffffffffffffffffffffffffffffffffffffff165f908152600660205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166117d1575f6113e8565b6127106117dc6113ee565b63ffffffff166117eb84611222565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16028161181957611819614ed3565b0492915050565b5f611829611b9b565b73ffffffffffffffffffffffffffffffffffffffff9092165f9081526004602052604090206002015464ffffffffff1663ffffffff9290921691909101919050565b5f606080828080836001463084806040519080825280602002602001820160405280156118a2578160200160208202803683370190505b507f0f0000000000000000000000000000000000000000000000000000000000000094939291908480546118d590614f00565b80601f016020809104026020016040519081016040528092919081815260200182805461190190614f00565b801561194c5780601f106119235761010080835404028352916020019161194c565b820191905f5260205f20905b81548152906001019060200180831161192f57829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b6003545f90610bbb906dffffffffffffffffffffffffffff16613a15565b5f807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a2d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a519190614f51565b6002546003549192505f917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911690611aa0906dffffffffffffffffffffffffffff16611a9b6112af565b613af8565b611aaa9190614f95565b9050817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161115611afa5703919050565b505090565b5f610bbb7f0000000000000000000000000000000000000000000000000000000000000000613b03565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812081611b56611b9b565b60028301549091505f90611b7e9064ffffffffff808216916501000000000090041684613b34565b9150611b92905063ffffffff831682614fd3565b95945050505050565b5f610bbb611bcb610bb67f0000000000000000000000000000000000000000000000000000000000000000613bbd565b610e10613be8565b5f8033611bdf81612fa2565b611be833613000565b335f908152600560205260409020805460018201547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16935065ffffffffffff81169064ffffffffff6601000000000000820416906b010000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16878314611c99576040517fd148789900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611ca261133b565b63ffffffff16830190508064ffffffffff16421015611cfb576040517fdddc549900000000000000000000000000000000000000000000000000000000815264ffffffffff821660048201526024015b60405180910390fd5b5f611d04610b89565b63ffffffff16820190508064ffffffffff16421115611d58576040517fa170e12000000000000000000000000000000000000000000000000000000000815264ffffffffff82166004820152602401611cf2565b5050611d643387613076565b335f90815260056020526040902080547fff0000000000000000000000000000000000000000000000000000000000000016815560010180547fffff000000000000000000000000000000000000000000000000000000000000169055611dca86613c02565b600354604080516dffffffffffffffffffffffffffff80851682527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8b166020830152939a509290911691339165ffffffffffff8716917fc7f1a91b0a382c263a18fea7b2908a6fcd90ebf2a9fde4bdf483b8ecceff1311910160405180910390a36002546dffffffffffffffffffffffffffff828116818b160191611e8d907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613c02565b6dffffffffffffffffffffffffffff16820110611ed6576040517f1123990900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff928316179055335f908152600660205260409081902080547fffff0000000000000000000000000000000000000000000000000000000000008116938c167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918216018116939093179055517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff848116600483015291891660248201527f000000000000000000000000000000000000000000000000000000000000000091909116906340c10f19906044015f604051808303815f87803b158015612003575f80fd5b505af1158015612015573d5f803e3d5ffd5b50505050612021612348565b50505050505050915091565b5f8161203881612fa2565b61204183612a36565b15612078576040517fe2a4b46500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61208183612b28565b5f61208b84611725565b905061209681613a15565b600380547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081166dffffffffffffffffffffffffffff918216859003909116179055600280547fffff00000000000000000000000000000000000000000000000000000000000081167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91821684018216179091556040519082168152909350339073ffffffffffffffffffffffffffffffffffffffff8616907f441d4e9d05bbbeb9019e911d76bdd0bf9389b0755175567d4e59607be5348d9b9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff84165f908152600460209081526040808320838155600180820180547fffff0000000000000000000000000000000000000000000000000000000000009081169091556002830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556005855283862080547fff0000000000000000000000000000000000000000000000000000000000000016815590910180548216905581547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010017909155600690925290912080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86169216919091179055612299612348565b505050919050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166123175773ffffffffffffffffffffffffffffffffffffffff82165f908152600660205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166113e8565b5f92915050565b5f610bbb7f0000000000000000000000000000000000000000000000000000000000000000613c14565b5f806123526119c2565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81161561245d576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660048301527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660248301527f000000000000000000000000000000000000000000000000000000000000000016906340c10f19906044015f604051808303815f87803b158015612446575f80fd5b505af1158015612458573d5f803e3d5ffd5b505050505b612465613c3f565b91507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b9f412b06040518163ffffffff1660e01b81526004016020604051808303815f875af11580156124d1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611afa9190614ff1565b5f3361250081612fa2565b825f03612539576040517faa58a19400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff8116740100000000000000000000000000000000000000009182900465ffffffffffff90811660010190811690920217909155335f9081526004602052604081208054929450916201000090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16906125d586612d1a565b60018401549091505f9061260a9083907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16614f95565b9050807dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1610156126b0576040517f10bb3f430000000000000000000000000000000000000000000000000000000081527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316600483015284166024820152604401611cf2565b60028401805464ffffffffff42166f01000000000000000000000000000000027fffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffffff9091161790556001840180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8084167fffff00000000000000000000000000000000000000000000000000000000000092831617909255335f81815260076020908152604080832065ffffffffffff8d16845290915281208054948716949093169390931790915561278191613076565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83168152339065ffffffffffff8816907f83f709bc37ea6de507e30b18311aa7a86c08833fa447f2c76ccc463c8936c4999060200160405180910390a35050505050919050565b5f610bbb610bb67f0000000000000000000000000000000000000000000000000000000000000000613d8f565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260046020526040812054610100900460ff166128b35773ffffffffffffffffffffffffffffffffffffffff83165f9081526007602052604081209061287684613dba565b65ffffffffffff16815260208101919091526040015f20547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166128b5565b5f5b9392505050565b5f806128e9846128d36128ce86612d1a565b613dfb565b6dffffffffffffffffffffffffffff1685610ced565b909590945092505050565b5f6128fe33612ace565b61290661165e565b73ffffffffffffffffffffffffffffffffffffffff83165f8181526004602090815260409182902060020180547fffffffffffffffffffffffffffffffffff0000000000ffffffffffffffffffff166a010000000000000000000063ffffffff96909616420164ffffffffff811696870291909117909155915193845290935090917f1ac7b35bca40be2823e4880f1b2e9ef02fa4c7bd62aed73c2ea2959232a1f9f5910160405180910390a2919050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812054610100900460ff166123175773ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600101547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166113e8565b5f6113e87f000000000000000000000000000000000000000000000000000000000000000083613e0d565b6002545f907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16612a8e6119a4565b01905090565b5f6113e8827f6d696e745f74746c000000000000000000000000000000000000000000000000613e39565b5f6113e88263ffffffff613ec8565b612ad7816113bd565b612b25576040517ff731555300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401611cf2565b50565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812060028101549091908190612b7b9064ffffffffff8082169165010000000000900416612b76611b9b565b613b34565b915091508164ffffffffff165f03612b935750505050565b6002830180547fffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffffff166501000000000064ffffffffff8416021790555f612bd885611725565b9050806dffffffffffffffffffffffffffff165f03612bf8575050505050565b5f612c2386612c1e64ffffffffff87166dffffffffffffffffffffffffffff8616615020565b613ed6565b9050806dffffffffffffffffffffffffffff165f03612c4457505050505050565b8573ffffffffffffffffffffffffffffffffffffffff167f4fb8bb7b0278c9f68d34ce501b521ecd2e3c1bc48fe37eda47db36da6defc7ef85612c8684613a15565b6040805164ffffffffff90931683527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911660208301520160405180910390a2505050505050565b5f6dffffffffffffffffffffffffffff821115612d16576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612d16576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80612d87612d8186611725565b85614036565b9150612d9282613a15565b9050827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161115612e38576040517f37666f1d0000000000000000000000000000000000000000000000000000000081527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316600483015284166024820152604401611cf2565b73ffffffffffffffffffffffffffffffffffffffff949094165f90815260066020526040902080547fffff00000000000000000000000000000000000000000000000000000000000081166dffffffffffffffffffffffffffff8481167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9384160390921617909155600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811690831684900390921691909117905593915050565b5f612f0e612f08846122a1565b83614064565b73ffffffffffffffffffffffffffffffffffffffff9093165f90815260066020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80821686900381167fffff0000000000000000000000000000000000000000000000000000000000009283161790925560028054808416879003909316929091169190911790555090919050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604090205460ff16612b25576040517f7bcd1d8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff16421015612b25576040517f1526e62e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6130808361179f565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106130ac57505050565b5f827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166130d78561168b565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160190508181111561313e576040517f5033ec120000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401611cf2565b50505050565b5f61271063ffffffff831664e8d4a5100002611819565b5f6128b56301e1338067ffffffffffffffff851663ffffffff851602046140b2565b5f64e8d4a510006fffffffffffffffffffffffffffffffff841665ffffffffffff84160264e8d4a50fff015b049392505050565b5f6113e8826fffffffffffffffffffffffffffffffff613ec8565b5f6113e8827f6d696e745f64656c617900000000000000000000000000000000000000000000613e39565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405161322a919061505e565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f6128b5837f76616c696461746f7273000000000000000000000000000000000000000000008461412b565b5f6113e8827f6d696e745f726174696f00000000000000000000000000000000000000000000613e39565b5f8163ffffffff168363ffffffff161061330357816128b5565b5090919050565b425f805b83811015613480575f811180156133a0575088886001830381811061333557613335615133565b905060200201602081019061334a9190614bd8565b73ffffffffffffffffffffffffffffffffffffffff1689898381811061337257613372615133565b90506020020160208101906133879190614bd8565b73ffffffffffffffffffffffffffffffffffffffff1611155b156133d7576040517f139f3c9200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134488e8e8e8e8e8e8e888181106133f1576133f1615133565b90506020020160208101906134069190614bd8565b8d8d8981811061341857613418615133565b905060200201358c8c8a81811061343157613431615133565b90506020028101906134439190615160565b6141cb565b156134785761346f8388888481811061346357613463615133565b9050602002013561438d565b92508160010191505b60010161330e565b505f61348a61231e565b9050808210156134d0576040517f74e8306f0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401611cf2565b50509b9a5050505050505050505050565b5f805b8281101561360d575f61350e85858481811061350257613502615133565b90506020020135613dba565b73ffffffffffffffffffffffffffffffffffffffff87165f90815260076020908152604080832065ffffffffffff851684529091528120549192507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091169081900361357a575050613605565b73ffffffffffffffffffffffffffffffffffffffff87165f81815260076020908152604080832065ffffffffffff8716808552925280832080547fffff000000000000000000000000000000000000000000000000000000000000169055519684019690917f7318b8ec2c2570ee6068ac690bbda62f9d13d38d52e36e23e0885101a5ffab0791a350505b6001016134e4565b5073ffffffffffffffffffffffffffffffffffffffff9093165f90815260046020526040902060010180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808216869003167fffff0000000000000000000000000000000000000000000000000000000000009091161790555090919050565b5f61369683611725565b9050806dffffffffffffffffffffffffffff165f036136b457505050565b5f6136be8461179f565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106136eb5750505050565b5f6136f582613dfb565b9050826dffffffffffffffffffffffffffff16816dffffffffffffffffffffffffffff1610613725575050505050565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600460205260408120600281015490919061376e9064ffffffffff80821691650100000000009004166143a9565b90508064ffffffffff168664ffffffffff161161378e5750505050505050565b8086038386035f6137e48a6137a1611b9b565b63ffffffff168564ffffffffff1685026dffffffffffffffffffffffffffff16816137ce576137ce614ed3565b046dffffffffffffffffffffffffffff16613ed6565b9050806dffffffffffffffffffffffffffff165f036138095750505050505050505050565b8973ffffffffffffffffffffffffffffffffffffffff167fadbe0a005ef4d2903a69fd3053b1c8dfc083430632f549a92a1d3bc648104ae361384a84613a15565b8561385485613a15565b604080517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff948516815264ffffffffff93909316602084015292168183015290519081900360600190a250505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600460205260408120600281015490919061391d906138ff9064ffffffffff808216916f010000000000000000000000000000009004166143a9565b61391661390a611b9b565b63ffffffff164261438d565b42036143a9565b90508064ffffffffff168364ffffffffff1611613977576040517f17ea884500000000000000000000000000000000000000000000000000000000815264ffffffffff808516600483015282166024820152604401611cf2565b5080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909316620100000261ffff9093169290921782556002909101805464ffffffffff9092167fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000090921691909117905550565b5f6113e8827f6d696e7465725f667265657a655f74696d650000000000000000000000000000613e39565b5f6113e882613a226112af565b6143c5565b5f61171a7f22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e5f1b88888888604051602001613a639291906151c1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083019590955273ffffffffffffffffffffffffffffffffffffffff909316928101929092526060820152608081019190915260a0810185905260c0810184905260e001604051602081830303815290604052805190602001206143d0565b5f6128b58383614431565b5f6113e8613b31837f6d696e7465725f726174655f6d6f64656c000000000000000000000000000000613e39565b90565b5f805f613b4186866143a9565b90508564ffffffffff165f03613b5b575f92509050613bb5565b5f613b668242615201565b90508463ffffffff168164ffffffffff161015613b8857505f92509050613bb5565b8463ffffffff168164ffffffffff1681613ba457613ba4614ed3565b0493505063ffffffff841683020190505b935093915050565b5f6113e8827f7570646174655f636f6c6c61746572616c5f696e74657276616c000000000000613e39565b5f8163ffffffff168363ffffffff161161330357816128b5565b5f6113e882613c0f6112af565b614462565b5f6113e8827f7570646174655f636f6c6c61746572616c5f7468726573686f6c640000000000613e39565b5f80613c4961446d565b5f5490915064ffffffffff740100000000000000000000000000000000000000009091041642148015613c9957505f5463ffffffff82811670010000000000000000000000000000000090920416145b15613cb75750505f546fffffffffffffffffffffffffffffffff1690565b613cbf6112af565b5f80546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811770010000000000000000000000000000000063ffffffff8616908102919091177fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004264ffffffffff16021783556040519395509290917f8f9a1730052b867fdeb484b52fbc51e9bb62830781805ac95c382bbf8ea717a291a35090565b5f6113e8827f70656e616c74795f726174650000000000000000000000000000000000000000613e39565b5f65ffffffffffff821115612d16576040517f069c6d4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6113e882613e086112af565b61456d565b5f6128b5837f6d696e74657273000000000000000000000000000000000000000000000000008461412b565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290525f9073ffffffffffffffffffffffffffffffffffffffff841690638eaa6ac090602401602060405180830381865afa158015613ea4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128b59190614f51565b5f81831061330357816128b5565b5f8172ffffffffffffffffffffffffffffffffffffff165f03613efa57505f6113e8565b5f613f036127ea565b90508063ffffffff165f03613f1b575f9150506113e8565b60035461271063ffffffff831672ffffffffffffffffffffffffffffffffffffff86160204906dffffffffffffffffffffffffffff908116820190811115613f7a5750506003546dffffffffffffffffffffffffffff90811681038116905b600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff92831617905573ffffffffffffffffffffffffffffffffffffffff959095165f90815260066020526040902080547fffff00000000000000000000000000000000000000000000000000000000000081169683167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918216011695909517909455509192915050565b5f816dffffffffffffffffffffffffffff16836dffffffffffffffffffffffffffff161061330357816128b5565b5f817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161061330357816128b5565b5f68ffffffffffffffffff821680026123288102642e90edd000820464174876e800830402016c010f6b2be4706a13fc200000000182633b9aca008304669536c708910000018568ffffffffffffffffff1602905080820381830164e8d4a51000028161412157614121614ed3565b0495945050505050565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301525f919085169063d7d1c1c090604401602060405180830381865afa15801561419f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141c3919061521f565b949350505050565b5f835f03614205576040517fda16d76700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4264ffffffffff16841115614246576040517f0ff02cef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff808b165f908152600860209081526040808320938916835292905220548085116142d7576040517f2e7a0bcb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024810186905260448101829052606401611cf2565b6142e0866113bd565b6142ed575f915050614380565b61433a866142ff8d8d8d8d8d8c613a27565b86868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061457892505050565b614347575f915050614380565b505073ffffffffffffffffffffffffffffffffffffffff808a165f90815260086020908152604080832093881683529290522083905560015b9998505050505050505050565b5f8164ffffffffff168364ffffffffff161061330357816128b5565b5f8164ffffffffff168364ffffffffff161161330357816128b5565b5f6128b58383614595565b5f6143d9611368565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff8416026131a9565b5f6128b583836145cd565b5f805f614478611aff565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2c4e722e00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff92909216916144f4919061523e565b5f60405180830381855afa9150503d805f811461452c576040519150601f19603f3d011682016040523d82523d5f602084013e614531565b606091505b509150915081801561454557506020815110155b61454f575f614566565b61456681806020019051810190610bb69190614f51565b9250505090565b5f6128b5838361468e565b5f614584848484614725565b806141c357506141c3848484614774565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff84160264e8d4a50fff016131a9565b5f816fffffffffffffffffffffffffffffffff165f03614619576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128b56fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff861664e8d4a51000028201018161468857614688614ed3565b04612ccd565b5f816fffffffffffffffffffffffffffffffff165f036146da576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128b56fffffffffffffffffffffffffffffffff83167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851664e8d4a51000028161468857614688614ed3565b5f815160400361474e5760208201516040830151614745868684846148bd565b925050506128b5565b5f61475a8585856148e6565b600581111561476b5761476b615259565b14949350505050565b5f805f8573ffffffffffffffffffffffffffffffffffffffff1685856040516024016147a1929190615286565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251614822919061523e565b5f60405180830381855afa9150503d805f811461485a576040519150601f19603f3d011682016040523d82523d5f602084013e61485f565b606091505b509150915081801561487357506020815110155b80156148b3575080517f1626ba7e00000000000000000000000000000000000000000000000000000000906148b19083016020908101908401614f51565b145b9695505050505050565b5f806148cb86868686614920565b60058111156148dc576148dc615259565b1495945050505050565b5f805f6148f3858561495b565b90925090505f82600581111561490b5761490b615259565b1461491657816148b3565b6148b3868261499f565b5f805f61492e8686866149da565b90925090505f82600581111561494657614946615259565b14614951578161171a565b61171a878261499f565b5f8082516041146149715750600290505f614998565b6020830151604084015160608501515f1a919061499087848484614a21565b945094505050505b9250929050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146128b35760056128b5565b5f80601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416614a1387838884614a21565b935093505050935093915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614a565750600390505f614b25565b8460ff16601b14158015614a6e57508460ff16601c14155b15614a7e5750600490505f614b25565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015614ace573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615614b1c575f81614b20565b60015f5b915091505b94509492505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614b51575f80fd5b919050565b5f8060408385031215614b67575f80fd5b614b7083614b2e565b946020939093013593505050565b5f805f60608486031215614b90575f80fd5b614b9984614b2e565b95602085013595506040909401359392505050565b5f8060408385031215614bbf575f80fd5b82359150614bcf60208401614b2e565b90509250929050565b5f60208284031215614be8575f80fd5b6128b582614b2e565b5f8083601f840112614c01575f80fd5b50813567ffffffffffffffff811115614c18575f80fd5b6020830191508360208260051b8501011115614998575f80fd5b5f805f805f805f805f8060c08b8d031215614c4b575f80fd5b8a35995060208b013567ffffffffffffffff80821115614c69575f80fd5b614c758e838f01614bf1565b909b50995060408d0135985060608d0135915080821115614c94575f80fd5b614ca08e838f01614bf1565b909850965060808d0135915080821115614cb8575f80fd5b614cc48e838f01614bf1565b909650945060a08d0135915080821115614cdc575f80fd5b50614ce98d828e01614bf1565b915080935050809150509295989b9194979a5092959850565b5f805f805f8060a08789031215614d17575f80fd5b614d2087614b2e565b955060208701359450604087013567ffffffffffffffff811115614d42575f80fd5b614d4e89828a01614bf1565b979a9699509760608101359660809091013595509350505050565b5f5b83811015614d83578181015183820152602001614d6b565b50505f910152565b5f8151808452614da2816020860160208601614d69565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681525f602060e06020840152614e1060e084018a614d8b565b8381036040850152614e22818a614d8b565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825260208088019350909101905f5b81811015614e8257835183529284019291840191600101614e66565b50909c9b505050505050505050505050565b5f60208284031215614ea4575f80fd5b5035919050565b5f8060408385031215614ebc575f80fd5b614ec583614b2e565b9150614bcf60208401614b2e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b600181811c90821680614f1457607f821691505b602082108103614f4b577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f60208284031215614f61575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818116838216019080821115614fcc57614fcc614f68565b5092915050565b64ffffffffff818116838216019080821115614fcc57614fcc614f68565b5f60208284031215615001575f80fd5b81516fffffffffffffffffffffffffffffffff811681146128b5575f80fd5b72ffffffffffffffffffffffffffffffffffffff82811682821681810283169291811582850482141761505557615055614f68565b50505092915050565b5f8083545f60018260011c9150600183168061507b57607f831692505b602080841082036150b3577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b8180156150c757600181146150fa57615125565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650615125565b5f8a8152602090205f5b8681101561511d5781548b820152908501908301615104565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615193575f80fd5b83018035915067ffffffffffffffff8211156151ad575f80fd5b602001915036819003821315614998575f80fd5b5f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156151ee575f80fd5b8260051b80858437919091019392505050565b64ffffffffff828116828216039080821115614fcc57614fcc614f68565b5f6020828403121561522f575f80fd5b815180151581146128b5575f80fd5b5f825161524f818460208701614d69565b9190910192915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b828152604060208201525f6141c36040830184614d8b56fea26469706673582212202fb093d96f0c6cdac2540fc9fd6bdc6dd1d2f7ff81d14968d63024c63999cf4364736f6c63430008170033000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec0
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061037d575f3560e01c8063a1088459116101d4578063c8a7d5bf11610109578063e62aa759116100a9578063f20eb87d11610079578063f20eb87d14610b48578063f5abed3214610b5b578063f7a31df614610b6e578063f962a44b14610b81575f80fd5b8063e62aa75914610a96578063e806250a14610ad3578063eda1599a14610b22578063f00c280c14610b35575f80fd5b8063d4084620116100e4578063d408462014610971578063d69527111461097a578063d6b7494f14610994578063e1ebf1ad1461099c575f80fd5b8063c8a7d5bf146108f8578063c8da88e11461093d578063cbf062f714610950575f80fd5b8063aab1d86f11610174578063b9f412b01161014f578063b9f412b01461085b578063c107634c14610863578063c2ee3a08146108b5578063c3b6f939146108d1575f80fd5b8063aab1d86f1461082d578063af9979c914610840578063b599105c14610853575f80fd5b8063a49c8461116101af578063a49c846114610773578063a59b9a35146107bb578063a6ce63cd146107ce578063a8c01961146107f5575f80fd5b8063a108845914610750578063a178094414610758578063a29b67ce1461076b575f80fd5b80634be1c1cd116102b557806374aaf5e91161025557806384b0196e1161022557806384b0196e146106d95780638fb7faf2146106f45780639675adb0146106fc57806399799bbd14610748575f80fd5b806374aaf5e9146106275780637572840e1461063a5780637ad636761461064d5780637efb685b1461069a575f80fd5b8063578f2aa011610290578063578f2aa0146105a7578063663485d7146105c25780636850a999146105cb57806371f8ffe5146105fd575f80fd5b80634be1c1cd146105595780635130406b1461056c57806353d96f2c1461057f575f80fd5b80633644e51511610320578063433ae061116102fb578063433ae061146104ee57806343dc2cad14610517578063452b9fd81461052a57806346f97d0b14610532575f80fd5b80633644e515146104ad5780633b547ae5146104c35780633f9bcc6c146104e6575f80fd5b806314bc32e81161035b57806314bc32e8146104105780631aefb1071461043a57806326987b601461047c57806334636e8e146104a5575f80fd5b80630ab18476146103815780630b88f09c146103a35780630ec06104146103b8575b5f80fd5b610389610b89565b60405163ffffffff90911681526020015b60405180910390f35b6103b66103b1366004614b56565b610bc0565b005b6103cb6103c6366004614b7e565b610ced565b604080516dffffffffffffffffffffffffffff90931683527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911660208301520161039a565b61042361041e366004614bae565b610fb3565b60405165ffffffffffff909116815260200161039a565b61044d610448366004614bd8565b611222565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b6104846112af565b6040516fffffffffffffffffffffffffffffffff909116815260200161039a565b61038961133b565b6104b5611368565b60405190815260200161039a565b6104d66104d1366004614bd8565b6113bd565b604051901515815260200161039a565b6103896113ee565b6105016104fc366004614c32565b611426565b60405164ffffffffff909116815260200161039a565b6103b6610525366004614bd8565b61154b565b61038961165e565b6104b57f22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e81565b61044d610567366004614bd8565b61168b565b6104b561057a366004614d02565b61170b565b5f546105019074010000000000000000000000000000000000000000900464ffffffffff1681565b5f54610484906fffffffffffffffffffffffffffffffff1681565b610389610e1081565b6105de6105d9366004614bd8565b611725565b6040516dffffffffffffffffffffffffffff909116815260200161039a565b60025461044d907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1681565b6104b5610635366004614bd8565b61179f565b610501610648366004614bd8565b611820565b61050161065b366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff1690565b6105016106a8366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090206002015464ffffffffff1690565b6106e161186b565b60405161039a9796959493929190614dd4565b61044d6119a4565b6107237f0000000000000000000000003dc71be52d6d687e21fc0d4ffc196f32cacbc26d81565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b61044d6119c2565b610723611aff565b610501610766366004614bd8565b611b29565b610389611b9b565b610501610781366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090206002015465010000000000900464ffffffffff1690565b6103cb6107c9366004614e94565b611bd3565b6107237f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a81565b6104d6610803366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f9081526004602052604090205460ff1690565b61044d61083b366004614bd8565b61202d565b61044d61084e366004614bd8565b6122a1565b6104b561231e565b610484612348565b610501610871366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546f01000000000000000000000000000000900464ffffffffff1690565b6108be61271081565b60405161ffff909116815260200161039a565b6107237f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec081565b6104b5610906366004614eab565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260086020908152604080832093909416825291909152205490565b61042361094b366004614e94565b6124f5565b5f54700100000000000000000000000000000000900463ffffffff16610389565b61038961fde881565b6003546105de906dffffffffffffffffffffffffffff1681565b6103896127ea565b610a2c6109aa366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff9081165f908152600560205260409020805460019091015465ffffffffffff82169364ffffffffff6601000000000000840416936b010000000000000000000000909304909216917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911690565b6040805165ffffffffffff909516855264ffffffffff909316602085015273ffffffffffffffffffffffffffffffffffffffff909116918301919091527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16606082015260800161039a565b6104d6610aa4366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f90815260046020526040902054610100900460ff1690565b6104d6610ae1366004614bd8565b73ffffffffffffffffffffffffffffffffffffffff165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff16421090565b61044d610b30366004614b56565b612817565b6103cb610b43366004614b56565b6128bc565b610501610b56366004614bd8565b6128f4565b61044d610b69366004614bd8565b6129b8565b6104d6610b7c366004614bd8565b612a36565b61044d612a61565b5f610bbb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a612a94565b612abf565b905090565b610bc933612ace565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526005602052604090205465ffffffffffff168181141580610c0b575065ffffffffffff8116155b15610c42576040517fd148789900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83165f8181526005602052604080822080547fff0000000000000000000000000000000000000000000000000000000000000016815560010180547fffff0000000000000000000000000000000000000000000000000000000000001690555133929165ffffffffffff8516917f84fab121b74a9cdfebabf1215a1abbe5fe44ba6c1920780c593aa5102a4062369190a4505050565b5f80831580610cfa575082155b15610d31576040517f97b9d4c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85165f908152600460205260409020805460ff1680158015610d6f57508154610100900460ff16155b15610da6576040517f7bcd1d8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8015610e5d57610db587612b28565b610dd087610dc288612ccd565b610dcb88612d1a565b612d73565b604080516dffffffffffffffffffffffffffff841681527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83166020820152929650909450339173ffffffffffffffffffffffffffffffffffffffff8a16917f48f6e7b6e5f16208a0eab9aa837aca65cc7ec94c304ed11ed98c2efc418ba50e910160405180910390a3610ee0565b610e6f87610e6a87612d1a565b612efb565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168152909350339073ffffffffffffffffffffffffffffffffffffffff8916907f1391bf1af04eb2e8c6bd62f9c85f621584bc4ed7d2a6def75a0a0835756415429060200160405180910390a35b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841660248201527f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec073ffffffffffffffffffffffffffffffffffffffff1690639dc29fac906044015f604051808303815f87803b158015610f8a575f80fd5b505af1158015610f9c573d5f803e3d5ffd5b50505050610fa8612348565b505050935093915050565b5f33610fbe81612fa2565b610fc733613000565b835f03611000576040517f215b990c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff831661104d576040517f785187dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61105785612d1a565b90506110633382613076565b600380547fffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffff81166e0100000000000000000000000000009182900465ffffffffffff908116600190810180831694850293909317909455604080516080810182528481524264ffffffffff908116602080840191825273ffffffffffffffffffffffffffffffffffffffff8d81168587018181527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8d811660608901818152335f818152600589528c90209a518b54995195519d167fffffffffffffffffffffffffffffffffffffffffff00000000000000000000009099169890981766010000000000009490991693909302979097177fff0000000000000000000000000000000000000000ffffffffffffffffffffff166b0100000000000000000000009a9094169990990292909217865590519490990180547fffff00000000000000000000000000000000000000000000000000000000000016949096169390931790945591519182529297509092917fc9b63ed98dd6e3f2536b8c9cd1668dd153c8a9207579556cb74ce9168b9c21d8910160405180910390a4505092915050565b5f61122c82611820565b64ffffffffff16421061124057505f919050565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600181015481547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9182169162010000909104168082106112a757505f949350505050565b039392505050565b5f8054610bbb90611322906fffffffffffffffffffffffffffffffff81169061131d906112f590700100000000000000000000000000000000900463ffffffff16613144565b5f5474010000000000000000000000000000000000000000900464ffffffffff16420361315b565b61317d565b71ffffffffffffffffffffffffffffffffffff166131b1565b5f610bbb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a6131cc565b5f7f0000000000000000000000000000000000000000000000000000000000aa36a7461461139857610bbb6131f7565b507fe56392b5ec2f838ae0b0f0dd0ee7ca621a9431614779af9e8fa59e96b9cb40f090565b5f6113e87f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a83613292565b92915050565b5f610bbb61fde8611421610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a6132be565b6132e9565b5f3361143181612fa2565b86831415806114405750828514155b15611477576040517fa68dc7d400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61148a338d8d8d8d8d8d8d8d8d8d61330a565b91505f6114968d612d1a565b90505f6114a4338e8e6134e1565b604080517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85811682528316602082015264ffffffffff87168183015290519192508c9133917f8c7a373ea6d1cedfcb77f0e5520921cc5d5a1a16b960c0c13c0f96b8dc24caa8919081900360600190a361151c33612b28565b611526338561368c565b6115313383866138a8565b611539612348565b505050509a9950505050505050505050565b61155481612a36565b61158a576040517f70b34fc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604090208054610100900460ff16156115ef576040517f30b2dfb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178155604051339073ffffffffffffffffffffffffffffffffffffffff8416907f2531adeb71f8681e6f4644f88cb102c71370151986071c92d43a7e82d217462a905f90a35050565b5f610bbb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a6139ea565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166116bd575f6113e8565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600660205260409020546113e8907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613a15565b5f61171a878787878787613a27565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff16611757575f6113e8565b5073ffffffffffffffffffffffffffffffffffffffff165f908152600660205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166117d1575f6113e8565b6127106117dc6113ee565b63ffffffff166117eb84611222565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16028161181957611819614ed3565b0492915050565b5f611829611b9b565b73ffffffffffffffffffffffffffffffffffffffff9092165f9081526004602052604090206002015464ffffffffff1663ffffffff9290921691909101919050565b5f606080828080836001463084806040519080825280602002602001820160405280156118a2578160200160208202803683370190505b507f0f0000000000000000000000000000000000000000000000000000000000000094939291908480546118d590614f00565b80601f016020809104026020016040519081016040528092919081815260200182805461190190614f00565b801561194c5780601f106119235761010080835404028352916020019161194c565b820191905f5260205f20905b81548152906001019060200180831161192f57829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b6003545f90610bbb906dffffffffffffffffffffffffffff16613a15565b5f807f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec073ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a2d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a519190614f51565b6002546003549192505f917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911690611aa0906dffffffffffffffffffffffffffff16611a9b6112af565b613af8565b611aaa9190614f95565b9050817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161115611afa5703919050565b505090565b5f610bbb7f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a613b03565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812081611b56611b9b565b60028301549091505f90611b7e9064ffffffffff808216916501000000000090041684613b34565b9150611b92905063ffffffff831682614fd3565b95945050505050565b5f610bbb611bcb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a613bbd565b610e10613be8565b5f8033611bdf81612fa2565b611be833613000565b335f908152600560205260409020805460018201547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16935065ffffffffffff81169064ffffffffff6601000000000000820416906b010000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16878314611c99576040517fd148789900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611ca261133b565b63ffffffff16830190508064ffffffffff16421015611cfb576040517fdddc549900000000000000000000000000000000000000000000000000000000815264ffffffffff821660048201526024015b60405180910390fd5b5f611d04610b89565b63ffffffff16820190508064ffffffffff16421115611d58576040517fa170e12000000000000000000000000000000000000000000000000000000000815264ffffffffff82166004820152602401611cf2565b5050611d643387613076565b335f90815260056020526040902080547fff0000000000000000000000000000000000000000000000000000000000000016815560010180547fffff000000000000000000000000000000000000000000000000000000000000169055611dca86613c02565b600354604080516dffffffffffffffffffffffffffff80851682527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8b166020830152939a509290911691339165ffffffffffff8716917fc7f1a91b0a382c263a18fea7b2908a6fcd90ebf2a9fde4bdf483b8ecceff1311910160405180910390a36002546dffffffffffffffffffffffffffff828116818b160191611e8d907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16613c02565b6dffffffffffffffffffffffffffff16820110611ed6576040517f1123990900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff928316179055335f908152600660205260409081902080547fffff0000000000000000000000000000000000000000000000000000000000008116938c167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918216018116939093179055517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff848116600483015291891660248201527f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec091909116906340c10f19906044015f604051808303815f87803b158015612003575f80fd5b505af1158015612015573d5f803e3d5ffd5b50505050612021612348565b50505050505050915091565b5f8161203881612fa2565b61204183612a36565b15612078576040517fe2a4b46500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61208183612b28565b5f61208b84611725565b905061209681613a15565b600380547fffffffffffffffffffffffffffffffffffff000000000000000000000000000081166dffffffffffffffffffffffffffff918216859003909116179055600280547fffff00000000000000000000000000000000000000000000000000000000000081167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91821684018216179091556040519082168152909350339073ffffffffffffffffffffffffffffffffffffffff8616907f441d4e9d05bbbeb9019e911d76bdd0bf9389b0755175567d4e59607be5348d9b9060200160405180910390a373ffffffffffffffffffffffffffffffffffffffff84165f908152600460209081526040808320838155600180820180547fffff0000000000000000000000000000000000000000000000000000000000009081169091556002830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556005855283862080547fff0000000000000000000000000000000000000000000000000000000000000016815590910180548216905581547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010017909155600690925290912080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86169216919091179055612299612348565b505050919050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604081205460ff166123175773ffffffffffffffffffffffffffffffffffffffff82165f908152600660205260409020547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166113e8565b5f92915050565b5f610bbb7f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a613c14565b5f806123526119c2565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81161561245d576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003dc71be52d6d687e21fc0d4ffc196f32cacbc26d811660048301527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831660248301527f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec016906340c10f19906044015f604051808303815f87803b158015612446575f80fd5b505af1158015612458573d5f803e3d5ffd5b505050505b612465613c3f565b91507f0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec073ffffffffffffffffffffffffffffffffffffffff1663b9f412b06040518163ffffffff1660e01b81526004016020604051808303815f875af11580156124d1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611afa9190614ff1565b5f3361250081612fa2565b825f03612539576040517faa58a19400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff8116740100000000000000000000000000000000000000009182900465ffffffffffff90811660010190811690920217909155335f9081526004602052604081208054929450916201000090047dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16906125d586612d1a565b60018401549091505f9061260a9083907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16614f95565b9050807dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1610156126b0576040517f10bb3f430000000000000000000000000000000000000000000000000000000081527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316600483015284166024820152604401611cf2565b60028401805464ffffffffff42166f01000000000000000000000000000000027fffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffffff9091161790556001840180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8084167fffff00000000000000000000000000000000000000000000000000000000000092831617909255335f81815260076020908152604080832065ffffffffffff8d16845290915281208054948716949093169390931790915561278191613076565b6040517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83168152339065ffffffffffff8816907f83f709bc37ea6de507e30b18311aa7a86c08833fa447f2c76ccc463c8936c4999060200160405180910390a35050505050919050565b5f610bbb610bb67f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a613d8f565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260046020526040812054610100900460ff166128b35773ffffffffffffffffffffffffffffffffffffffff83165f9081526007602052604081209061287684613dba565b65ffffffffffff16815260208101919091526040015f20547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166128b5565b5f5b9392505050565b5f806128e9846128d36128ce86612d1a565b613dfb565b6dffffffffffffffffffffffffffff1685610ced565b909590945092505050565b5f6128fe33612ace565b61290661165e565b73ffffffffffffffffffffffffffffffffffffffff83165f8181526004602090815260409182902060020180547fffffffffffffffffffffffffffffffffff0000000000ffffffffffffffffffff166a010000000000000000000063ffffffff96909616420164ffffffffff811696870291909117909155915193845290935090917f1ac7b35bca40be2823e4880f1b2e9ef02fa4c7bd62aed73c2ea2959232a1f9f5910160405180910390a2919050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812054610100900460ff166123175773ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600101547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166113e8565b5f6113e87f000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a83613e0d565b6002545f907dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16612a8e6119a4565b01905090565b5f6113e8827f6d696e745f74746c000000000000000000000000000000000000000000000000613e39565b5f6113e88263ffffffff613ec8565b612ad7816113bd565b612b25576040517ff731555300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401611cf2565b50565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040812060028101549091908190612b7b9064ffffffffff8082169165010000000000900416612b76611b9b565b613b34565b915091508164ffffffffff165f03612b935750505050565b6002830180547fffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffffff166501000000000064ffffffffff8416021790555f612bd885611725565b9050806dffffffffffffffffffffffffffff165f03612bf8575050505050565b5f612c2386612c1e64ffffffffff87166dffffffffffffffffffffffffffff8616615020565b613ed6565b9050806dffffffffffffffffffffffffffff165f03612c4457505050505050565b8573ffffffffffffffffffffffffffffffffffffffff167f4fb8bb7b0278c9f68d34ce501b521ecd2e3c1bc48fe37eda47db36da6defc7ef85612c8684613a15565b6040805164ffffffffff90931683527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911660208301520160405180910390a2505050505050565b5f6dffffffffffffffffffffffffffff821115612d16576040517fca21dbd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612d16576040517f2a49c10d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80612d87612d8186611725565b85614036565b9150612d9282613a15565b9050827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161115612e38576040517f37666f1d0000000000000000000000000000000000000000000000000000000081527dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316600483015284166024820152604401611cf2565b73ffffffffffffffffffffffffffffffffffffffff949094165f90815260066020526040902080547fffff00000000000000000000000000000000000000000000000000000000000081166dffffffffffffffffffffffffffff8481167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9384160390921617909155600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000811690831684900390921691909117905593915050565b5f612f0e612f08846122a1565b83614064565b73ffffffffffffffffffffffffffffffffffffffff9093165f90815260066020526040902080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80821686900381167fffff0000000000000000000000000000000000000000000000000000000000009283161790925560028054808416879003909316929091169190911790555090919050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526004602052604090205460ff16612b25576040517f7bcd1d8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f908152600460205260409020600201546a0100000000000000000000900464ffffffffff16421015612b25576040517f1526e62e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6130808361179f565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106130ac57505050565b5f827dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166130d78561168b565b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff160190508181111561313e576040517f5033ec120000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401611cf2565b50505050565b5f61271063ffffffff831664e8d4a5100002611819565b5f6128b56301e1338067ffffffffffffffff851663ffffffff851602046140b2565b5f64e8d4a510006fffffffffffffffffffffffffffffffff841665ffffffffffff84160264e8d4a50fff015b049392505050565b5f6113e8826fffffffffffffffffffffffffffffffff613ec8565b5f6113e8827f6d696e745f64656c617900000000000000000000000000000000000000000000613e39565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b600160405161322a919061505e565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f6128b5837f76616c696461746f7273000000000000000000000000000000000000000000008461412b565b5f6113e8827f6d696e745f726174696f00000000000000000000000000000000000000000000613e39565b5f8163ffffffff168363ffffffff161061330357816128b5565b5090919050565b425f805b83811015613480575f811180156133a0575088886001830381811061333557613335615133565b905060200201602081019061334a9190614bd8565b73ffffffffffffffffffffffffffffffffffffffff1689898381811061337257613372615133565b90506020020160208101906133879190614bd8565b73ffffffffffffffffffffffffffffffffffffffff1611155b156133d7576040517f139f3c9200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6134488e8e8e8e8e8e8e888181106133f1576133f1615133565b90506020020160208101906134069190614bd8565b8d8d8981811061341857613418615133565b905060200201358c8c8a81811061343157613431615133565b90506020028101906134439190615160565b6141cb565b156134785761346f8388888481811061346357613463615133565b9050602002013561438d565b92508160010191505b60010161330e565b505f61348a61231e565b9050808210156134d0576040517f74e8306f0000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401611cf2565b50509b9a5050505050505050505050565b5f805b8281101561360d575f61350e85858481811061350257613502615133565b90506020020135613dba565b73ffffffffffffffffffffffffffffffffffffffff87165f90815260076020908152604080832065ffffffffffff851684529091528120549192507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091169081900361357a575050613605565b73ffffffffffffffffffffffffffffffffffffffff87165f81815260076020908152604080832065ffffffffffff8716808552925280832080547fffff000000000000000000000000000000000000000000000000000000000000169055519684019690917f7318b8ec2c2570ee6068ac690bbda62f9d13d38d52e36e23e0885101a5ffab0791a350505b6001016134e4565b5073ffffffffffffffffffffffffffffffffffffffff9093165f90815260046020526040902060010180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808216869003167fffff0000000000000000000000000000000000000000000000000000000000009091161790555090919050565b5f61369683611725565b9050806dffffffffffffffffffffffffffff165f036136b457505050565b5f6136be8461179f565b90507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106136eb5750505050565b5f6136f582613dfb565b9050826dffffffffffffffffffffffffffff16816dffffffffffffffffffffffffffff1610613725575050505050565b73ffffffffffffffffffffffffffffffffffffffff85165f908152600460205260408120600281015490919061376e9064ffffffffff80821691650100000000009004166143a9565b90508064ffffffffff168664ffffffffff161161378e5750505050505050565b8086038386035f6137e48a6137a1611b9b565b63ffffffff168564ffffffffff1685026dffffffffffffffffffffffffffff16816137ce576137ce614ed3565b046dffffffffffffffffffffffffffff16613ed6565b9050806dffffffffffffffffffffffffffff165f036138095750505050505050505050565b8973ffffffffffffffffffffffffffffffffffffffff167fadbe0a005ef4d2903a69fd3053b1c8dfc083430632f549a92a1d3bc648104ae361384a84613a15565b8561385485613a15565b604080517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff948516815264ffffffffff93909316602084015292168183015290519081900360600190a250505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600460205260408120600281015490919061391d906138ff9064ffffffffff808216916f010000000000000000000000000000009004166143a9565b61391661390a611b9b565b63ffffffff164261438d565b42036143a9565b90508064ffffffffff168364ffffffffff1611613977576040517f17ea884500000000000000000000000000000000000000000000000000000000815264ffffffffff808516600483015282166024820152604401611cf2565b5080547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909316620100000261ffff9093169290921782556002909101805464ffffffffff9092167fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000090921691909117905550565b5f6113e8827f6d696e7465725f667265657a655f74696d650000000000000000000000000000613e39565b5f6113e882613a226112af565b6143c5565b5f61171a7f22b57ca54bd15c6234b29e87aa1d76a0841b6e65e63d7acacef989de0bc3ff9e5f1b88888888604051602001613a639291906151c1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083019590955273ffffffffffffffffffffffffffffffffffffffff909316928101929092526060820152608081019190915260a0810185905260c0810184905260e001604051602081830303815290604052805190602001206143d0565b5f6128b58383614431565b5f6113e8613b31837f6d696e7465725f726174655f6d6f64656c000000000000000000000000000000613e39565b90565b5f805f613b4186866143a9565b90508564ffffffffff165f03613b5b575f92509050613bb5565b5f613b668242615201565b90508463ffffffff168164ffffffffff161015613b8857505f92509050613bb5565b8463ffffffff168164ffffffffff1681613ba457613ba4614ed3565b0493505063ffffffff841683020190505b935093915050565b5f6113e8827f7570646174655f636f6c6c61746572616c5f696e74657276616c000000000000613e39565b5f8163ffffffff168363ffffffff161161330357816128b5565b5f6113e882613c0f6112af565b614462565b5f6113e8827f7570646174655f636f6c6c61746572616c5f7468726573686f6c640000000000613e39565b5f80613c4961446d565b5f5490915064ffffffffff740100000000000000000000000000000000000000009091041642148015613c9957505f5463ffffffff82811670010000000000000000000000000000000090920416145b15613cb75750505f546fffffffffffffffffffffffffffffffff1690565b613cbf6112af565b5f80546fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811770010000000000000000000000000000000063ffffffff8616908102919091177fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004264ffffffffff16021783556040519395509290917f8f9a1730052b867fdeb484b52fbc51e9bb62830781805ac95c382bbf8ea717a291a35090565b5f6113e8827f70656e616c74795f726174650000000000000000000000000000000000000000613e39565b5f65ffffffffffff821115612d16576040517f069c6d4d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6113e882613e086112af565b61456d565b5f6128b5837f6d696e74657273000000000000000000000000000000000000000000000000008461412b565b6040517f8eaa6ac0000000000000000000000000000000000000000000000000000000008152600481018290525f9073ffffffffffffffffffffffffffffffffffffffff841690638eaa6ac090602401602060405180830381865afa158015613ea4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128b59190614f51565b5f81831061330357816128b5565b5f8172ffffffffffffffffffffffffffffffffffffff165f03613efa57505f6113e8565b5f613f036127ea565b90508063ffffffff165f03613f1b575f9150506113e8565b60035461271063ffffffff831672ffffffffffffffffffffffffffffffffffffff86160204906dffffffffffffffffffffffffffff908116820190811115613f7a5750506003546dffffffffffffffffffffffffffff90811681038116905b600380547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166dffffffffffffffffffffffffffff92831617905573ffffffffffffffffffffffffffffffffffffffff959095165f90815260066020526040902080547fffff00000000000000000000000000000000000000000000000000000000000081169683167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918216011695909517909455509192915050565b5f816dffffffffffffffffffffffffffff16836dffffffffffffffffffffffffffff161061330357816128b5565b5f817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff161061330357816128b5565b5f68ffffffffffffffffff821680026123288102642e90edd000820464174876e800830402016c010f6b2be4706a13fc200000000182633b9aca008304669536c708910000018568ffffffffffffffffff1602905080820381830164e8d4a51000028161412157614121614ed3565b0495945050505050565b6040517fd7d1c1c00000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301525f919085169063d7d1c1c090604401602060405180830381865afa15801561419f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141c3919061521f565b949350505050565b5f835f03614205576040517fda16d76700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4264ffffffffff16841115614246576040517f0ff02cef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff808b165f908152600860209081526040808320938916835292905220548085116142d7576040517f2e7a0bcb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024810186905260448101829052606401611cf2565b6142e0866113bd565b6142ed575f915050614380565b61433a866142ff8d8d8d8d8d8c613a27565b86868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061457892505050565b614347575f915050614380565b505073ffffffffffffffffffffffffffffffffffffffff808a165f90815260086020908152604080832093881683529290522083905560015b9998505050505050505050565b5f8164ffffffffff168364ffffffffff161061330357816128b5565b5f8164ffffffffff168364ffffffffff161161330357816128b5565b5f6128b58383614595565b5f6143d9611368565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff8416026131a9565b5f6128b583836145cd565b5f805f614478611aff565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2c4e722e00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff92909216916144f4919061523e565b5f60405180830381855afa9150503d805f811461452c576040519150601f19603f3d011682016040523d82523d5f602084013e614531565b606091505b509150915081801561454557506020815110155b61454f575f614566565b61456681806020019051810190610bb69190614f51565b9250505090565b5f6128b5838361468e565b5f614584848484614725565b806141c357506141c3848484614774565b5f64e8d4a510006dffffffffffffffffffffffffffff84166fffffffffffffffffffffffffffffffff84160264e8d4a50fff016131a9565b5f816fffffffffffffffffffffffffffffffff165f03614619576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128b56fffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff861664e8d4a51000028201018161468857614688614ed3565b04612ccd565b5f816fffffffffffffffffffffffffffffffff165f036146da576040517f23d359a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128b56fffffffffffffffffffffffffffffffff83167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff851664e8d4a51000028161468857614688614ed3565b5f815160400361474e5760208201516040830151614745868684846148bd565b925050506128b5565b5f61475a8585856148e6565b600581111561476b5761476b615259565b14949350505050565b5f805f8573ffffffffffffffffffffffffffffffffffffffff1685856040516024016147a1929190615286565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251614822919061523e565b5f60405180830381855afa9150503d805f811461485a576040519150601f19603f3d011682016040523d82523d5f602084013e61485f565b606091505b509150915081801561487357506020815110155b80156148b3575080517f1626ba7e00000000000000000000000000000000000000000000000000000000906148b19083016020908101908401614f51565b145b9695505050505050565b5f806148cb86868686614920565b60058111156148dc576148dc615259565b1495945050505050565b5f805f6148f3858561495b565b90925090505f82600581111561490b5761490b615259565b1461491657816148b3565b6148b3868261499f565b5f805f61492e8686866149da565b90925090505f82600581111561494657614946615259565b14614951578161171a565b61171a878261499f565b5f8082516041146149715750600290505f614998565b6020830151604084015160608501515f1a919061499087848484614a21565b945094505050505b9250929050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146128b35760056128b5565b5f80601b60ff84901c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416614a1387838884614a21565b935093505050935093915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614a565750600390505f614b25565b8460ff16601b14158015614a6e57508460ff16601c14155b15614a7e5750600490505f614b25565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa158015614ace573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615614b1c575f81614b20565b60015f5b915091505b94509492505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614b51575f80fd5b919050565b5f8060408385031215614b67575f80fd5b614b7083614b2e565b946020939093013593505050565b5f805f60608486031215614b90575f80fd5b614b9984614b2e565b95602085013595506040909401359392505050565b5f8060408385031215614bbf575f80fd5b82359150614bcf60208401614b2e565b90509250929050565b5f60208284031215614be8575f80fd5b6128b582614b2e565b5f8083601f840112614c01575f80fd5b50813567ffffffffffffffff811115614c18575f80fd5b6020830191508360208260051b8501011115614998575f80fd5b5f805f805f805f805f8060c08b8d031215614c4b575f80fd5b8a35995060208b013567ffffffffffffffff80821115614c69575f80fd5b614c758e838f01614bf1565b909b50995060408d0135985060608d0135915080821115614c94575f80fd5b614ca08e838f01614bf1565b909850965060808d0135915080821115614cb8575f80fd5b614cc48e838f01614bf1565b909650945060a08d0135915080821115614cdc575f80fd5b50614ce98d828e01614bf1565b915080935050809150509295989b9194979a5092959850565b5f805f805f8060a08789031215614d17575f80fd5b614d2087614b2e565b955060208701359450604087013567ffffffffffffffff811115614d42575f80fd5b614d4e89828a01614bf1565b979a9699509760608101359660809091013595509350505050565b5f5b83811015614d83578181015183820152602001614d6b565b50505f910152565b5f8151808452614da2816020860160208601614d69565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681525f602060e06020840152614e1060e084018a614d8b565b8381036040850152614e22818a614d8b565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c0860152855180825260208088019350909101905f5b81811015614e8257835183529284019291840191600101614e66565b50909c9b505050505050505050505050565b5f60208284031215614ea4575f80fd5b5035919050565b5f8060408385031215614ebc575f80fd5b614ec583614b2e565b9150614bcf60208401614b2e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b600181811c90821680614f1457607f821691505b602082108103614f4b577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f60208284031215614f61575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818116838216019080821115614fcc57614fcc614f68565b5092915050565b64ffffffffff818116838216019080821115614fcc57614fcc614f68565b5f60208284031215615001575f80fd5b81516fffffffffffffffffffffffffffffffff811681146128b5575f80fd5b72ffffffffffffffffffffffffffffffffffffff82811682821681810283169291811582850482141761505557615055614f68565b50505092915050565b5f8083545f60018260011c9150600183168061507b57607f831692505b602080841082036150b3577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b8180156150c757600181146150fa57615125565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650615125565b5f8a8152602090205f5b8681101561511d5781548b820152908501908301615104565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615193575f80fd5b83018035915067ffffffffffffffff8211156151ad575f80fd5b602001915036819003821315614998575f80fd5b5f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156151ee575f80fd5b8260051b80858437919091019392505050565b64ffffffffff828116828216039080821115614fcc57614fcc614f68565b5f6020828403121561522f575f80fd5b815180151581146128b5575f80fd5b5f825161524f818460208701614d69565b9190910192915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b828152604060208201525f6141c36040830184614d8b56fea26469706673582212202fb093d96f0c6cdac2540fc9fd6bdc6dd1d2f7ff81d14968d63024c63999cf4364736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec0
-----Decoded View---------------
Arg [0] : ttgRegistrar_ (address): 0x975Bf5f212367D09CB7f69D3dc4BA8C9B440aD3A
Arg [1] : mToken_ (address): 0x0c941AD94Ca4A52EDAeAbF203b61bdd1807CeEC0
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000975bf5f212367d09cb7f69d3dc4ba8c9b440ad3a
Arg [1] : 0000000000000000000000000c941ad94ca4a52edaeabf203b61bdd1807ceec0
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.