ETH Price: $3,420.88 (+3.21%)

Contract

0x686e8500B6bE8812EB198aAbbbFA14C95c03fC88
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Create Withdrawa...212776352024-11-27 7:36:112 hrs ago1732692971IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.001245912.83044318
Create Withdrawa...212776302024-11-27 7:35:112 hrs ago1732692911IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0011833312.1861742
Deposit212741942024-11-26 20:04:2314 hrs ago1732651463IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0013640110.73327069
Redeem212734282024-11-26 17:30:3516 hrs ago1732642235IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0017778313.42685986
Create Withdrawa...212733352024-11-26 17:11:5917 hrs ago1732641119IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0015109615.56011451
Redeem212726522024-11-26 14:55:1119 hrs ago1732632911IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0034068925.72772613
Execute Withdraw...212723942024-11-26 14:03:2320 hrs ago1732629803IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0026447716.56370657
Create Withdrawa...212596002024-11-24 19:09:352 days ago1732475375IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0010420910.73167183
Create Withdrawa...212506242024-11-23 13:04:593 days ago1732367099IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0013331313.7287632
Execute Withdraw...212446682024-11-22 17:09:354 days ago1732295375IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0018473213.44425353
Create Withdrawa...212429542024-11-22 11:24:354 days ago1732274675IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.000905899.32906407
Redeem212422842024-11-22 9:09:595 days ago1732266599IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0013676610.32907279
Redeem212400972024-11-22 1:50:115 days ago1732240211IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0012126910.51913877
Execute Withdraw...212357362024-11-21 11:14:475 days ago1732187687IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0020132114.65160404
Create Withdrawa...212332012024-11-21 2:43:476 days ago1732157027IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0012623613
Execute Withdraw...212234612024-11-19 18:05:597 days ago1732039559IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.003812224.67348631
Create Withdrawa...212005722024-11-16 13:31:3510 days ago1731763895IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0011218211.55274574
Create Withdrawa...211942492024-11-15 16:20:2311 days ago1731687623IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0029735330.62183606
Create Withdrawa...211860772024-11-14 12:57:1112 days ago1731589031IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0025932426.70885634
Redeem211804162024-11-13 17:58:5913 days ago1731520739IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0102882277.69332214
Redeem211793052024-11-13 14:15:3513 days ago1731507335IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0131939699.63652643
Redeem211676132024-11-11 23:05:5915 days ago1731366359IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0049824437.16242306
Cancel Withdrawa...211675952024-11-11 23:02:2315 days ago1731366143IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0050882842.26081124
Redeem211555722024-11-10 6:47:5917 days ago1731221279IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.0017831513.3
Redeem211518852024-11-09 18:27:3517 days ago1731176855IN
Open Ticketing Ecosystem: xOPN Token
0 ETH0.00113939.73997764
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
GovernanceLockedRevenueDistributionToken

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
File 1 of 12 : GovernanceLockedRevenueDistributionToken.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.7;

import {ERC20} from "erc20/ERC20.sol";
import {RevenueDistributionToken} from "revenue-distribution-token/RevenueDistributionToken.sol";
import {LockedRevenueDistributionToken} from "./LockedRevenueDistributionToken.sol";
import {IGovernanceLockedRevenueDistributionToken} from "./interfaces/IGovernanceLockedRevenueDistributionToken.sol";
import {Math} from "./libraries/Math.sol";

/*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░██████╗░██╗░░░░░██████╗░██████╗░████████╗░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░██╔════╝░██║░░░░░██╔══██╗██╔══██╗╚══██╔══╝░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░██║░░██╗░██║░░░░░██████╔╝██║░░██║░░░██║░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░██║░░╚██╗██║░░░░░██╔══██╗██║░░██║░░░██║░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░╚██████╔╝███████╗██║░░██║██████╔╝░░░██║░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░╚═════╝░╚══════╝╚═╝░░╚═╝╚═════╝░░░░╚═╝░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░                                                                       ░░░░
░░░░            Governance Locked Revenue Distribution Token               ░░░░
░░░░                                                                       ░░░░
░░░░  Extending LockedRevenueDistributionToken with Compound governance,   ░░░░
░░░░  using OpenZeppelin's ERC20VotesComp implementation.                  ░░░░
░░░░                                                                       ░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

/**
 * @title  ERC-4626 revenue distribution vault with locking and Compound-compatible governance.
 * @notice Tokens are locked and must be subject to time-based or fee-based withdrawal conditions.
 * @dev    Voting power applies to the the total asset balance, including assets reserved for withdrawal.
 * @dev    Limited to a maximum asset supply of uint96.
 * @author GET Protocol DAO
 * @author Uses Maple's RevenueDistributionToken v1.0.1 under AGPL-3.0 (https://github.com/maple-labs/revenue-distribution-token/tree/v1.0.1)
 * @author Uses OpenZeppelin's ERC20Votes and ERC20VotesComp v4.8.0-rc.1 under MIT (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.0-rc.1/)
 */
contract GovernanceLockedRevenueDistributionToken is
    IGovernanceLockedRevenueDistributionToken,
    LockedRevenueDistributionToken
{
    // DELEGATE_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
    bytes32 private constant DELEGATE_TYPEHASH = 0xe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf;

    mapping(address => address) public delegates;
    mapping(address => Checkpoint[]) public override userCheckpoints;
    Checkpoint[] private totalSupplyCheckpoints;

    constructor(
        string memory name_,
        string memory symbol_,
        address owner_,
        address asset_,
        uint256 precision_,
        uint256 instantWithdrawalFee_,
        uint256 lockTime_,
        uint256 initialSeed_
    )
        LockedRevenueDistributionToken(
            name_,
            symbol_,
            owner_,
            asset_,
            precision_,
            instantWithdrawalFee_,
            lockTime_,
            initialSeed_
        )
    {}

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                         Public Functions                          ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function delegate(address delegatee_) external virtual override {
        _delegate(msg.sender, delegatee_);
    }

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     * @dev Equivalent to the OpenZeppelin implementation but written in style of ERC20.permit.
     */
    function delegateBySig(address delegatee_, uint256 nonce_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_)
        public
        virtual
        override
    {
        require(deadline_ >= block.timestamp, "GLRDT:DBS:EXPIRED");

        // 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}.
        require(
            uint256(s_) <= uint256(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0)
                && (v_ == 27 || v_ == 28),
            "GLRDT:DBS:MALLEABLE"
        );

        bytes32 digest_ = keccak256(
            abi.encodePacked(
                "\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee_, nonce_, deadline_))
            )
        );

        address recoveredAddress_ = ecrecover(digest_, v_, r_, s_);

        require(recoveredAddress_ != address(0), "GLRDT:DBS:INVALID_SIGNATURE");

        // Nonce realistically cannot overflow.
        unchecked {
            require(nonce_ == nonces[recoveredAddress_]++, "GLRDT:DBS:INVALID_NONCE");
        }

        _delegate(recoveredAddress_, delegatee_);
    }

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                          View Functions                           ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function convertToAssets(uint256 shares_, uint256 blockNumber_)
        public
        view
        virtual
        override
        returns (uint256 assets_)
    {
        (uint256 totalSupply_, uint256 totalAssets_) = _checkpointsLookup(totalSupplyCheckpoints, blockNumber_);
        assets_ = totalSupply_ == 0 ? shares_ : (shares_ * totalAssets_) / totalSupply_;
    }

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function checkpoints(address account_, uint32 pos_)
        external
        view
        virtual
        override
        returns (uint32 fromBlock_, uint96 votes_)
    {
        Checkpoint memory checkpoint_ = userCheckpoints[account_][pos_];
        fromBlock_ = checkpoint_.fromBlock;
        votes_ = checkpoint_.assets;
    }

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function numCheckpoints(address account_) public view virtual override returns (uint32 numCheckpoints_) {
        numCheckpoints_ = _toUint32(userCheckpoints[account_].length);
    }

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function getVotes(address account_) public view virtual override returns (uint256 votes_) {
        uint256 pos_ = userCheckpoints[account_].length;
        if (pos_ == 0) {
            return 0;
        }
        uint256 shares_ = userCheckpoints[account_][pos_ - 1].shares;
        votes_ = convertToAssets(shares_, block.number);
    }

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function getCurrentVotes(address account_) external view virtual override returns (uint96 votes_) {
        votes_ = _toUint96(getVotes(account_));
    }

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function getPastVotes(address account_, uint256 blockNumber_)
        public
        view
        virtual
        override
        returns (uint256 votes_)
    {
        require(blockNumber_ < block.number, "GLRDT:BLOCK_NOT_MINED");
        (uint256 shares_,) = _checkpointsLookup(userCheckpoints[account_], blockNumber_);
        votes_ = convertToAssets(shares_, blockNumber_);
    }

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function getPriorVotes(address account_, uint256 blockNumber_)
        external
        view
        virtual
        override
        returns (uint96 votes_)
    {
        votes_ = _toUint96(getPastVotes(account_, blockNumber_));
    }

    /**
     * @inheritdoc IGovernanceLockedRevenueDistributionToken
     */
    function getPastTotalSupply(uint256 blockNumber_) external view virtual override returns (uint256 totalSupply_) {
        require(blockNumber_ < block.number, "GLRDT:BLOCK_NOT_MINED");
        (totalSupply_,) = _checkpointsLookup(totalSupplyCheckpoints, blockNumber_);
    }

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                        Internal Functions                         ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @inheritdoc RevenueDistributionToken
     * @dev Snapshots the totalSupply after it has been increased.
     */
    function _mint(uint256 shares_, uint256 assets_, address receiver_, address caller_) internal virtual override {
        super._mint(shares_, assets_, receiver_, caller_);
        _moveVotingPower(address(0), delegates[receiver_], shares_);
        _writeCheckpoint(totalSupplyCheckpoints, _add, shares_);
    }

    /**
     * @inheritdoc RevenueDistributionToken
     * @dev Snapshots the totalSupply after it has been decreased.
     */
    function _burn(uint256 shares_, uint256 assets_, address receiver_, address owner_, address caller_)
        internal
        virtual
        override
    {
        super._burn(shares_, assets_, receiver_, owner_, caller_);
        _moveVotingPower(delegates[owner_], address(0), shares_);
        _writeCheckpoint(totalSupplyCheckpoints, _subtract, shares_);
    }

    /**
     * @inheritdoc ERC20
     * @dev Move voting power on transfer.
     */
    function _transfer(address owner_, address recipient_, uint256 amount_) internal virtual override {
        super._transfer(owner_, recipient_, amount_);
        _moveVotingPower(delegates[owner_], delegates[recipient_], amount_);
    }

    /**
     * @notice Change delegation for delegator to delegatee.
     * @param  delegator_ Account to transfer delegate balance from.
     * @param  delegatee_ Account to transfer delegate balance to.
     */
    function _delegate(address delegator_, address delegatee_) internal virtual {
        address currentDelegate_ = delegates[delegator_];
        uint256 delegatorBalance_ = balanceOf[delegator_];
        delegates[delegator_] = delegatee_;

        emit DelegateChanged(delegator_, currentDelegate_, delegatee_);

        _moveVotingPower(currentDelegate_, delegatee_, delegatorBalance_);
    }

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                         Private Functions                         ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Lookup a value in a list of (sorted) checkpoints.
     * @param  ckpts        List of checkpoints to find within.
     * @param  blockNumber_ Block number of latest checkpoint.
     * @param  shares_      Amount of shares at checkpoint.
     * @param  assets_      Amount of assets at checkpoint.
     */
    function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber_)
        private
        view
        returns (uint96 shares_, uint96 assets_)
    {
        // We run a binary search to look for the earliest checkpoint taken after `blockNumber_`.
        //
        // Initially we check if the block is recent to narrow the search range.
        // During the loop, the index of the wanted checkpoint remains in the range [low_-1, high_).
        // With each iteration, either `low_` or `high_` is moved towards the middle of the range to maintain the invariant.
        // - If the middle checkpoint is after `blockNumber_`, we look in [low_, mid_)
        // - If the middle checkpoint is before or equal to `blockNumber_`, we look in [mid_+1, high_)
        // Once we reach a single value (when low_ == high_), we've found the right checkpoint at the index high_-1, if not
        // out of bounds (in which case we're looking too far in the past and the result is 0).
        // Note that if the latest checkpoint available is exactly for `blockNumber_`, we end up with an index that is
        // past the end of the array, so we technically don't find a checkpoint after `blockNumber_`, but it works out
        // the same.
        uint256 length_ = ckpts.length;

        uint256 low_ = 0;
        uint256 high_ = length_;

        if (length_ > 5) {
            uint256 mid_ = length_ - Math.sqrt(length_);
            if (_unsafeAccess(ckpts, mid_).fromBlock > blockNumber_) {
                high_ = mid_;
            } else {
                low_ = mid_ + 1;
            }
        }

        while (low_ < high_) {
            uint256 mid_ = Math.average(low_, high_);
            if (_unsafeAccess(ckpts, mid_).fromBlock > blockNumber_) {
                high_ = mid_;
            } else {
                low_ = mid_ + 1;
            }
        }

        if (high_ == 0) {
            return (0, 0);
        }

        Checkpoint memory checkpoint_ = _unsafeAccess(ckpts, high_ - 1);
        return (checkpoint_.shares, checkpoint_.assets);
    }

    /**
     * @notice Move voting power from one account to another.
     * @param  src_    Source account to withdraw voting power from.
     * @param  dst_    Destination account to deposit voting power to.
     * @param  amount_ Ammont of voring power to move, measured in shares.
     */
    function _moveVotingPower(address src_, address dst_, uint256 amount_) private {
        if (src_ != dst_ && amount_ > 0) {
            if (src_ != address(0)) {
                (uint256 oldWeight_, uint256 newWeight_) = _writeCheckpoint(userCheckpoints[src_], _subtract, amount_);
                emit DelegateVotesChanged(src_, oldWeight_, newWeight_);
            }

            if (dst_ != address(0)) {
                (uint256 oldWeight_, uint256 newWeight_) = _writeCheckpoint(userCheckpoints[dst_], _add, amount_);
                emit DelegateVotesChanged(dst_, oldWeight_, newWeight_);
            }
        }
    }

    /**
     * @notice Compute and store a checkpoint within a Checkpoints array. Delta applied to share balance.
     * @param  ckpts      List of checkpoints to add to.
     * @param  op_        Function reference of mathematical operation to apply to delta. Either add or subtract.
     * @param  delta_     Delta between previous checkpoint's shares and new checkpoint's shares.
     * @return oldWeight_ Previous share balance.
     * @return newWeight_ New share balance.
     */
    function _writeCheckpoint(
        Checkpoint[] storage ckpts,
        function(uint256, uint256) view returns (uint256) op_,
        uint256 delta_
    ) private returns (uint256 oldWeight_, uint256 newWeight_) {
        uint256 pos_ = ckpts.length;

        Checkpoint memory oldCkpt_ = pos_ == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, pos_ - 1);

        oldWeight_ = oldCkpt_.shares;
        newWeight_ = op_(oldWeight_, delta_);

        if (pos_ > 0 && oldCkpt_.fromBlock == block.number) {
            _unsafeAccess(ckpts, pos_ - 1).shares = _toUint96(newWeight_);
            _unsafeAccess(ckpts, pos_ - 1).assets = _toUint96(convertToAssets(newWeight_));
        } else {
            ckpts.push(
                Checkpoint({
                    fromBlock: _toUint32(block.number),
                    shares: _toUint96(newWeight_),
                    assets: _toUint96(convertToAssets(newWeight_))
                })
            );
        }
    }

    /**
     * @notice Computes the sum of two numbers.
     * @param  a_      First number.
     * @param  b_      Second number.
     * @return result_ Sum of first and second numbers.
     */
    function _add(uint256 a_, uint256 b_) private pure returns (uint256 result_) {
        return a_ + b_;
    }

    /**
     * @notice Subtracts the second number from the first.
     * @param  a_      First number.
     * @param  b_      Second number.
     * @return result_ Result of first number minus second number.
     */
    function _subtract(uint256 a_, uint256 b_) private pure returns (uint256 result_) {
        return a_ - b_;
    }
    /**
     * @notice Returns the downcasted uint32 from uint256, reverting on overflow (when the input is greater than
     * largest uint32). Counterpart to Solidity's `uint32` operator.
     * @param  value_ Input value to cast.
     */

    function _toUint32(uint256 value_) private pure returns (uint32) {
        require(value_ <= type(uint32).max, "GLRDT:CAST_EXCEEDS_32_BITS");
        return uint32(value_);
    }

    /**
     * @notice Returns the downcasted uint96 from uint256, reverting on overflow (when the input is greater than
     * largest uint96). Counterpart to Solidity's `uint96` operator.
     * @param  value_ Input value to cast.
     */
    function _toUint96(uint256 value_) private pure returns (uint96) {
        require(value_ <= type(uint96).max, "GLRDT:CAST_EXCEEDS_96_BITS");
        return uint96(value_);
    }

    /**
     * @notice Optimize accessing checkpoints from storage.
     * @dev    Added to OpenZeppelin v4.8.0-rc.0 (https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3673)
     * @param  ckpts  Checkpoints array in storage to access.
     * @param  pos_   Index/position of the checkpoint.
     * @return result Checkpoint found at position in array.
     */
    function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos_) private pure returns (Checkpoint storage result) {
        assembly {
            mstore(0, ckpts.slot)
            result.slot := add(keccak256(0, 0x20), pos_)
        }
    }
}

File 2 of 12 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.7;

import { IERC20 } from "./interfaces/IERC20.sol";

/*
    ███████╗██████╗  ██████╗    ██████╗  ██████╗
    ██╔════╝██╔══██╗██╔════╝    ╚════██╗██╔═████╗
    █████╗  ██████╔╝██║          █████╔╝██║██╔██║
    ██╔══╝  ██╔══██╗██║         ██╔═══╝ ████╔╝██║
    ███████╗██║  ██║╚██████╗    ███████╗╚██████╔╝
    ╚══════╝╚═╝  ╚═╝ ╚═════╝    ╚══════╝ ╚═════╝
*/

/**
 *  @title Modern ERC-20 implementation.
 *  @dev   Acknowledgements to Solmate, OpenZeppelin, and DSS for inspiring this code.
 */
contract ERC20 is IERC20 {

    /**************/
    /*** ERC-20 ***/
    /**************/

    string public override name;
    string public override symbol;

    uint8 public immutable override decimals;

    uint256 public override totalSupply;

    mapping(address => uint256) public override balanceOf;

    mapping(address => mapping(address => uint256)) public override allowance;

    /****************/
    /*** ERC-2612 ***/
    /****************/

    // PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 private constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    uint256 internal immutable initialChainId;

    bytes32 internal immutable initialDomainSeparator;

    mapping(address => uint256) public override nonces;

    /**
     *  @param name_     The name of the token.
     *  @param symbol_   The symbol of the token.
     *  @param decimals_ The decimal precision used by the token.
     */
    constructor(string memory name_, string memory symbol_, uint8 decimals_) {
        name     = name_;
        symbol   = symbol_;
        decimals = decimals_;
        initialChainId = block.chainid;
        initialDomainSeparator = _computeDomainSeparator();
    }

    /**************************/
    /*** External Functions ***/
    /**************************/

    function approve(address spender_, uint256 amount_) external override returns (bool success_) {
        _approve(msg.sender, spender_, amount_);
        return true;
    }

    function decreaseAllowance(address spender_, uint256 subtractedAmount_) external override returns (bool success_) {
        _decreaseAllowance(msg.sender, spender_, subtractedAmount_);
        return true;
    }

    function increaseAllowance(address spender_, uint256 addedAmount_) external override returns (bool success_) {
        _approve(msg.sender, spender_, allowance[msg.sender][spender_] + addedAmount_);
        return true;
    }

    function permit(address owner_, address spender_, uint256 amount_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_) external override {
        require(deadline_ >= block.timestamp, "ERC20:P:EXPIRED");

        // 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}.
        require(
            uint256(s_) <= uint256(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) &&
            (v_ == 27 || v_ == 28),
            "ERC20:P:MALLEABLE"
        );

        // Nonce realistically cannot overflow.
        unchecked {
            bytes32 digest = keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR(),
                    keccak256(abi.encode(PERMIT_TYPEHASH, owner_, spender_, amount_, nonces[owner_]++, deadline_))
                )
            );

            address recoveredAddress = ecrecover(digest, v_, r_, s_);

            require(recoveredAddress == owner_ && owner_ != address(0), "ERC20:P:INVALID_SIGNATURE");
        }

        _approve(owner_, spender_, amount_);
    }

    function transfer(address recipient_, uint256 amount_) external override returns (bool success_) {
        _transfer(msg.sender, recipient_, amount_);
        return true;
    }

    function transferFrom(address owner_, address recipient_, uint256 amount_) external override returns (bool success_) {
        _decreaseAllowance(owner_, msg.sender, amount_);
        _transfer(owner_, recipient_, amount_);
        return true;
    }

    /**********************/
    /*** View Functions ***/
    /**********************/

    function DOMAIN_SEPARATOR() public view virtual override returns (bytes32 domainSeparator_) {
        return block.chainid == initialChainId ? initialDomainSeparator : _computeDomainSeparator();
    }

    /**************************/
    /*** Internal Functions ***/
    /**************************/

    function _approve(address owner_, address spender_, uint256 amount_) internal {
        emit Approval(owner_, spender_, allowance[owner_][spender_] = amount_);
    }

    function _burn(address owner_, uint256 amount_) internal virtual {
        balanceOf[owner_] -= amount_;

        // Cannot underflow because a user's balance will never be larger than the total supply.
        unchecked { totalSupply -= amount_; }

        emit Transfer(owner_, address(0), amount_);
    }

    function _computeDomainSeparator() internal view virtual returns (bytes32 domainSeparator_) {
        return keccak256(
            abi.encode(
                keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );
    }

    function _decreaseAllowance(address owner_, address spender_, uint256 subtractedAmount_) internal {
        uint256 spenderAllowance = allowance[owner_][spender_];  // Cache to memory.

        if (spenderAllowance != type(uint256).max) {
            _approve(owner_, spender_, spenderAllowance - subtractedAmount_);
        }
    }

    function _mint(address recipient_, uint256 amount_) internal virtual {
        totalSupply += amount_;

        // Cannot overflow because totalSupply would first overflow in the statement above.
        unchecked { balanceOf[recipient_] += amount_; }

        emit Transfer(address(0), recipient_, amount_);
    }

    function _transfer(address owner_, address recipient_, uint256 amount_) internal virtual {
        balanceOf[owner_] -= amount_;

        // Cannot overflow because minting prevents overflow of totalSupply, and sum of user balances == totalSupply.
        unchecked { balanceOf[recipient_] += amount_; }

        emit Transfer(owner_, recipient_, amount_);
    }

}

File 3 of 12 : RevenueDistributionToken.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;

import { ERC20 }       from "erc20/ERC20.sol";
import { ERC20Helper } from "erc20-helper/ERC20Helper.sol";

import { IRevenueDistributionToken } from "./interfaces/IRevenueDistributionToken.sol";

/*
    ██████╗ ██████╗ ████████╗
    ██╔══██╗██╔══██╗╚══██╔══╝
    ██████╔╝██║  ██║   ██║
    ██╔══██╗██║  ██║   ██║
    ██║  ██║██████╔╝   ██║
    ╚═╝  ╚═╝╚═════╝    ╚═╝
*/

contract RevenueDistributionToken is IRevenueDistributionToken, ERC20 {

    uint256 public immutable override precision;  // Precision of rates, equals max deposit amounts before rounding errors occur

    address public override asset;  // Underlying ERC-20 asset used by ERC-4626 functionality.

    address public override owner;         // Current owner of the contract, able to update the vesting schedule.
    address public override pendingOwner;  // Pending owner of the contract, able to accept ownership.

    uint256 public override freeAssets;           // Amount of assets unlocked regardless of time passed.
    uint256 public override issuanceRate;         // asset/second rate dependent on aggregate vesting schedule.
    uint256 public override lastUpdated;          // Timestamp of when issuance equation was last updated.
    uint256 public override vestingPeriodFinish;  // Timestamp when current vesting schedule ends.

    uint256 private locked = 1;  // Used in reentrancy check.

    /*****************/
    /*** Modifiers ***/
    /*****************/

    modifier nonReentrant() {
        require(locked == 1, "RDT:LOCKED");

        locked = 2;

        _;

        locked = 1;
    }

    constructor(string memory name_, string memory symbol_, address owner_, address asset_, uint256 precision_)
        ERC20(name_, symbol_, ERC20(asset_).decimals())
    {
        require((owner = owner_) != address(0), "RDT:C:OWNER_ZERO_ADDRESS");

        asset     = asset_;  // Don't need to check zero address as ERC20(asset_).decimals() will fail in ERC20 constructor.
        precision = precision_;
    }

    /********************************/
    /*** Administrative Functions ***/
    /********************************/

    function acceptOwnership() external virtual override {
        require(msg.sender == pendingOwner, "RDT:AO:NOT_PO");

        emit OwnershipAccepted(owner, msg.sender);

        owner        = msg.sender;
        pendingOwner = address(0);
    }

    function setPendingOwner(address pendingOwner_) external virtual override {
        require(msg.sender == owner, "RDT:SPO:NOT_OWNER");

        pendingOwner = pendingOwner_;

        emit PendingOwnerSet(msg.sender, pendingOwner_);
    }

    function updateVestingSchedule(uint256 vestingPeriod_) external virtual override returns (uint256 issuanceRate_, uint256 freeAssets_) {
        require(msg.sender == owner, "RDT:UVS:NOT_OWNER");
        require(totalSupply != 0,    "RDT:UVS:ZERO_SUPPLY");

        // Update "y-intercept" to reflect current available asset.
        freeAssets_ = freeAssets = totalAssets();

        // Calculate slope.
        issuanceRate_ = issuanceRate = ((ERC20(asset).balanceOf(address(this)) - freeAssets_) * precision) / vestingPeriod_;

        // Update timestamp and period finish.
        vestingPeriodFinish = (lastUpdated = block.timestamp) + vestingPeriod_;

        emit IssuanceParamsUpdated(freeAssets_, issuanceRate_);
        emit VestingScheduleUpdated(msg.sender, vestingPeriodFinish);
    }

    /************************/
    /*** Staker Functions ***/
    /************************/

    function deposit(uint256 assets_, address receiver_) public virtual override nonReentrant returns (uint256 shares_) {
        _mint(shares_ = previewDeposit(assets_), assets_, receiver_, msg.sender);
    }

    function depositWithPermit(
        uint256 assets_,
        address receiver_,
        uint256 deadline_,
        uint8   v_,
        bytes32 r_,
        bytes32 s_
    )
        external virtual override nonReentrant returns (uint256 shares_)
    {
        ERC20(asset).permit(msg.sender, address(this), assets_, deadline_, v_, r_, s_);
        _mint(shares_ = previewDeposit(assets_), assets_, receiver_, msg.sender);
    }

    function mint(uint256 shares_, address receiver_) public virtual override nonReentrant returns (uint256 assets_) {
        _mint(shares_, assets_ = previewMint(shares_), receiver_, msg.sender);
    }

    function mintWithPermit(
        uint256 shares_,
        address receiver_,
        uint256 maxAssets_,
        uint256 deadline_,
        uint8   v_,
        bytes32 r_,
        bytes32 s_
    )
        external virtual override nonReentrant returns (uint256 assets_)
    {
        require((assets_ = previewMint(shares_)) <= maxAssets_, "RDT:MWP:INSUFFICIENT_PERMIT");

        ERC20(asset).permit(msg.sender, address(this), maxAssets_, deadline_, v_, r_, s_);
        _mint(shares_, assets_, receiver_, msg.sender);
    }

    function redeem(uint256 shares_, address receiver_, address owner_) external virtual override nonReentrant returns (uint256 assets_) {
        _burn(shares_, assets_ = previewRedeem(shares_), receiver_, owner_, msg.sender);
    }

    function withdraw(uint256 assets_, address receiver_, address owner_) external virtual override nonReentrant returns (uint256 shares_) {
        _burn(shares_ = previewWithdraw(assets_), assets_, receiver_, owner_, msg.sender);
    }

    /**************************/
    /*** Internal Functions ***/
    /**************************/

    function _mint(uint256 shares_, uint256 assets_, address receiver_, address caller_) internal virtual {
        require(receiver_ != address(0), "RDT:M:ZERO_RECEIVER");
        require(shares_   != uint256(0), "RDT:M:ZERO_SHARES");
        require(assets_   != uint256(0), "RDT:M:ZERO_ASSETS");

        _mint(receiver_, shares_);

        uint256 freeAssetsCache = freeAssets = totalAssets() + assets_;

        uint256 issuanceRate_ = _updateIssuanceParams();

        emit Deposit(caller_, receiver_, assets_, shares_);
        emit IssuanceParamsUpdated(freeAssetsCache, issuanceRate_);

        require(ERC20Helper.transferFrom(asset, caller_, address(this), assets_), "RDT:M:TRANSFER_FROM");
    }

    function _burn(uint256 shares_, uint256 assets_, address receiver_, address owner_, address caller_) internal virtual {
        require(receiver_ != address(0), "RDT:B:ZERO_RECEIVER");
        require(shares_   != uint256(0), "RDT:B:ZERO_SHARES");
        require(assets_   != uint256(0), "RDT:B:ZERO_ASSETS");

        if (caller_ != owner_) {
            _decreaseAllowance(owner_, caller_, shares_);
        }

        _burn(owner_, shares_);

        uint256 freeAssetsCache = freeAssets = totalAssets() - assets_;

        uint256 issuanceRate_ = _updateIssuanceParams();

        emit Withdraw(caller_, receiver_, owner_, assets_, shares_);
        emit IssuanceParamsUpdated(freeAssetsCache, issuanceRate_);

        require(ERC20Helper.transfer(asset, receiver_, assets_), "RDT:B:TRANSFER");
    }

    function _updateIssuanceParams() internal returns (uint256 issuanceRate_) {
        return issuanceRate = (lastUpdated = block.timestamp) > vestingPeriodFinish ? 0 : issuanceRate;
    }

    /**********************/
    /*** View Functions ***/
    /**********************/

    function balanceOfAssets(address account_) public view virtual override returns (uint256 balanceOfAssets_) {
        return convertToAssets(balanceOf[account_]);
    }

    function convertToAssets(uint256 shares_) public view virtual override returns (uint256 assets_) {
        uint256 supply = totalSupply;  // Cache to stack.

        assets_ = supply == 0 ? shares_ : (shares_ * totalAssets()) / supply;
    }

    function convertToShares(uint256 assets_) public view virtual override returns (uint256 shares_) {
        uint256 supply = totalSupply;  // Cache to stack.

        shares_ = supply == 0 ? assets_ : (assets_ * supply) / totalAssets();
    }

    function maxDeposit(address receiver_) external pure virtual override returns (uint256 maxAssets_) {
        receiver_;  // Silence warning
        maxAssets_ = type(uint256).max;
    }

    function maxMint(address receiver_) external pure virtual override returns (uint256 maxShares_) {
        receiver_;  // Silence warning
        maxShares_ = type(uint256).max;
    }

    function maxRedeem(address owner_) external view virtual override returns (uint256 maxShares_) {
        maxShares_ = balanceOf[owner_];
    }

    function maxWithdraw(address owner_) external view virtual override returns (uint256 maxAssets_) {
        maxAssets_ = balanceOfAssets(owner_);
    }

    function previewDeposit(uint256 assets_) public view virtual override returns (uint256 shares_) {
        // As per https://eips.ethereum.org/EIPS/eip-4626#security-considerations,
        // it should round DOWN if it’s calculating the amount of shares to issue to a user, given an amount of assets provided.
        shares_ = convertToShares(assets_);
    }

    function previewMint(uint256 shares_) public view virtual override returns (uint256 assets_) {
        uint256 supply = totalSupply;  // Cache to stack.

        // As per https://eips.ethereum.org/EIPS/eip-4626#security-considerations,
        // it should round UP if it’s calculating the amount of assets a user must provide, to be issued a given amount of shares.
        assets_ = supply == 0 ? shares_ : _divRoundUp(shares_ * totalAssets(), supply);
    }

    function previewRedeem(uint256 shares_) public view virtual override returns (uint256 assets_) {
        // As per https://eips.ethereum.org/EIPS/eip-4626#security-considerations,
        // it should round DOWN if it’s calculating the amount of assets to send to a user, given amount of shares returned.
        assets_ = convertToAssets(shares_);
    }

    function previewWithdraw(uint256 assets_) public view virtual override returns (uint256 shares_) {
        uint256 supply = totalSupply;  // Cache to stack.

        // As per https://eips.ethereum.org/EIPS/eip-4626#security-considerations,
        // it should round UP if it’s calculating the amount of shares a user must return, to be sent a given amount of assets.
        shares_ = supply == 0 ? assets_ : _divRoundUp(assets_ * supply, totalAssets());
    }

    function totalAssets() public view virtual override returns (uint256 totalManagedAssets_) {
        uint256 issuanceRate_ = issuanceRate;

        if (issuanceRate_ == 0) return freeAssets;

        uint256 vestingPeriodFinish_ = vestingPeriodFinish;
        uint256 lastUpdated_         = lastUpdated;

        uint256 vestingTimePassed =
            block.timestamp > vestingPeriodFinish_ ?
                vestingPeriodFinish_ - lastUpdated_ :
                block.timestamp - lastUpdated_;

        return ((issuanceRate_ * vestingTimePassed) / precision) + freeAssets;
    }

    /**************************/
    /*** Internal Functions ***/
    /**************************/

    function _divRoundUp(uint256 numerator_, uint256 divisor_) internal pure returns (uint256 result_) {
       return (numerator_ / divisor_) + (numerator_ % divisor_ > 0 ? 1 : 0);
    }

}

File 4 of 12 : LockedRevenueDistributionToken.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.7;

import {RevenueDistributionToken} from "revenue-distribution-token/RevenueDistributionToken.sol";
import {ERC20} from "erc20/ERC20.sol";
import {ERC20Helper} from "erc20-helper/ERC20Helper.sol";
import {ILockedRevenueDistributionToken} from "./interfaces/ILockedRevenueDistributionToken.sol";

/*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░██╗░░░░░██████╗░██████╗░████████╗░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░██║░░░░░██╔══██╗██╔══██╗╚══██╔══╝░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░██║░░░░░██████╔╝██║░░██║░░░██║░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░██║░░░░░██╔══██╗██║░░██║░░░██║░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░███████╗██║░░██║██████╔╝░░░██║░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░╚══════╝╚═╝░░╚═╝╚═════╝░░░░╚═╝░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░                                                                       ░░░░
░░░░                  Locked Revenue Distribution Token                    ░░░░
░░░░                                                                       ░░░░
░░░░  Extending Maple's RevenueDistributionToken with time-based locking,  ░░░░
░░░░  fee-based instant withdrawals and public vesting schedule updating.  ░░░░
░░░░                                                                       ░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

/**
 * @title  ERC-4626 revenue distribution vault with locking.
 * @notice Tokens are locked and must be subject to time-based or fee-based withdrawal conditions.
 * @dev    Limited to a maximum asset supply of uint96.
 * @author GET Protocol DAO
 * @author Uses Maple's RevenueDistributionToken v1.0.1 under AGPL-3.0 (https://github.com/maple-labs/revenue-distribution-token/tree/v1.0.1)
 */
contract LockedRevenueDistributionToken is ILockedRevenueDistributionToken, RevenueDistributionToken {
    uint256 public constant override MAXIMUM_LOCK_TIME = 104 weeks;
    uint256 public constant override VESTING_PERIOD = 2 weeks;
    uint256 public constant override WITHDRAWAL_WINDOW = 4 weeks;
    uint256 public override instantWithdrawalFee;
    uint256 public override lockTime;

    mapping(address => WithdrawalRequest[]) internal userWithdrawalRequests;
    mapping(address => bool) public override withdrawalFeeExemptions;

    constructor(
        string memory name_,
        string memory symbol_,
        address owner_,
        address asset_,
        uint256 precision_,
        uint256 instantWithdrawalFee_,
        uint256 lockTime_,
        uint256 initialSeed_
    ) RevenueDistributionToken(name_, symbol_, owner_, asset_, precision_) {
        instantWithdrawalFee = instantWithdrawalFee_;
        lockTime = lockTime_;

        // We initialize the contract by seeding an amount of shares and then burning them. This prevents donation
        // attacks from affecting the precision of the shares:assets rate.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706
        if (initialSeed_ > 0) {
            address caller_ = msg.sender;
            address receiver_ = address(0);

            // RDT.deposit() cannot be called within the constructor as this uses immutable variables.
            // ERC20._mint()
            totalSupply += initialSeed_;
            unchecked {
                balanceOf[receiver_] += initialSeed_;
            }
            emit Transfer(address(0), receiver_, initialSeed_);

            // RDT._mint()
            freeAssets = initialSeed_;
            emit Deposit(caller_, receiver_, initialSeed_, initialSeed_);
            emit IssuanceParamsUpdated(freeAssets, 0);
            require(ERC20Helper.transferFrom(asset_, msg.sender, address(this), initialSeed_), "LRDT:C:TRANSFER_FROM");
        }
    }

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                     Administrative Functions                      ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function setInstantWithdrawalFee(uint256 percentage_) external virtual override {
        require(msg.sender == owner, "LRDT:CALLER_NOT_OWNER");
        require(percentage_ < 100, "LRDT:INVALID_FEE");

        instantWithdrawalFee = percentage_;

        emit InstantWithdrawalFeeChanged(percentage_);
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function setLockTime(uint256 lockTime_) external virtual override {
        require(msg.sender == owner, "LRDT:CALLER_NOT_OWNER");
        require(lockTime_ <= MAXIMUM_LOCK_TIME, "LRDT:INVALID_LOCK_TIME");

        lockTime = lockTime_;

        emit LockTimeChanged(lockTime_);
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function setWithdrawalFeeExemption(address account_, bool status_) external virtual override {
        require(msg.sender == owner, "LRDT:CALLER_NOT_OWNER");
        require(account_ != address(0), "LRDT:ZERO_ACCOUNT");

        if (status_) {
            withdrawalFeeExemptions[account_] = true;
        } else {
            delete withdrawalFeeExemptions[account_];
        }

        emit WithdrawalFeeExemptionStatusChanged(account_, status_);
    }

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                         Public Functions                          ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function createWithdrawalRequest(uint256 shares_) external virtual override nonReentrant {
        require(shares_ > 0, "LRDT:INVALID_AMOUNT");
        require(shares_ <= balanceOf[msg.sender], "LRDT:INSUFFICIENT_BALANCE");

        WithdrawalRequest memory request_ = WithdrawalRequest(
            uint32(block.timestamp + lockTime), uint32(lockTime), uint96(shares_), uint96(convertToAssets(shares_))
        );
        userWithdrawalRequests[msg.sender].push(request_);

        _transfer(msg.sender, address(this), shares_);

        emit WithdrawalRequestCreated(request_, userWithdrawalRequests[msg.sender].length - 1);
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function cancelWithdrawalRequest(uint256 pos_) external virtual override nonReentrant {
        WithdrawalRequest memory request_ = userWithdrawalRequests[msg.sender][pos_];
        require(request_.shares > 0, "LRDT:NO_WITHDRAWAL_REQUEST");

        delete userWithdrawalRequests[msg.sender][pos_];

        uint256 refundShares_ = convertToShares(request_.assets);
        uint256 burnShares_ = request_.shares - refundShares_;

        if (burnShares_ > 0) {
            uint256 burnAssets_ = convertToAssets(burnShares_);
            _burn(burnShares_, burnAssets_, address(this), address(this), address(this));
            emit Redistribute(burnAssets_);
        }

        if (refundShares_ > 0) {
            _transfer(address(this), msg.sender, refundShares_);
            emit Refund(msg.sender, convertToAssets(refundShares_), refundShares_);
        }

        emit WithdrawalRequestCancelled(pos_);
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function executeWithdrawalRequest(uint256 pos_) external virtual override nonReentrant {
        (WithdrawalRequest memory request_, uint256 assets_, uint256 fee_) = previewWithdrawalRequest(pos_, msg.sender);
        require(request_.shares > 0, "LRDT:NO_WITHDRAWAL_REQUEST");
        require(request_.unlockedAt + WITHDRAWAL_WINDOW > block.timestamp, "LRDT:WITHDRAWAL_WINDOW_CLOSED");

        delete userWithdrawalRequests[msg.sender][pos_];

        uint256 executeShares_ = convertToShares(assets_);
        uint256 burnShares_ = request_.shares - executeShares_;

        if (burnShares_ > 0) {
            uint256 burnAssets_ = convertToAssets(burnShares_);
            _burn(burnShares_, burnAssets_, address(this), address(this), address(this));
            emit Redistribute(burnAssets_ - fee_);
        }

        if (executeShares_ > 0) {
            _transfer(address(this), msg.sender, executeShares_);
            _burn(executeShares_, assets_, msg.sender, msg.sender, msg.sender);
        }

        if (fee_ > 0) {
            emit WithdrawalFeePaid(msg.sender, msg.sender, msg.sender, fee_);
        }

        emit WithdrawalRequestExecuted(pos_);
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function updateVestingSchedule() external virtual override returns (uint256 issuanceRate_, uint256 freeAssets_) {
        // This require is here to prevent public function calls extending the vesting period infinitely. By allowing
        // this to be called again on the last day of the vesting period, we can maintain a regular schedule of reward
        // distribution on the same day of the week.
        //
        // Aside from the following line, and a fixed vesting period, this function is unchanged from the Maple
        // implementation.
        require(vestingPeriodFinish <= block.timestamp + 24 hours, "LRDT:UVS:STILL_VESTING");
        require(totalSupply > 0, "LRDT:UVS:ZERO_SUPPLY");

        // Update "y-intercept" to reflect current available asset.
        freeAssets_ = (freeAssets = totalAssets());

        // Carry over remaining time.
        uint256 vestingTime_ = VESTING_PERIOD;
        if (vestingPeriodFinish > block.timestamp) {
            vestingTime_ = VESTING_PERIOD + (vestingPeriodFinish - block.timestamp);
        }

        // Calculate slope.
        issuanceRate_ =
            (issuanceRate = ((ERC20(asset).balanceOf(address(this)) - freeAssets_) * precision) / vestingTime_);

        require(issuanceRate_ > 0, "LRDT:UVS:ZERO_ISSUANCE_RATE");

        // Update timestamp and period finish.
        vestingPeriodFinish = (lastUpdated = block.timestamp) + vestingTime_;

        emit IssuanceParamsUpdated(freeAssets_, issuanceRate_);
        emit VestingScheduleUpdated(msg.sender, vestingPeriodFinish);
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     * @dev Reentrancy modifier provided within the internal function call.
     */
    function deposit(uint256 assets_, address receiver_, uint256 minShares_)
        external
        virtual
        override
        returns (uint256 shares_)
    {
        shares_ = deposit(assets_, receiver_);
        require(shares_ >= minShares_, "LRDT:D:SLIPPAGE_PROTECTION");
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     * @dev Reentrancy modifier provided within the internal function call.
     */
    function mint(uint256 shares_, address receiver_, uint256 maxAssets_)
        external
        virtual
        override
        returns (uint256 assets_)
    {
        assets_ = mint(shares_, receiver_);
        require(assets_ <= maxAssets_, "LRDT:M:SLIPPAGE_PROTECTION");
    }

    /**
     * @inheritdoc RevenueDistributionToken
     * @dev Will check for withdrawal fee exemption present on owner.
     */
    function redeem(uint256 shares_, address receiver_, address owner_)
        public
        virtual
        override
        nonReentrant
        returns (uint256 assets_)
    {
        uint256 fee_;
        (assets_, fee_) = previewRedeem(shares_, owner_);
        _burn(shares_, assets_, receiver_, owner_, msg.sender);

        if (fee_ > 0) {
            emit WithdrawalFeePaid(msg.sender, receiver_, owner_, fee_);
        }
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     * @dev Reentrancy modifier provided within the internal function call.
     */
    function redeem(uint256 shares_, address receiver_, address owner_, uint256 minAssets_)
        external
        virtual
        override
        returns (uint256 assets_)
    {
        assets_ = redeem(shares_, receiver_, owner_);
        require(assets_ >= minAssets_, "LRDT:R:SLIPPAGE_PROTECTION");
    }

    /**
     * @inheritdoc RevenueDistributionToken
     * @dev Will check for withdrawal fee exemption present on owner.
     */
    function withdraw(uint256 assets_, address receiver_, address owner_)
        public
        virtual
        override
        nonReentrant
        returns (uint256 shares_)
    {
        uint256 fee_;
        (shares_, fee_) = previewWithdraw(assets_, owner_);
        _burn(shares_, assets_, receiver_, owner_, msg.sender);

        if (fee_ > 0) {
            emit WithdrawalFeePaid(msg.sender, receiver_, owner_, fee_);
        }
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     * @dev Reentrancy modifier provided within the internal function call.
     */
    function withdraw(uint256 assets_, address receiver_, address owner_, uint256 maxShares_)
        external
        virtual
        override
        returns (uint256 shares_)
    {
        shares_ = withdraw(assets_, receiver_, owner_);
        require(shares_ <= maxShares_, "LRDT:W:SLIPPAGE_PROTECTION");
    }

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                          View Functions                           ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @inheritdoc RevenueDistributionToken
     * @dev Returns the amount of redeemable assets for given shares after instant withdrawal fee.
     * @dev `address(0)` cannot be set as exempt, and is used here as default to imply that fees must be deducted.
     */
    function previewRedeem(uint256 shares_) public view virtual override returns (uint256 assets_) {
        (assets_,) = previewRedeem(shares_, address(0));
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function previewRedeem(uint256 shares_, address owner_)
        public
        view
        virtual
        override
        returns (uint256 assets_, uint256 fee_)
    {
        if (withdrawalFeeExemptions[owner_]) {
            return (super.previewRedeem(shares_), 0);
        }

        uint256 assetsPlusFee_ = super.previewRedeem(shares_);
        assets_ = (assetsPlusFee_ * (100 - instantWithdrawalFee)) / 100;
        fee_ = assetsPlusFee_ - assets_;
    }

    /**
     * @inheritdoc RevenueDistributionToken
     * @dev Returns the amount of redeemable assets for given shares after instant withdrawal fee.
     * @dev `address(0)` cannot be set as exempt, and is used here as default to imply that fees must be deducted.
     */
    function previewWithdraw(uint256 assets_) public view virtual override returns (uint256 shares_) {
        (shares_,) = previewWithdraw(assets_, address(0));
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function previewWithdraw(uint256 assets_, address owner_)
        public
        view
        virtual
        override
        returns (uint256 shares_, uint256 fee_)
    {
        if (withdrawalFeeExemptions[owner_]) {
            return (super.previewWithdraw(assets_), 0);
        }

        uint256 assetsPlusFee_ = (assets_ * 100) / (100 - instantWithdrawalFee);
        shares_ = super.previewWithdraw(assetsPlusFee_);
        fee_ = assetsPlusFee_ - assets_;
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function previewWithdrawalRequest(uint256 pos_, address owner_)
        public
        view
        virtual
        override
        returns (WithdrawalRequest memory request_, uint256 assets_, uint256 fee_)
    {
        request_ = userWithdrawalRequests[owner_][pos_];

        if (withdrawalFeeExemptions[owner_] || request_.unlockedAt <= block.timestamp) {
            return (request_, request_.assets, 0);
        }

        uint256 remainingTime_ = request_.unlockedAt - block.timestamp;
        uint256 feePercentage_ = (instantWithdrawalFee * remainingTime_ * precision) / request_.lockTime;
        assets_ = (request_.assets * (100 * precision - feePercentage_)) / (100 * precision);
        fee_ = request_.assets - assets_;
    }

    /**
     * @inheritdoc RevenueDistributionToken
     * @dev Restricted to uint96 as defined in WithdrawalRequest struct.
     */
    function maxDeposit(address receiver_) external pure virtual override returns (uint256 maxAssets_) {
        receiver_; // Silence warning
        maxAssets_ = type(uint96).max;
    }

    /**
     * @inheritdoc RevenueDistributionToken
     * @dev Restricted to uint96 as defined in WithdrawalRequest struct.
     */
    function maxMint(address receiver_) external pure virtual override returns (uint256 maxShares_) {
        receiver_; // Silence warning
        maxShares_ = type(uint96).max;
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function withdrawalRequestCount(address owner_) external view virtual override returns (uint256 count_) {
        count_ = userWithdrawalRequests[owner_].length;
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function withdrawalRequests(address owner_)
        external
        view
        virtual
        override
        returns (WithdrawalRequest[] memory withdrawalRequests_)
    {
        withdrawalRequests_ = userWithdrawalRequests[owner_];
    }

    /**
     * @inheritdoc ILockedRevenueDistributionToken
     */
    function withdrawalRequests(address account_, uint256 pos_)
        external
        view
        virtual
        override
        returns (WithdrawalRequest memory withdrawalRequest_)
    {
        withdrawalRequest_ = userWithdrawalRequests[account_][pos_];
    }
}

File 5 of 12 : IGovernanceLockedRevenueDistributionToken.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.7;

interface IGovernanceLockedRevenueDistributionToken {
    /**
     * @notice        Represents a voting checkpoin, packed into a single word.
     * @custom:member fromBlock Block number after which the checkpoint applies.
     * @custom:member shares    Amount of shares held & delegated to calculate point-in-time votes.
     * @custom:member assets    Amount of assets held & delegated to calculate point-in-time votes.
     */
    struct Checkpoint {
        uint32 fromBlock;
        uint96 shares;
        uint96 assets;
    }

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                              Events                               ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Emitted when an account changes their delegate.
     * @param  delegator_    Account that has changed delegate.
     * @param  fromDelegate_ Previous delegate.
     * @param  toDelegate_   New delegate.
     */
    event DelegateChanged(address indexed delegator_, address indexed fromDelegate_, address indexed toDelegate_);

    /**
     * @notice Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
     * @param  delegate_        Delegate that has received delegated balance change.
     * @param  previousBalance_ Previous delegated balance.
     * @param  newBalance_      New delegated balance.
     */
    event DelegateVotesChanged(address indexed delegate_, uint256 previousBalance_, uint256 newBalance_);

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                          State Variables                          ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Get the `pos`-th checkpoint for `account`.
     * @dev    Unused in Compound governance specification, exposes underlying Checkpoint struct.
     * @param  account_   Account that holds checkpoint.
     * @param  pos_       Index/position of the checkpoint.
     * @return fromBlock  Block in which the checkpoint is valid from.
     * @return shares     Total amount of shares within the checkpoint.
     * @return assets     Total amount of underlying assets derived from shares at time of checkpoint.
     */
    function userCheckpoints(address account_, uint256 pos_)
        external
        view
        returns (uint32 fromBlock, uint96 shares, uint96 assets);

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                         Public Functions                          ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Delegates votes from the sender to `delegatee`.
     * @dev    Shares are delegated upon mint and transfer and removed upon burn.
     * @param  delegatee_ Account to delegate votes to.
     */
    function delegate(address delegatee_) external;

    /**
     * @notice Delegates votes from signer to `delegatee`.
     * @param  delegatee_ Account to delegate votes to.
     * @param  nonce_     Nonce of next signature transaction, expected to be equal to `nonces(signer)`.
     * @param  deadline_  Deadline after which the permit is invalid.
     * @param  v_         ECDSA signature v component.
     * @param  r_         ECDSA signature r component.
     * @param  s_         ECDSA signature s component.
     */
    function delegateBySig(address delegatee_, uint256 nonce_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_)
        external;

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                          View Functions                           ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Historical conversion from shares to assets, used for calculating voting power on past blocks.
     * @param  shares_      Amount of shares to conver to assets.
     * @param  blockNumber_ Block to use for checkpoint lookup.
     * @return assets_      Amount of assets held at block, representing voting power.
     */
    function convertToAssets(uint256 shares_, uint256 blockNumber_) external view returns (uint256 assets_);

    /**
     * @notice Get the Compound-compatible `pos`-th checkpoint for `account`.
     * @dev    Maintains Compound `checkpoints` compatibility by returning votes as a uint96 and omitting shares.
     * @param  account_   Account that holds checkpoint.
     * @param  pos_       Index/position of the checkpoint.
     * @return fromBlock_ Block in which the checkpoint is valid from.
     * @return votes_     Total amount of underlying assets (votes) derived from shares.
     */
    function checkpoints(address account_, uint32 pos_) external view returns (uint32 fromBlock_, uint96 votes_);

    /**
     * @dev Get number of checkpoints for `account`.
     */
    function numCheckpoints(address account_) external view returns (uint32);

    /**
     * @notice Returns the current amount of votes that `account` has.
     * @dev    The delegated balance is denominated in the amount of shares delegated to an account, but voting power
     * is measured in assets. A conversion is done using the delegated shares to get the assets as of the latest
     * checkpoint. This ensures that all stakers' shares are converted to assets at the same rate.
     * @param  account_ Address of account to get votes for.
     * @return votes_   Amount of voting power as the number of assets for delegated shares.
     */
    function getVotes(address account_) external view returns (uint256 votes_);

    /**
     * @notice Comp version of the `getVotes` accessor, with `uint96` return type.
     * @param  account_ Address of account to get votes for.
     * @return votes_   Amount of voting power as the number of assets for delegated shares.
     */
    function getCurrentVotes(address account_) external view returns (uint96 votes_);

    /**
     * @notice Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
     * @param  account_     Address of account to get votes for.
     * @param  blockNumber_ Voting power at block.
     * @return votes_       Amount of voting power as the number of assets for delegated shares.
     */
    function getPastVotes(address account_, uint256 blockNumber_) external view returns (uint256 votes_);

    /**
     * @notice Comp version of the `getPastVotes` accessor, with `uint96` return type.
     * @param  account_     Address of account to get votes for.
     * @param  blockNumber_ Voting power at block.
     * @return votes_       Amount of voting power as the number of assets for delegated shares.
     */
    function getPriorVotes(address account_, uint256 blockNumber_) external view returns (uint96 votes_);

    /**
     * @notice Returns the total supply of shares available at the end of a past block (`blockNumber`).
     * @param  blockNumber_ Block number to check for total supply.
     * @return totalSupply_ Total supply of shares.
     */
    function getPastTotalSupply(uint256 blockNumber_) external view returns (uint256 totalSupply_);
}

File 6 of 12 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

/**
 * @dev Standard math utilities missing in the Solidity language.
 * @author Uses OpenZeppelin's Math.sol v4.7.0 under MIT (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.0/contracts/utils/math/Math.sol)
 */
library Math {
    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb computation, we are able to compute `result = 2**(k/2)` which is a
        // good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 1;
        }

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }
}

File 7 of 12 : IERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.7;

/// @title Interface of the ERC20 standard as defined in the EIP, including EIP-2612 permit functionality.
interface IERC20 {

    /**************/
    /*** Events ***/
    /**************/

    /**
     *  @dev   Emitted when one account has set the allowance of another account over their tokens.
     *  @param owner_   Account that tokens are approved from.
     *  @param spender_ Account that tokens are approved for.
     *  @param amount_  Amount of tokens that have been approved.
     */
    event Approval(address indexed owner_, address indexed spender_, uint256 amount_);

    /**
     *  @dev   Emitted when tokens have moved from one account to another.
     *  @param owner_     Account that tokens have moved from.
     *  @param recipient_ Account that tokens have moved to.
     *  @param amount_    Amount of tokens that have been transferred.
     */
    event Transfer(address indexed owner_, address indexed recipient_, uint256 amount_);

    /**************************/
    /*** External Functions ***/
    /**************************/

    /**
     *  @dev    Function that allows one account to set the allowance of another account over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender_ Account that tokens are approved for.
     *  @param  amount_  Amount of tokens that have been approved.
     *  @return success_ Boolean indicating whether the operation succeeded.
     */
    function approve(address spender_, uint256 amount_) external returns (bool success_);

    /**
     *  @dev    Function that allows one account to decrease the allowance of another account over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender_          Account that tokens are approved for.
     *  @param  subtractedAmount_ Amount to decrease approval by.
     *  @return success_          Boolean indicating whether the operation succeeded.
     */
    function decreaseAllowance(address spender_, uint256 subtractedAmount_) external returns (bool success_);

    /**
     *  @dev    Function that allows one account to increase the allowance of another account over their tokens.
     *          Emits an {Approval} event.
     *  @param  spender_     Account that tokens are approved for.
     *  @param  addedAmount_ Amount to increase approval by.
     *  @return success_     Boolean indicating whether the operation succeeded.
     */
    function increaseAllowance(address spender_, uint256 addedAmount_) external returns (bool success_);

    /**
     *  @dev   Approve by signature.
     *  @param owner_    Owner address that signed the permit.
     *  @param spender_  Spender of the permit.
     *  @param amount_   Permit approval spend limit.
     *  @param deadline_ Deadline after which the permit is invalid.
     *  @param v_        ECDSA signature v component.
     *  @param r_        ECDSA signature r component.
     *  @param s_        ECDSA signature s component.
     */
    function permit(address owner_, address spender_, uint amount_, uint deadline_, uint8 v_, bytes32 r_, bytes32 s_) external;

    /**
     *  @dev    Moves an amount of tokens from `msg.sender` to a specified account.
     *          Emits a {Transfer} event.
     *  @param  recipient_ Account that receives tokens.
     *  @param  amount_    Amount of tokens that are transferred.
     *  @return success_   Boolean indicating whether the operation succeeded.
     */
    function transfer(address recipient_, uint256 amount_) external returns (bool success_);

    /**
     *  @dev    Moves a pre-approved amount of tokens from a sender to a specified account.
     *          Emits a {Transfer} event.
     *          Emits an {Approval} event.
     *  @param  owner_     Account that tokens are moving from.
     *  @param  recipient_ Account that receives tokens.
     *  @param  amount_    Amount of tokens that are transferred.
     *  @return success_   Boolean indicating whether the operation succeeded.
     */
    function transferFrom(address owner_, address recipient_, uint256 amount_) external returns (bool success_);

    /**********************/
    /*** View Functions ***/
    /**********************/

    /**
     *  @dev    Returns the allowance that one account has given another over their tokens.
     *  @param  owner_     Account that tokens are approved from.
     *  @param  spender_   Account that tokens are approved for.
     *  @return allowance_ Allowance that one account has given another over their tokens.
     */
    function allowance(address owner_, address spender_) external view returns (uint256 allowance_);

    /**
     *  @dev    Returns the amount of tokens owned by a given account.
     *  @param  account_ Account that owns the tokens.
     *  @return balance_ Amount of tokens owned by a given account.
     */
    function balanceOf(address account_) external view returns (uint256 balance_);

    /**
     *  @dev    Returns the decimal precision used by the token.
     *  @return decimals_ The decimal precision used by the token.
     */
    function decimals() external view returns (uint8 decimals_);

    /**
     *  @dev    Returns the signature domain separator.
     *  @return domainSeparator_ The signature domain separator.
     */
    function DOMAIN_SEPARATOR() external view returns (bytes32 domainSeparator_);

    /**
     *  @dev    Returns the name of the token.
     *  @return name_ The name of the token.
     */
    function name() external view returns (string memory name_);

    /**
      *  @dev    Returns the nonce for the given owner.
      *  @param  owner_  The address of the owner account.
      *  @return nonce_ The nonce for the given owner.
     */
    function nonces(address owner_) external view returns (uint256 nonce_);

    /**
     *  @dev    Returns the symbol of the token.
     *  @return symbol_ The symbol of the token.
     */
    function symbol() external view returns (string memory symbol_);

    /**
     *  @dev    Returns the total amount of tokens in existence.
     *  @return totalSupply_ The total amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256 totalSupply_);

}

File 8 of 12 : ERC20Helper.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

import { IERC20Like } from "./interfaces/IERC20Like.sol";

/**
 * @title Small Library to standardize erc20 token interactions.
 */
library ERC20Helper {

    /**************************/
    /*** Internal Functions ***/
    /**************************/

    function transfer(address token_, address to_, uint256 amount_) internal returns (bool success_) {
        return _call(token_, abi.encodeWithSelector(IERC20Like.transfer.selector, to_, amount_));
    }

    function transferFrom(address token_, address from_, address to_, uint256 amount_) internal returns (bool success_) {
        return _call(token_, abi.encodeWithSelector(IERC20Like.transferFrom.selector, from_, to_, amount_));
    }

    function approve(address token_, address spender_, uint256 amount_) internal returns (bool success_) {
        // If setting approval to zero fails, return false.
        if (!_call(token_, abi.encodeWithSelector(IERC20Like.approve.selector, spender_, uint256(0)))) return false;

        // If `amount_` is zero, return true as the previous step already did this.
        if (amount_ == uint256(0)) return true;

        // Return the result of setting the approval to `amount_`.
        return _call(token_, abi.encodeWithSelector(IERC20Like.approve.selector, spender_, amount_));
    }

    function _call(address token_, bytes memory data_) private returns (bool success_) {
        if (token_.code.length == uint256(0)) return false;

        bytes memory returnData;
        ( success_, returnData ) = token_.call(data_);

        return success_ && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
    }

}

File 9 of 12 : IRevenueDistributionToken.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.7;

import { IERC20 } from "erc20/interfaces/IERC20.sol";

import { IERC4626 } from "./IERC4626.sol";

/// @title A token that represents ownership of future revenues distributed linearly over time.
interface IRevenueDistributionToken is IERC20, IERC4626 {

    /**************/
    /*** Events ***/
    /**************/

    /**
     *  @dev   Issuance parameters have been updated after a `_mint` or `_burn`.
     *  @param freeAssets_   Resulting `freeAssets` (y-intercept) value after accounting update.
     *  @param issuanceRate_ The new issuance rate of `asset` until `vestingPeriodFinish_`.
     */
    event IssuanceParamsUpdated(uint256 freeAssets_, uint256 issuanceRate_);

    /**
     *  @dev   `newOwner_` has accepted the transferral of RDT ownership from `previousOwner_`.
     *  @param previousOwner_ The previous RDT owner.
     *  @param newOwner_      The new RDT owner.
     */
    event OwnershipAccepted(address indexed previousOwner_, address indexed newOwner_);

    /**
     *  @dev   `owner_` has set the new pending owner of RDT to `pendingOwner_`.
     *  @param owner_        The current RDT owner.
     *  @param pendingOwner_ The new pending RDT owner.
     */
    event PendingOwnerSet(address indexed owner_, address indexed pendingOwner_);

    /**
     *  @dev   `owner_` has updated the RDT vesting schedule to end at `vestingPeriodFinish_`.
     *  @param owner_               The current RDT owner.
     *  @param vestingPeriodFinish_ When the unvested balance will finish vesting.
     */
    event VestingScheduleUpdated(address indexed owner_, uint256 vestingPeriodFinish_);

    /***********************/
    /*** State Variables ***/
    /***********************/

    /**
     *  @dev The total amount of the underlying asset that is currently unlocked and is not time-dependent.
     *       Analogous to the y-intercept in a linear function.
     */
    function freeAssets() external view returns (uint256 freeAssets_);

    /**
     *  @dev The rate of issuance of the vesting schedule that is currently active.
     *       Denominated as the amount of underlying assets vesting per second.
     */
    function issuanceRate() external view returns (uint256 issuanceRate_);

    /**
     *  @dev The timestamp of when the linear function was last recalculated.
     *       Analogous to t0 in a linear function.
     */
    function lastUpdated() external view returns (uint256 lastUpdated_);

    /**
     *  @dev The address of the account that is allowed to update the vesting schedule.
     */
    function owner() external view returns (address owner_);

    /**
     *  @dev The next owner, nominated by the current owner.
     */
    function pendingOwner() external view returns (address pendingOwner_);

    /**
     *  @dev The precision at which the issuance rate is measured.
     */
    function precision() external view returns (uint256 precision_);

    /**
     *  @dev The end of the current vesting schedule.
     */
    function vestingPeriodFinish() external view returns (uint256 vestingPeriodFinish_);

    /********************************/
    /*** Administrative Functions ***/
    /********************************/

    /**
     *  @dev Sets the pending owner as the new owner.
     *       Can be called only by the pending owner, and only after their nomination by the current owner.
     */
    function acceptOwnership() external;

    /**
     *  @dev   Sets a new address as the pending owner.
     *  @param pendingOwner_ The address of the next potential owner.
     */
    function setPendingOwner(address pendingOwner_) external;

    /**
     *  @dev    Updates the current vesting formula based on the amount of total unvested funds in the contract and the new `vestingPeriod_`.
     *  @param  vestingPeriod_ The amount of time over which all currently unaccounted underlying assets will be vested over.
     *  @return issuanceRate_  The new issuance rate.
     *  @return freeAssets_    The new amount of underlying assets that are unlocked.
     */
    function updateVestingSchedule(uint256 vestingPeriod_) external returns (uint256 issuanceRate_, uint256 freeAssets_);

    /************************/
    /*** Staker Functions ***/
    /************************/

    /**
     *  @dev    Does a ERC4626 `deposit` with a ERC-2612 `permit`.
     *  @param  assets_   The amount of `asset` to deposit.
     *  @param  receiver_ The receiver of the shares.
     *  @param  deadline_ The timestamp after which the `permit` signature is no longer valid.
     *  @param  v_        ECDSA signature v component.
     *  @param  r_        ECDSA signature r component.
     *  @param  s_        ECDSA signature s component.
     *  @return shares_   The amount of shares minted.
     */
    function depositWithPermit(uint256 assets_, address receiver_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_) external returns (uint256 shares_);

    /**
     *  @dev    Does a ERC4626 `mint` with a ERC-2612 `permit`.
     *  @param  shares_    The amount of `shares` to mint.
     *  @param  receiver_  The receiver of the shares.
     *  @param  maxAssets_ The maximum amount of assets that can be taken, as per the permit.
     *  @param  deadline_  The timestamp after which the `permit` signature is no longer valid.
     *  @param  v_         ECDSA signature v component.
     *  @param  r_         ECDSA signature r component.
     *  @param  s_         ECDSA signature s component.
     *  @return assets_    The amount of shares deposited.
     */
    function mintWithPermit(uint256 shares_, address receiver_, uint256 maxAssets_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_) external returns (uint256 assets_);


    /**********************/
    /*** View Functions ***/
    /**********************/

    /**
     *  @dev    Returns the amount of underlying assets owned by the specified account.
     *  @param  account_ Address of the account.
     *  @return assets_  Amount of assets owned.
     */
    function balanceOfAssets(address account_) external view returns (uint256 assets_);

}

File 10 of 12 : ILockedRevenueDistributionToken.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.7;

interface ILockedRevenueDistributionToken {
    /**
     * @notice        Represents a withdrawal request, packed into a single word.
     * @custom:member unlockedAt Timestamp after which the withdrawal is unlocked.
     * @custom:member shares     Amount of shares to be burned upon withdrawal execution.
     * @custom:member assets     Amount of assets to be returned to user upon withdrawal execution.
     */
    struct WithdrawalRequest {
        uint32 unlockedAt;
        uint32 lockTime;
        uint96 shares;
        uint96 assets;
    }

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                              Events                               ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Emitted when burning shares upon withdrawal request cancellation.
     * @param  assets_ Amount of assets returned to contract address.
     * @param  shares_ Share delta between withdrawal request creation and cancellation.
     */
    event CancellationBurn(uint256 assets_, uint256 shares_);

    /**
     * @notice Emitted when the instant withdrawal fee is set.
     * @param  percentage_ A percentage value from 0 to 100.
     */
    event InstantWithdrawalFeeChanged(uint256 percentage_);

    /**
     * @notice Emitted when time-to-unlock for a standard withdrawal set.
     * @param  lockTime_ Integer length of lock time, e.g. `26 weeks`.
     */
    event LockTimeChanged(uint256 lockTime_);

    /**
     * @notice Emitted when redistributing rewards upon early execution or cancellation of a withdrawal request.
     * @param  assets_ Assets redistributed to remaining stakers.
     */
    event Redistribute(uint256 assets_);

    /**
     * @notice Emitted when refunding shares upon withdrawal request cancellation.
     * @param  receiver_   Account to refund shares to at spot rate.
     * @param  assets_     Equivalent asset value for shares returned.
     * @param  shares_     Amount of shares returned to the receiver.
     */
    event Refund(address indexed receiver_, uint256 assets_, uint256 shares_);

    /**
     * @notice Emitted when fee exemption status has been set for an address.
     * @param  account_ Address in which to apply the exemption.
     * @param  status_  True for exempt, false to remove exemption.
     */
    event WithdrawalFeeExemptionStatusChanged(address indexed account_, bool status_);

    /**
     * @notice Emitted when an instant withdrawal fee is paid.
     * @param  caller_   The caller of the `redeem` or `withdraw` function.
     * @param  receiver_ The receiver of the assets.
     * @param  owner_    The owner of the shares or withdrawal request.
     * @param  fee_      The assets paid as fee.
     */
    event WithdrawalFeePaid(address indexed caller_, address indexed receiver_, address indexed owner_, uint256 fee_);

    /**
     * @notice Emitted when a new withdrawal request has been created for an account.
     * @param  request_ Struct containing shares, assets, and maturity date of the created request.
     * @param  pos_   Index/position of the withdrawal request created.
     */
    event WithdrawalRequestCreated(WithdrawalRequest request_, uint256 pos_);

    /**
     * @notice Emitted when an account cancels any existing withdrawal requests.
     * @param  pos_   Index/position of the withdrawal request cancelled.
     */
    event WithdrawalRequestCancelled(uint256 pos_);

    /**
     * @notice Emitted when a withdrawal request has been executed with shares burned and assets withdrawn.
     * @param  pos_ Index/position of the withdrawal request executed.
     */
    event WithdrawalRequestExecuted(uint256 pos_);

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                          State Variables                          ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Constant maximum lock time able to be set using `setLockTime` to avoid permanent lockup.
     * @return maximumLockTime_ Maxmimum lock time integer length, e.g. `104 weeks`.
     */
    function MAXIMUM_LOCK_TIME() external view returns (uint256 maximumLockTime_);

    /**
     * @notice Constant vesting period, used in `updateVestingSchedule`.
     * @return vestingPeriod_ Fixed vesting period, e.g. `2 weeks`.
     */
    function VESTING_PERIOD() external view returns (uint256 vestingPeriod_);

    /**
     * @notice Constant time window in which unlocked withdrawal requests can be executed.
     * @return withdrawalWindow_ Fixed withdrawal window, e.g. `4 weeks`.
     */
    function WITHDRAWAL_WINDOW() external view returns (uint256 withdrawalWindow_);

    /**
     * @notice Percentage withdrawal fee to be applied to instant withdrawals.
     * @return instantWithdrawalFee_ A percentage value from 0 to 100.
     */
    function instantWithdrawalFee() external view returns (uint256 instantWithdrawalFee_);

    /**
     * @notice The lock time set for standard withdrawals to become unlocked.
     * @return lockTime_ Length of lock of a standard withdrawal request, e.g. `26 weeks`.
     */
    function lockTime() external view returns (uint256 lockTime_);

    /**
     * @notice Returns exemption status for a given account. When true then instant withdrawal fee will not apply.
     * @param  account_ Account to check for exemption.
     * @return status_  Exemption status.
     */
    function withdrawalFeeExemptions(address account_) external view returns (bool status_);

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                     Administrative Functions                      ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Sets the intstant withdrawal fee, applied when making instant withdrawals or redemptions.
     * @notice Can only be set by owner.
     * @param  percentage_ Fee percentage. Must be an integer between 0 and 100 inclusive.
     */
    function setInstantWithdrawalFee(uint256 percentage_) external;

    /**
     * @notice Sets the lock time for standard withdrawals to become unlocked.
     * @notice Can only be set by owner.
     * @notice Must be lower than MAXIMUM_LOCK_TIME to prevent permanent lockup.
     * @param  lockTime_ Length of lock of a standard withdrawal request.
     */
    function setLockTime(uint256 lockTime_) external;

    /**
     * @notice Sets or unsets an owner address to be exempt from the withdrawal fee.
     * @notice Useful in case of future migrations where an approved contract may be given permission to migrate
     * balances to a new token. Can also be used to exempt third-party vaults from facing withdrawal fee when
     * managing balances, such as lending platform liquidations.
     * @notice Can only be set by contract `owner`.
     * @dev    The zero address cannot be set as exmempt as this will always represent an address that pays fees.
     * @param  owner_  Owner address to exempt from instant withdrawal fees.
     * @param  status_ true to add exemption, false to remove exemption.
     */
    function setWithdrawalFeeExemption(address owner_, bool status_) external;

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                         Public Functions                          ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Creates a new withdrawal request for future execution using the shares conversion at the point of
     * request. May only be executed after the unlock date.
     * @notice Transfers shares to the vault contract to reserve them, reducing share balance.
     * @param  shares_ Amount of shares to redeem upon unlock.
     */
    function createWithdrawalRequest(uint256 shares_) external;

    /**
     * @notice Removes an open withdrawal request for the sender.
     * @param  pos_ Index/position of the withdrawal request to be cancelled.
     */
    function cancelWithdrawalRequest(uint256 pos_) external;

    /**
     * @notice Executes an existing withdrawal request for msg.sender. Before the request is unlocked, a percentage
     * fee will be paid, equal to a percentage of the instantWithdrawalFee by time elapsed of the request.
     * @param  pos_ Index/position of the withdrawal request to be executed.
     */
    function executeWithdrawalRequest(uint256 pos_) external;

    /**
     * @notice Executes an existing withdrawal request that has passed its unlock date.
     * @dev    Identical to parent implementation but made public by fixed vesting period and removal of owner check.
     * @return issuanceRate_ Slope of release of newly added assets, scaled up by `precision`.
     * @return freeAssets_   Amount of assets currently released to stakers.
     */
    function updateVestingSchedule() external returns (uint256 issuanceRate_, uint256 freeAssets_);

    /**
     * @notice ERC5143 slippage-protected deposit method. The transaction will revert if the shares to be returned is
     * less than minShares_.
     * @param  assets_    Amount of assets to deposit.
     * @param  receiver_  The receiver of the shares.
     * @param  minShares_ Minimum amount of shares to be returned.
     * @return shares_    Amount of shares returned to receiver_.
     */
    function deposit(uint256 assets_, address receiver_, uint256 minShares_) external returns (uint256 shares_);

    /**
     * @notice ERC5143 slippage-protected mint method. The transaction will revert if the assets to be deducted is
     * greater than maxAssets_.
     * @param  shares_    Amount of shares to mint.
     * @param  receiver_  The receiver of the shares.
     * @param  maxAssets_ Maximum amount of assets to be deducted.
     * @return assets_    Amount of deducted when minting shares.
     */
    function mint(uint256 shares_, address receiver_, uint256 maxAssets_) external returns (uint256 assets_);

    /**
     * @notice ERC5143 slippage-protected redeem method. The transaction will revert if the assets to be returned is
     * less than minAssets_.
     * @param  shares_    Amount of shares to redeem.
     * @param  receiver_  The receiver of the assets.
     * @param  owner_     Owner of shares making redemption.
     * @param  minAssets_ Minimum amount of assets to be returned.
     * @return assets_    Amount of assets returned.
     */
    function redeem(uint256 shares_, address receiver_, address owner_, uint256 minAssets_)
        external
        returns (uint256 assets_);

    /**
     * @notice ERC5143 slippage-protected withdraw method. The transaction will revert if the shares to be deducted is
     * greater than maxShares_.
     * @param  assets_    Amount of assets to withdraw.
     * @param  receiver_  The receiver of the assets.
     * @param  owner_     Owner of shares making withdrawal.
     * @param  maxShares_ Minimum amount of shares to be deducted.
     * @return shares_    Amount of shares deducted.
     */
    function withdraw(uint256 assets_, address receiver_, address owner_, uint256 maxShares_)
        external
        returns (uint256 shares_);

    /*░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
    ░░░░                          View Functions                           ░░░░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░*/

    /**
     * @notice Previews a redemption of shares for owner. Applies withdrawal fee if owner does not have an exemption.
     * @param  owner_  Owner of shares making redemption.
     * @param  shares_ Amount of shares to redeem.
     * @return assets_ Assets redeemed for shares for owner.
     * @param  fee_    The assets paid as fee.
     */
    function previewRedeem(uint256 shares_, address owner_) external view returns (uint256 assets_, uint256 fee_);

    /**
     * @notice Previews a withdrawal of assets for owner. Applies withdrawal fee if owner does not have an exemption.
     * @param  owner_  Owner of shares makeing withdrawal.
     * @param  assets_ Amount of assets to withdraw.
     * @return shares_ Shares needed to be burned for owner.
     * @param  fee_    The assets paid as fee.
     */
    function previewWithdraw(uint256 assets_, address owner_) external view returns (uint256 shares_, uint256 fee_);

    /**
     * @notice Previews a withdrawal request execution, calculating the assets returned to the receiver and fee paid.
     * @notice Fee percentage reduces linearly from instantWithdrawalFee until 0 at the unlockedAt timestamp.
     * @param  pos_     Index/position of the withdrawal request to be previewed.
     * @param  owner_   Owner of the withdrawal request.
     * @return request_ The WithdrawalRequest struct within storage.
     * @return assets_  Amount of assets returned to owner if withdrawn.
     * @return fee_     The assets paid as fee.
     */
    function previewWithdrawalRequest(uint256 pos_, address owner_)
        external
        view
        returns (WithdrawalRequest memory request_, uint256 assets_, uint256 fee_);

    /**
     * @notice Returns a count of the number of created withdrawal requests for an account, including cancelled.
     * @param  owner_ Account address of owner of withdrawal requests.
     * @return count_ Number of withdrawal request created for owner account.
     */
    function withdrawalRequestCount(address owner_) external view returns (uint256 count_);

    /**
     * @notice Returns an array of created withdrawal requests for an account, including cancelled.
     * @param  owner_    Account address of owner of withdrawal requests.
     * @return requests_ Array of withdrawal request structs for an owner.
     */
    function withdrawalRequests(address owner_) external view returns (WithdrawalRequest[] memory requests_);

    /**
     * @notice Returns existing withdrawal request for a given account.
     * @param  account_ Account address holding withdrawal request.
     * @param  pos_     Index/position of the withdrawal request in the array.
     * @return request_ Withdrawal request struct found at position for owner.
     */
    function withdrawalRequests(address account_, uint256 pos_)
        external
        view
        returns (WithdrawalRequest memory request_);
}

File 11 of 12 : IERC20Like.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

/// @title Interface of the ERC20 standard as needed by ERC20Helper.
interface IERC20Like {

    function approve(address spender_, uint256 amount_) external returns (bool success_);

    function transfer(address recipient_, uint256 amount_) external returns (bool success_);

    function transferFrom(address owner_, address recipient_, uint256 amount_) external returns (bool success_);

}

File 12 of 12 : IERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.7;

import { IERC20 } from "erc20/interfaces/IERC20.sol";

/// @title A standard for tokenized Vaults with a single underlying ERC-20 token.
interface IERC4626 is IERC20 {

    /**************/
    /*** Events ***/
    /**************/

    /**
     *  @dev   `caller_` has exchanged `assets_` for `shares_` and transferred them to `owner_`.
     *         MUST be emitted when assets are deposited via the `deposit` or `mint` methods.
     *  @param caller_ The caller of the function that emitted the `Deposit` event.
     *  @param owner_  The owner of the shares.
     *  @param assets_ The amount of assets deposited.
     *  @param shares_ The amount of shares minted.
     */
    event Deposit(address indexed caller_, address indexed owner_, uint256 assets_, uint256 shares_);

    /**
     *  @dev   `caller_` has exchanged `shares_`, owned by `owner_`, for `assets_`, and transferred them to `receiver_`.
     *         MUST be emitted when assets are withdrawn via the `withdraw` or `redeem` methods.
     *  @param caller_   The caller of the function that emitted the `Withdraw` event.
     *  @param receiver_ The receiver of the assets.
     *  @param owner_    The owner of the shares.
     *  @param assets_   The amount of assets withdrawn.
     *  @param shares_   The amount of shares burned.
     */
    event Withdraw(address indexed caller_, address indexed receiver_, address indexed owner_, uint256 assets_, uint256 shares_);

    /***********************/
    /*** State Variables ***/
    /***********************/

    /**
     *  @dev    The address of the underlying asset used by the Vault.
     *          MUST be a contract that implements the ERC-20 standard.
     *          MUST NOT revert.
     *  @return asset_ The address of the underlying asset.
     */
    function asset() external view returns (address asset_);

    /********************************/
    /*** State Changing Functions ***/
    /********************************/

    /**
     *  @dev    Mints `shares_` to `receiver_` by depositing `assets_` into the Vault.
     *          MUST emit the {Deposit} event.
     *          MUST revert if all of the assets cannot be deposited (due to insufficient approval, deposit limits, slippage, etc).
     *  @param  assets_   The amount of assets to deposit.
     *  @param  receiver_ The receiver of the shares.
     *  @return shares_   The amount of shares minted.
     */
    function deposit(uint256 assets_, address receiver_) external returns (uint256 shares_);

    /**
     *  @dev    Mints `shares_` to `receiver_` by depositing `assets_` into the Vault.
     *          MUST emit the {Deposit} event.
     *          MUST revert if all of shares cannot be minted (due to insufficient approval, deposit limits, slippage, etc).
     *  @param  shares_   The amount of shares to mint.
     *  @param  receiver_ The receiver of the shares.
     *  @return assets_   The amount of assets deposited.
     */
    function mint(uint256 shares_, address receiver_) external returns (uint256 assets_);

    /**
     *  @dev    Burns `shares_` from `owner_` and sends `assets_` to `receiver_`.
     *          MUST emit the {Withdraw} event.
     *          MUST revert if all of the shares cannot be redeemed (due to insufficient shares, withdrawal limits, slippage, etc).
     *  @param  shares_   The amount of shares to redeem.
     *  @param  receiver_ The receiver of the assets.
     *  @param  owner_    The owner of the shares.
     *  @return assets_   The amount of assets sent to the receiver.
     */
    function redeem(uint256 shares_, address receiver_, address owner_) external returns (uint256 assets_);

    /**
     *  @dev    Burns `shares_` from `owner_` and sends `assets_` to `receiver_`.
     *          MUST emit the {Withdraw} event.
     *          MUST revert if all of the assets cannot be withdrawn (due to insufficient assets, withdrawal limits, slippage, etc).
     *  @param  assets_   The amount of assets to withdraw.
     *  @param  receiver_ The receiver of the assets.
     *  @param  owner_    The owner of the assets.
     *  @return shares_   The amount of shares burned from the owner.
     */
    function withdraw(uint256 assets_, address receiver_, address owner_) external returns (uint256 shares_);

    /**********************/
    /*** View Functions ***/
    /**********************/

    /**
     *  @dev    The amount of `assets_` the `shares_` are currently equivalent to.
     *          MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     *          MUST NOT reflect slippage or other on-chain conditions when performing the actual exchange.
     *          MUST NOT show any variations depending on the caller.
     *          MUST NOT revert.
     *  @param  shares_ The amount of shares to convert.
     *  @return assets_ The amount of equivalent assets.
     */
    function convertToAssets(uint256 shares_) external view returns (uint256 assets_);

    /**
     *  @dev    The amount of `shares_` the `assets_` are currently equivalent to.
     *          MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     *          MUST NOT reflect slippage or other on-chain conditions when performing the actual exchange.
     *          MUST NOT show any variations depending on the caller.
     *          MUST NOT revert.
     *  @param  assets_ The amount of assets to convert.
     *  @return shares_ The amount of equivalent shares.
     */
    function convertToShares(uint256 assets_) external view returns (uint256 shares_);

    /**
     *  @dev    Maximum amount of `assets_` that can be deposited on behalf of the `receiver_` through a `deposit` call.
     *          MUST return a limited value if the receiver is subject to any limits, or the maximum value otherwise.
     *          MUST NOT revert.
     *  @param  receiver_ The receiver of the assets.
     *  @return assets_   The maximum amount of assets that can be deposited.
     */
    function maxDeposit(address receiver_) external view returns (uint256 assets_);

    /**
     *  @dev    Maximum amount of `shares_` that can be minted on behalf of the `receiver_` through a `mint` call.
     *          MUST return a limited value if the receiver is subject to any limits, or the maximum value otherwise.
     *          MUST NOT revert.
     *  @param  receiver_ The receiver of the shares.
     *  @return shares_   The maximum amount of shares that can be minted.
     */
    function maxMint(address receiver_) external view returns (uint256 shares_);

    /**
     *  @dev    Maximum amount of `shares_` that can be redeemed from the `owner_` through a `redeem` call.
     *          MUST return a limited value if the owner is subject to any limits, or the total amount of owned shares otherwise.
     *          MUST NOT revert.
     *  @param  owner_  The owner of the shares.
     *  @return shares_ The maximum amount of shares that can be redeemed.
     */
    function maxRedeem(address owner_) external view returns (uint256 shares_);

    /**
     *  @dev    Maximum amount of `assets_` that can be withdrawn from the `owner_` through a `withdraw` call.
     *          MUST return a limited value if the owner is subject to any limits, or the total amount of owned assets otherwise.
     *          MUST NOT revert.
     *  @param  owner_  The owner of the assets.
     *  @return assets_ The maximum amount of assets that can be withdrawn.
     */
    function maxWithdraw(address owner_) external view returns (uint256 assets_);

    /**
     *  @dev    Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
     *          MUST return as close to and no more than the exact amount of shares that would be minted in a `deposit` call in the same transaction.
     *          MUST NOT account for deposit limits like those returned from `maxDeposit` and should always act as though the deposit would be accepted.
     *          MUST NOT revert.
     *  @param  assets_ The amount of assets to deposit.
     *  @return shares_ The amount of shares that would be minted.
     */
    function previewDeposit(uint256 assets_) external view returns (uint256 shares_);

    /**
     *  @dev    Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
     *          MUST return as close to and no fewer than the exact amount of assets that would be deposited in a `mint` call in the same transaction.
     *          MUST NOT account for mint limits like those returned from `maxMint` and should always act as though the minting would be accepted.
     *          MUST NOT revert.
     *  @param  shares_ The amount of shares to mint.
     *  @return assets_ The amount of assets that would be deposited.
     */
    function previewMint(uint256 shares_) external view returns (uint256 assets_);

    /**
     *  @dev    Allows an on-chain or off-chain user to simulate the effects of their redemption at the current block, given current on-chain conditions.
     *          MUST return as close to and no more than the exact amount of assets that would be withdrawn in a `redeem` call in the same transaction.
     *          MUST NOT account for redemption limits like those returned from `maxRedeem` and should always act as though the redemption would be accepted.
     *          MUST NOT revert.
     *  @param  shares_ The amount of shares to redeem.
     *  @return assets_ The amount of assets that would be withdrawn.
     */
    function previewRedeem(uint256 shares_) external view returns (uint256 assets_);

    /**
     *  @dev    Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
     *          MUST return as close to and no fewer than the exact amount of shares that would be burned in a `withdraw` call in the same transaction.
     *          MUST NOT account for withdrawal limits like those returned from `maxWithdraw` and should always act as though the withdrawal would be accepted.
     *          MUST NOT revert.
     *  @param  assets_ The amount of assets to withdraw.
     *  @return shares_ The amount of shares that would be redeemed.
     */
    function previewWithdraw(uint256 assets_) external view returns (uint256 shares_);

    /**
     *  @dev    Total amount of the underlying asset that is managed by the Vault.
     *          SHOULD include compounding that occurs from any yields.
     *          MUST NOT revert.
     *  @return totalAssets_ The total amount of assets the Vault manages.
     */
    function totalAssets() external view returns (uint256 totalAssets_);

}

Settings
{
  "remappings": [
    "@solmate/src/=lib/solmate/src/",
    "forge-std/=lib/forge-std/src/",
    "contract-test-utils/=lib/erc20-helper/lib/contract-test-utils/contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc20-helper/=lib/erc20-helper/src/",
    "erc20/=lib/erc20/contracts/",
    "revenue-distribution-token/=lib/revenue-distribution-token/contracts/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"asset_","type":"address"},{"internalType":"uint256","name":"precision_","type":"uint256"},{"internalType":"uint256","name":"instantWithdrawalFee_","type":"uint256"},{"internalType":"uint256","name":"lockTime_","type":"uint256"},{"internalType":"uint256","name":"initialSeed_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner_","type":"address"},{"indexed":true,"internalType":"address","name":"spender_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assets_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"CancellationBurn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator_","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate_","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate_","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate_","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance_","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller_","type":"address"},{"indexed":true,"internalType":"address","name":"owner_","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"percentage_","type":"uint256"}],"name":"InstantWithdrawalFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"freeAssets_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"issuanceRate_","type":"uint256"}],"name":"IssuanceParamsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lockTime_","type":"uint256"}],"name":"LockTimeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner_","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner_","type":"address"}],"name":"OwnershipAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner_","type":"address"},{"indexed":true,"internalType":"address","name":"pendingOwner_","type":"address"}],"name":"PendingOwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assets_","type":"uint256"}],"name":"Redistribute","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver_","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner_","type":"address"},{"indexed":true,"internalType":"address","name":"recipient_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner_","type":"address"},{"indexed":false,"internalType":"uint256","name":"vestingPeriodFinish_","type":"uint256"}],"name":"VestingScheduleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller_","type":"address"},{"indexed":true,"internalType":"address","name":"receiver_","type":"address"},{"indexed":true,"internalType":"address","name":"owner_","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account_","type":"address"},{"indexed":false,"internalType":"bool","name":"status_","type":"bool"}],"name":"WithdrawalFeeExemptionStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller_","type":"address"},{"indexed":true,"internalType":"address","name":"receiver_","type":"address"},{"indexed":true,"internalType":"address","name":"owner_","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee_","type":"uint256"}],"name":"WithdrawalFeePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pos_","type":"uint256"}],"name":"WithdrawalRequestCancelled","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint32","name":"unlockedAt","type":"uint32"},{"internalType":"uint32","name":"lockTime","type":"uint32"},{"internalType":"uint96","name":"shares","type":"uint96"},{"internalType":"uint96","name":"assets","type":"uint96"}],"indexed":false,"internalType":"struct ILockedRevenueDistributionToken.WithdrawalRequest","name":"request_","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"pos_","type":"uint256"}],"name":"WithdrawalRequestCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pos_","type":"uint256"}],"name":"WithdrawalRequestExecuted","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"domainSeparator_","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAXIMUM_LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VESTING_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAWAL_WINDOW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"balanceOfAssets","outputs":[{"internalType":"uint256","name":"balanceOfAssets_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pos_","type":"uint256"}],"name":"cancelWithdrawalRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint32","name":"pos_","type":"uint32"}],"name":"checkpoints","outputs":[{"internalType":"uint32","name":"fromBlock_","type":"uint32"},{"internalType":"uint96","name":"votes_","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"blockNumber_","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"createWithdrawalRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"subtractedAmount_","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee_","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee_","type":"address"},{"internalType":"uint256","name":"nonce_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"minShares_","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"depositWithPermit","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pos_","type":"uint256"}],"name":"executeWithdrawalRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freeAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"getCurrentVotes","outputs":[{"internalType":"uint96","name":"votes_","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber_","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"totalSupply_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"blockNumber_","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"votes_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"blockNumber_","type":"uint256"}],"name":"getPriorVotes","outputs":[{"internalType":"uint96","name":"votes_","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"votes_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"addedAmount_","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"instantWithdrawalFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"issuanceRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"maxAssets_","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"maxShares_","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"maxShares_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"maxAssets_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"maxAssets_","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"maxAssets_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"mintWithPermit","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"numCheckpoints_","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"precision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"address","name":"owner_","type":"address"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"uint256","name":"fee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"address","name":"owner_","type":"address"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"fee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pos_","type":"uint256"},{"internalType":"address","name":"owner_","type":"address"}],"name":"previewWithdrawalRequest","outputs":[{"components":[{"internalType":"uint32","name":"unlockedAt","type":"uint32"},{"internalType":"uint32","name":"lockTime","type":"uint32"},{"internalType":"uint96","name":"shares","type":"uint96"},{"internalType":"uint96","name":"assets","type":"uint96"}],"internalType":"struct ILockedRevenueDistributionToken.WithdrawalRequest","name":"request_","type":"tuple"},{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"uint256","name":"fee_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"uint256","name":"minAssets_","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"percentage_","type":"uint256"}],"name":"setInstantWithdrawalFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lockTime_","type":"uint256"}],"name":"setLockTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner_","type":"address"}],"name":"setPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"bool","name":"status_","type":"bool"}],"name":"setWithdrawalFeeExemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"totalManagedAssets_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success_","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateVestingSchedule","outputs":[{"internalType":"uint256","name":"issuanceRate_","type":"uint256"},{"internalType":"uint256","name":"freeAssets_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vestingPeriod_","type":"uint256"}],"name":"updateVestingSchedule","outputs":[{"internalType":"uint256","name":"issuanceRate_","type":"uint256"},{"internalType":"uint256","name":"freeAssets_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userCheckpoints","outputs":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint96","name":"shares","type":"uint96"},{"internalType":"uint96","name":"assets","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingPeriodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"uint256","name":"maxShares_","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets_","type":"uint256"},{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawalFeeExemptions","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"withdrawalRequestCount","outputs":[{"internalType":"uint256","name":"count_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"withdrawalRequests","outputs":[{"components":[{"internalType":"uint32","name":"unlockedAt","type":"uint32"},{"internalType":"uint32","name":"lockTime","type":"uint32"},{"internalType":"uint96","name":"shares","type":"uint96"},{"internalType":"uint96","name":"assets","type":"uint96"}],"internalType":"struct ILockedRevenueDistributionToken.WithdrawalRequest[]","name":"withdrawalRequests_","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"pos_","type":"uint256"}],"name":"withdrawalRequests","outputs":[{"components":[{"internalType":"uint32","name":"unlockedAt","type":"uint32"},{"internalType":"uint32","name":"lockTime","type":"uint32"},{"internalType":"uint96","name":"shares","type":"uint96"},{"internalType":"uint96","name":"assets","type":"uint96"}],"internalType":"struct ILockedRevenueDistributionToken.WithdrawalRequest","name":"withdrawalRequest_","type":"tuple"}],"stateMutability":"view","type":"function"}]

6101006040526001600d553480156200001757600080fd5b5060405162004f2938038062004f298339810160408190526200003a916200069e565b878787878787878787878787878484836001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156200008357600080fd5b505afa15801562000098573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000be919062000754565b8251620000d39060009060208601906200051f565b508151620000e99060019060208501906200051f565b507fff0000000000000000000000000000000000000000000000000000000000000060f882901b166080524660a052620001226200034d565b60c0525050600780546001600160a01b0319166001600160a01b038616908117909155151590506200019b5760405162461bcd60e51b815260206004820152601860248201527f5244543a433a4f574e45525f5a45524f5f41444452455353000000000000000060448201526064015b60405180910390fd5b600680546001600160a01b0319166001600160a01b03939093169290921790915560e052505050600e839055600f82905580156200033757600033905060008260026000828254620001ee91906200083b565b90915550506001600160a01b0381166000818152600360209081526040808320805488019055518681527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3600983905560408051848152602081018590526001600160a01b0380841692908516917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a360095460408051918252600060208301527f68b521a89bf844ff03e484d89fd64ce292a698ec522170f0dad7ecd11c2dc8fa910160405180910390a1620002e687333086620003fe60201b62002dda1760201c565b620003345760405162461bcd60e51b815260206004820152601460248201527f4c5244543a433a5452414e534645525f46524f4d000000000000000000000000604482015260640162000192565b50505b50505050505050505050505050505050620008e8565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405162000381919062000797565b60408051918290038220828201825260018352603160f81b6020938401528151928301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b039081166323b872dd60e01b1790915260009162000462918791906200046b16565b95945050505050565b60006001600160a01b0383163b620004865750600062000519565b6060836001600160a01b031683604051620004a2919062000779565b6000604051808303816000865af19150503d8060008114620004e1576040519150601f19603f3d011682016040523d82523d6000602084013e620004e6565b606091505b509092509050818015620005155750805115806200051557508080602001905181019062000515919062000673565b9150505b92915050565b8280546200052d9062000895565b90600052602060002090601f0160209004810192826200055157600085556200059c565b82601f106200056c57805160ff19168380011785556200059c565b828001600101855582156200059c579182015b828111156200059c5782518255916020019190600101906200057f565b50620005aa929150620005ae565b5090565b5b80821115620005aa5760008155600101620005af565b80516001600160a01b0381168114620005dd57600080fd5b919050565b600082601f830112620005f457600080fd5b81516001600160401b0380821115620006115762000611620008d2565b604051601f8301601f19908116603f011681019082821181831017156200063c576200063c620008d2565b816040528381528660208588010111156200065657600080fd5b6200066984602083016020890162000862565b9695505050505050565b6000602082840312156200068657600080fd5b815180151581146200069757600080fd5b9392505050565b600080600080600080600080610100898b031215620006bc57600080fd5b88516001600160401b0380821115620006d457600080fd5b620006e28c838d01620005e2565b995060208b0151915080821115620006f957600080fd5b50620007088b828c01620005e2565b9750506200071960408a01620005c5565b95506200072960608a01620005c5565b94506080890151935060a0890151925060c0890151915060e089015190509295985092959890939650565b6000602082840312156200076757600080fd5b815160ff811681146200069757600080fd5b600082516200078d81846020870162000862565b9190910192915050565b600080835481600182811c915080831680620007b457607f831692505b6020808410821415620007d557634e487b7160e01b86526022600452602486fd5b818015620007ec5760018114620007fe576200082d565b60ff198616895284890196506200082d565b60008a81526020902060005b86811015620008255781548b8201529085019083016200080a565b505084890196505b509498975050505050505050565b600082198211156200085d57634e487b7160e01b600052601160045260246000fd5b500190565b60005b838110156200087f57818101518382015260200162000865565b838111156200088f576000848401525b50505050565b600181811c90821680620008aa57607f821691505b60208210811415620008cc57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b60805160f81c60a05160c05160e0516145da6200094f600039600081816109eb01528181610b9101528181610e1801528181611a3301528181611a7b01528181611aa701526126a90152600061116501526000611130015260006105c701526145da6000f3fe608060405234801561001057600080fd5b506004361061046a5760003560e01c8063836a10401161024c578063c3cda52011610146578063dd62ed3e116100c3578063ec5de22811610087578063ec5de22814610acb578063ef8b30f714610ade578063f00aa90a14610af1578063f1127ed814610b04578063fab0079214610b3b57600080fd5b8063dd62ed3e14610a5c578063e13aa99014610a87578063e1deded114610a9a578063e1ef57ad14610aad578063e30c397814610ab857600080fd5b8063d0b06f5d1161010a578063d0b06f5d146109dd578063d3b5dc3b146109e6578063d505accf14610a0d578063d905777e14610a20578063dc02bde314610a4957600080fd5b8063c3cda52014610991578063c42069ec146109a4578063c63d75b614610666578063c6e6f592146109b7578063ce96cb77146109ca57600080fd5b8063a318c1a4116101d4578063b3d7f6b911610198578063b3d7f6b914610932578063b460af9414610945578063b4b5ea5714610958578063ba0876521461096b578063bc157ac11461097e57600080fd5b8063a318c1a4146108bd578063a457c2d7146108d0578063a9059cbb146108e3578063ade3142f146108f6578063ae04d45d1461091f57600080fd5b806393f3f1b61161021b57806393f3f1b61461085a57806394bf804d1461087c57806395d89b411461088f5780639ab24eb0146108975780639f40a7b3146108aa57600080fd5b8063836a10401461080e5780638da5cb5b146108215780638e539e8c146108345780639159b2061461084757600080fd5b80633a46b1a811610368578063587cde1e116102e55780636fcfff45116102a95780636fcfff451461077357806370a082311461079b578063782d6fe1146107bb57806379ba5097146107e65780637ecebe00146107ee57600080fd5b8063587cde1e146107085780635c19a95c1461073157806360dd37d9146107445780636b34128c146107575780636e553f651461076057600080fd5b80634823deea1161032c5780634823deea146106955780634cdad506146106b85780634e5a2328146106cb57806350921b23146106eb57806356765c51146106fe57600080fd5b80633a46b1a8146106415780633c2f7773146106545780633c9ae2ba1461065d578063402d267d1461066657806345b05d091461068057600080fd5b806314149bcb116103f657806327b380f3116103ba57806327b380f3146105a2578063313ce567146105c25780633644e515146105fb57806338d52e0f14610603578063395093511461062e57600080fd5b806314149bcb1461054357806318160ddd14610560578063181e7b3b146105695780631daea4431461057c57806323b872dd1461058f57600080fd5b8063094383201161043d57806309438320146104bc578063095ea7b3146104fb5780630a28a4771461051e5780630d6680871461053157806311f240ac1461053a57600080fd5b80630197d9721461046f57806301e1d1141461048c57806306fdde031461049457806307a2d13a146104a9575b600080fd5b6104796212750081565b6040519081526020015b60405180910390f35b610479610b4e565b61049c610bd7565b6040516104839190614389565b6104796104b736600461407e565b610c65565b6104cf6104ca366004613faa565b610c9c565b6040805163ffffffff90941684526001600160601b039283166020850152911690820152606001610483565b61050e610509366004613faa565b610cec565b6040519015158152602001610483565b61047961052c36600461407e565b610d03565b610479600f5481565b61047960095481565b61054b610d17565b60408051928352602083019190915201610483565b61047960025481565b6104796105773660046141e1565b610f8a565b61054b61058a3660046140b0565b610fdd565b61050e61059d366004613ecd565b61105d565b6105b56105b0366004613e7f565b61107f565b604051610483919061433b565b6105e97f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610483565b61047961112c565b600654610616906001600160a01b031681565b6040516001600160a01b039091168152602001610483565b61050e61063c366004613faa565b611187565b61047961064f366004613faa565b6111c3565b610479600c5481565b610479600a5481565b610479610674366004613e7f565b506001600160601b0390565b61069361068e36600461407e565b61124c565b005b61050e6106a3366004613e7f565b60116020526000908152604090205460ff1681565b6104796106c636600461407e565b611488565b6106de6106d9366004613faa565b611495565b604051610483919061440f565b6104796106f93660046141a3565b61153f565b6104796224ea0081565b610616610716366004613e7f565b6012602052600090815260409020546001600160a01b031681565b61069361073f366004613e7f565b6115fc565b610479610752366004614178565b611609565b610479600e5481565b61047961076e3660046140b0565b611717565b610786610781366004613e7f565b611762565b60405163ffffffff9091168152602001610483565b6104796107a9366004613e7f565b60036020526000908152604090205481565b6107ce6107c9366004613faa565b611784565b6040516001600160601b039091168152602001610483565b610693611798565b6104796107fc366004613e7f565b60056020526000908152604090205481565b61047961081c366004614153565b61183a565b600754610616906001600160a01b031681565b61047961084236600461407e565b611898565b610479610855366004613e7f565b6118fc565b61086d6108683660046140b0565b61191e565b60405161048393929190614438565b61047961088a3660046140b0565b611b20565b61049c611b60565b6104796108a5366004613e7f565b611b6d565b6104796108b836600461410f565b611bef565b6104796108cb36600461410f565b611c4e565b61050e6108de366004613faa565b611cad565b61050e6108f1366004613faa565b611cba565b610479610904366004613e7f565b6001600160a01b031660009081526010602052604090205490565b61069361092d36600461407e565b611cc7565b61047961094036600461407e565b611d7a565b6104796109533660046140d3565b611da1565b6107ce610966366004613e7f565b611e53565b6104796109793660046140d3565b611e61565b61047961098c366004614153565b611ea8565b61069361099f366004613fd4565b611f06565b6106936109b2366004613e7f565b6121a6565b6104796109c536600461407e565b612240565b6104796109d8366004613e7f565b61225e565b610479600b5481565b6104797f000000000000000000000000000000000000000000000000000000000000000081565b610693610a1b366004613f09565b612269565b610479610a2e366004613e7f565b6001600160a01b031660009081526003602052604090205490565b610693610a57366004613f73565b6124db565b610479610a6a366004613e9a565b600460209081526000928352604080842090915290825290205481565b61054b610a9536600461407e565b6125e7565b61054b610aa83660046140b0565b6127cd565b6104796303bfc40081565b600854610616906001600160a01b031681565b610693610ad936600461407e565b612836565b610479610aec36600461407e565b6128d8565b610693610aff36600461407e565b6128e3565b610b17610b1236600461402c565b612b32565b6040805163ffffffff90931683526001600160601b03909116602083015201610483565b610693610b4936600461407e565b612bbe565b600a5460009080610b6157505060095490565b600c54600b546000428310610b7f57610b7a82426144a2565b610b89565b610b8982846144a2565b6009549091507f0000000000000000000000000000000000000000000000000000000000000000610bba8387614483565b610bc4919061446f565b610bce9190614457565b94505050505090565b60008054610be4906144e5565b80601f0160208091040260200160405190810160405280929190818152602001828054610c10906144e5565b8015610c5d5780601f10610c3257610100808354040283529160200191610c5d565b820191906000526020600020905b815481529060010190602001808311610c4057829003601f168201915b505050505081565b6002546000908015610c935780610c7a610b4e565b610c849085614483565b610c8e919061446f565b610c95565b825b9392505050565b60136020528160005260406000208181548110610cb857600080fd5b60009182526020909120015463ffffffff811692506001600160601b03600160201b820481169250600160801b9091041683565b6000610cf9338484612e48565b5060015b92915050565b6000610d108260006127cd565b5092915050565b600080610d274262015180614457565b600c541115610d765760405162461bcd60e51b81526020600482015260166024820152754c5244543a5556533a5354494c4c5f56455354494e4760501b60448201526064015b60405180910390fd5b600060025411610dbf5760405162461bcd60e51b81526020600482015260146024820152734c5244543a5556533a5a45524f5f535550504c5960601b6044820152606401610d6d565b610dc7610b4e565b60098190559050600062127500905042600c541115610dfe5742600c54610dee91906144a2565b610dfb9062127500614457565b90505b6006546040516370a0823160e01b815230600482015282917f00000000000000000000000000000000000000000000000000000000000000009185916001600160a01b0316906370a082319060240160206040518083038186803b158015610e6557600080fd5b505afa158015610e79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9d9190614097565b610ea791906144a2565b610eb19190614483565b610ebb919061446f565b600a819055925060008311610f125760405162461bcd60e51b815260206004820152601b60248201527f4c5244543a5556533a5a45524f5f49535355414e43455f5241544500000000006044820152606401610d6d565b8042600b819055610f239190614457565b600c556040805183815260208101859052600080516020614585833981519152910160405180910390a1600c5460405190815233907f8c84e3b4df93f5b7c8d4ab6647708f5b14cacc124e22908187e30695ec54bab39060200160405180910390a2509091565b6000806000610f9a601485612eaa565b6001600160601b031691506001600160601b0316915081600014610fd25781610fc38287614483565b610fcd919061446f565b610fd4565b845b95945050505050565b6001600160a01b038116600090815260116020526040812054819060ff16156110145761100984612fd2565b600091509150611056565b600061101f85612fd2565b90506064600e54606461103291906144a2565b61103c9083614483565b611046919061446f565b925061105283826144a2565b9150505b9250929050565b600061106a843384612fdd565b611075848484613021565b5060019392505050565b6001600160a01b0381166000908152601060209081526040808320805482518185028101850190935280835260609492939192909184015b82821015611121576000848152602090819020604080516080810182529185015463ffffffff8082168452600160201b820416838501526001600160601b03600160401b8204811692840192909252600160a01b90041660608201528252600190920191016110b7565b505050509050919050565b60007f000000000000000000000000000000000000000000000000000000000000000046146111625761115d613063565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b3360008181526004602090815260408083206001600160a01b03871684529091528120549091610cf99185906111be908690614457565b612e48565b600043821061120c5760405162461bcd60e51b815260206004820152601560248201527411d31491150e909313d0d2d7d393d517d352539151605a1b6044820152606401610d6d565b6001600160a01b038316600090815260136020526040812061122e9084612eaa565b506001600160601b031690506112448184610f8a565b949350505050565b600d5460011461126e5760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5533600090815260106020526040812080548390811061129457611294614560565b600091825260209182902060408051608081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b84048116928201839052600160a01b909304909216606083015290915061133d5760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a4e4f5f5749544844524157414c5f524551554553540000000000006044820152606401610d6d565b33600090815260106020526040902080548390811061135e5761135e614560565b600091825260208220018190556060820151611382906001600160601b0316612240565b905060008183604001516001600160601b031661139f91906144a2565b905080156113f65760006113b282610c65565b90506113c18282303030613112565b6040518181527f3d1d48d523494a7f0b705feab835de0a0cc492111d612282ed4d667f6ffd44ee9060200160405180910390a1505b811561144a57611407303384613021565b337f73f04af9dcc582a923ec15d3eea990fe34adabfff2879e28d44572e01a54abb661143284610c65565b60408051918252602082018690520160405180910390a25b6040518481527f6bbfb85db1d5494c2c8fe727a38cdfdbb1577ef2045fc2a5faf2117e9c02cdfc9060200160405180910390a150506001600d555050565b6000610d10826000610fdd565b6040805160808101825260008082526020808301829052828401829052606083018290526001600160a01b038616825260109052919091208054839081106114df576114df614560565b600091825260209182902060408051608081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b8404811692820192909252600160a01b9092041660608201529392505050565b6000600d546001146115635760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5560065460405163d505accf60e01b81526001600160a01b039091169063d505accf906115a490339030908c908b908b908b908b906004016142fa565b600060405180830381600087803b1580156115be57600080fd5b505af11580156115d2573d6000803e3d6000fd5b505050506115ed6115e2886128d8565b915081888833613153565b6001600d559695505050505050565b611606338261319a565b50565b6000600d5460011461162d5760405162461bcd60e51b8152600401610d6d906143bc565b6002600d558561163c89611d7a565b915081111561168d5760405162461bcd60e51b815260206004820152601b60248201527f5244543a4d57503a494e53554646494349454e545f5045524d495400000000006044820152606401610d6d565b60065460405163d505accf60e01b81526001600160a01b039091169063d505accf906116c990339030908b908b908b908b908b906004016142fa565b600060405180830381600087803b1580156116e357600080fd5b505af11580156116f7573d6000803e3d6000fd5b5050505061170788828933613153565b6001600d55979650505050505050565b6000600d5460011461173b5760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5561175761174c846128d8565b915081848433613153565b6001600d5592915050565b6001600160a01b038116600090815260136020526040812054610cfd90613214565b6000610c9561179384846111c3565b61326e565b6008546001600160a01b031633146117e25760405162461bcd60e51b815260206004820152600d60248201526c5244543a414f3a4e4f545f504f60981b6044820152606401610d6d565b60075460405133916001600160a01b0316907f357bdeb5828fa83945f38a88510ce5cd7d628dafb346d767efbc693149fdd97c90600090a3600780546001600160a01b03199081163317909155600880549091169055565b60006118468484611b20565b905081811115610c955760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a4d3a534c4950504147455f50524f54454354494f4e0000000000006044820152606401610d6d565b60004382106118e15760405162461bcd60e51b815260206004820152601560248201527411d31491150e909313d0d2d7d393d517d352539151605a1b6044820152606401610d6d565b6118ec601483612eaa565b506001600160601b031692915050565b6001600160a01b038116600090815260036020526040812054610cfd90610c65565b6040805160808101825260008082526020820181905291810182905260608101919091526001600160a01b038216600090815260106020526040812080548291908690811061196f5761196f614560565b6000918252602080832060408051608081018252919093015463ffffffff8082168352600160201b820416828401526001600160601b03600160401b8204811683860152600160a01b9091041660608201526001600160a01b0388168452601190915291205490935060ff16806119f0575042836000015163ffffffff1611155b15611a0c57505060608101516001600160601b03166000611b19565b8251600090611a2290429063ffffffff166144a2565b90506000846020015163ffffffff167f000000000000000000000000000000000000000000000000000000000000000083600e54611a609190614483565b611a6a9190614483565b611a74919061446f565b9050611aa17f00000000000000000000000000000000000000000000000000000000000000006064614483565b81611acd7f00000000000000000000000000000000000000000000000000000000000000006064614483565b611ad791906144a2565b86606001516001600160601b0316611aef9190614483565b611af9919061446f565b93508385606001516001600160601b0316611b1491906144a2565b925050505b9250925092565b6000600d54600114611b445760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5561175783611b5681611d7a565b9250828433613153565b60018054610be4906144e5565b6001600160a01b03811660009081526013602052604081205480611b945750600092915050565b6001600160a01b0383166000908152601360205260408120611bb76001846144a2565b81548110611bc757611bc7614560565b600091825260209091200154600160201b90046001600160601b031690506112448143610f8a565b6000611bfc858585611e61565b9050818110156112445760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a523a534c4950504147455f50524f54454354494f4e0000000000006044820152606401610d6d565b6000611c5b858585611da1565b9050818111156112445760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a573a534c4950504147455f50524f54454354494f4e0000000000006044820152606401610d6d565b6000610cf9338484612fdd565b6000610cf9338484613021565b6007546001600160a01b03163314611cf15760405162461bcd60e51b8152600401610d6d906143e0565b6303bfc400811115611d3e5760405162461bcd60e51b81526020600482015260166024820152754c5244543a494e56414c49445f4c4f434b5f54494d4560501b6044820152606401610d6d565b600f8190556040518181527f41a6f7a07efe36ac8a11a10901396cbbac41e0210de8914c1ca3891288b28f4e906020015b60405180910390a150565b6002546000908015610c9357610c8e611d91610b4e565b611d9b9085614483565b826132c7565b6000600d54600114611dc55760405162461bcd60e51b8152600401610d6d906143bc565b6002600d556000611dd685846127cd565b9092509050611de88286868633613112565b8015611e4657826001600160a01b0316846001600160a01b0316336001600160a01b03167f8bfaff93aece64b3b1f1c4200e3bca1488f82eabe59506e8339b44005a345fbf84604051611e3d91815260200190565b60405180910390a45b506001600d559392505050565b6000610cfd61179383611b6d565b6000600d54600114611e855760405162461bcd60e51b8152600401610d6d906143bc565b6002600d556000611e968584610fdd565b9092509050611de88583868633613112565b6000611eb48484611717565b905081811015610c955760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a443a534c4950504147455f50524f54454354494f4e0000000000006044820152606401610d6d565b42841015611f4a5760405162461bcd60e51b815260206004820152601160248201527011d31491150e911094ce91561412549151607a1b6044820152606401610d6d565b6fa2a8918ca85bafe22016d0b997e4df60600160ff1b038111801590611f8057508260ff16601b1480611f8057508260ff16601c145b611fc25760405162461bcd60e51b8152602060048201526013602482015272474c5244543a4442533a4d414c4c4541424c4560681b6044820152606401610d6d565b6000611fcc61112c565b604080517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60208201526001600160a01b038a1691810191909152606081018890526080810187905260a0016040516020818303038152906040528051906020012060405160200161205592919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa1580156120c0573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166121235760405162461bcd60e51b815260206004820152601b60248201527f474c5244543a4442533a494e56414c49445f5349474e415455524500000000006044820152606401610d6d565b6001600160a01b038116600090815260056020526040902080546001810190915587146121925760405162461bcd60e51b815260206004820152601760248201527f474c5244543a4442533a494e56414c49445f4e4f4e43450000000000000000006044820152606401610d6d565b61219c818961319a565b5050505050505050565b6007546001600160a01b031633146121f45760405162461bcd60e51b815260206004820152601160248201527029222a1d29a8279d2727aa2fa7aba722a960791b6044820152606401610d6d565b600880546001600160a01b0319166001600160a01b03831690811790915560405133907fa86864fa6b65f969d5ac8391ddaac6a0eba3f41386cbf6e78c3e4d6c59eb115f90600090a350565b6002546000908015610c9357612254610b4e565b610c848285614483565b6000610cfd826118fc565b428410156122ab5760405162461bcd60e51b815260206004820152600f60248201526e115490cc8c0e940e91561412549151608a1b6044820152606401610d6d565b6fa2a8918ca85bafe22016d0b997e4df60600160ff1b0381118015906122e157508260ff16601b14806122e157508260ff16601c145b6123215760405162461bcd60e51b815260206004820152601160248201527045524332303a503a4d414c4c4541424c4560781b6044820152606401610d6d565b600061232b61112c565b6001600160a01b0389811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938c166060840152608083018b905260a083019390935260c08083018a90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015612444573d6000803e3d6000fd5b505050602060405103519050886001600160a01b0316816001600160a01b031614801561247957506001600160a01b03891615155b6124c55760405162461bcd60e51b815260206004820152601960248201527f45524332303a503a494e56414c49445f5349474e4154555245000000000000006044820152606401610d6d565b50506124d2878787612e48565b50505050505050565b6007546001600160a01b031633146125055760405162461bcd60e51b8152600401610d6d906143e0565b6001600160a01b03821661254f5760405162461bcd60e51b8152602060048201526011602482015270131491150e96915493d7d050d0d3d55395607a1b6044820152606401610d6d565b801561257d576001600160a01b0382166000908152601160205260409020805460ff1916600117905561259e565b6001600160a01b0382166000908152601160205260409020805460ff191690555b816001600160a01b03167f8cb23b40e76bb3d8bd36536c73d565149be7b163c1c774a0c8e26e4616b84c0d826040516125db911515815260200190565b60405180910390a25050565b60075460009081906001600160a01b0316331461263a5760405162461bcd60e51b815260206004820152601160248201527029222a1d2aab299d2727aa2fa7aba722a960791b6044820152606401610d6d565b60025461267f5760405162461bcd60e51b81526020600482015260136024820152725244543a5556533a5a45524f5f535550504c5960681b6044820152606401610d6d565b612687610b4e565b60098190556006546040516370a0823160e01b815230600482015291925084917f00000000000000000000000000000000000000000000000000000000000000009184916001600160a01b03909116906370a082319060240160206040518083038186803b1580156126f857600080fd5b505afa15801561270c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127309190614097565b61273a91906144a2565b6127449190614483565b61274e919061446f565b600a81905591508242600b8190556127669190614457565b600c556040805182815260208101849052600080516020614585833981519152910160405180910390a1600c5460405190815233907f8c84e3b4df93f5b7c8d4ab6647708f5b14cacc124e22908187e30695ec54bab39060200160405180910390a2915091565b6001600160a01b038116600090815260116020526040812054819060ff16156127f957611009846132fa565b6000600e54606461280a91906144a2565b612815866064614483565b61281f919061446f565b905061282a816132fa565b925061105285826144a2565b6007546001600160a01b031633146128605760405162461bcd60e51b8152600401610d6d906143e0565b606481106128a35760405162461bcd60e51b815260206004820152601060248201526f4c5244543a494e56414c49445f46454560801b6044820152606401610d6d565b600e8190556040518181527f04714fe6809a3f2492f738090bf4055c802fd8f1a44cb9a58e85dcf2ed12821390602001611d6f565b6000610cfd82612240565b600d546001146129055760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5560008080612918843361191e565b925092509250600083604001516001600160601b03161161297b5760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a4e4f5f5749544844524157414c5f524551554553540000000000006044820152606401610d6d565b82514290612993906224ea009063ffffffff16614457565b116129e05760405162461bcd60e51b815260206004820152601d60248201527f4c5244543a5749544844524157414c5f57494e444f575f434c4f5345440000006044820152606401610d6d565b336000908152601060205260409020805485908110612a0157612a01614560565b60009182526020822001819055612a1783612240565b905060008185604001516001600160601b0316612a3491906144a2565b90508015612a94576000612a4782610c65565b9050612a568282303030613112565b7f3d1d48d523494a7f0b705feab835de0a0cc492111d612282ed4d667f6ffd44ee612a8185836144a2565b60405190815260200160405180910390a1505b8115612ab257612aa5303384613021565b612ab28285333333613112565b8215612af2576040518381523390819081907f8bfaff93aece64b3b1f1c4200e3bca1488f82eabe59506e8339b44005a345fbf9060200160405180910390a45b6040518681527feb5addd1a4f9c1d06c0c1394e99b7ac1c8b708697da061fd48dec16fedd1b24f9060200160405180910390a150506001600d5550505050565b6001600160a01b038216600090815260136020526040812080548291829163ffffffff8616908110612b6657612b66614560565b600091825260209182902060408051606081018252919092015463ffffffff81168083526001600160601b03600160201b8304811695840195909552600160801b909104909316910181905290969095509350505050565b600d54600114612be05760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5580612c285760405162461bcd60e51b8152602060048201526013602482015272131491150e9253959053125117d05353d55395606a1b6044820152606401610d6d565b33600090815260036020526040902054811115612c875760405162461bcd60e51b815260206004820152601960248201527f4c5244543a494e53554646494349454e545f42414c414e4345000000000000006044820152606401610d6d565b60006040518060800160405280600f5442612ca29190614457565b63ffffffff168152602001600f5463ffffffff168152602001836001600160601b03168152602001612cd384610c65565b6001600160601b03908116909152336000818152601060209081526040808320805460018101825590845292829020865193018054928701519187015160608801518716600160a01b029616600160401b026001600160a01b031667ffffffffffffffff63ffffffff938416600160201b0267ffffffffffffffff19909516939095169290921792909217929092169190911792909217909155909150612d7b903084613021565b336000908152601060205260409020547fad52132cbad731070e75516d3243ed9517052fd767f2ee04c1b78a7c80dcbf4e908290612dbb906001906144a2565b604051612dc992919061441d565b60405180910390a150506001600d55565b6040516001600160a01b0380851660248301528316604482015260648101829052600090610fd49086906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613320565b6001600160a01b0383811660008181526004602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b8154600090819081816005811115612f06576000612ec7846133c0565b612ed190856144a2565b600089815260209020909150879082015463ffffffff161115612ef657809150612f04565b612f01816001614457565b92505b505b80821015612f53576000612f1a8383613523565b600089815260209020909150879082015463ffffffff161115612f3f57809150612f4d565b612f4a816001614457565b92505b50612f06565b80612f675760008094509450505050611056565b6000612f8688612f786001856144a2565b600091825260209091200190565b60408051606081018252915463ffffffff811683526001600160601b03600160201b8204811660208501819052600160801b909204169290910182905296509450505050509250929050565b6000610cfd82610c65565b6001600160a01b03808416600090815260046020908152604080832093861683529290522054600019811461301b5761301b84846111be85856144a2565b50505050565b61302c83838361353e565b6001600160a01b0380841660009081526012602052604080822054858416835291205461305e929182169116836135c1565b505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051613095919061425e565b60408051918290038220828201825260018352603160f81b6020938401528151928301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b61311f85858585856136fe565b6001600160a01b03808316600090815260126020526040812054613145921690876135c1565b6124d260146138fd87613909565b61315f84848484613b21565b6001600160a01b038083166000908152601260205260408120546131849216866135c1565b6131926014613cf886613909565b505050505050565b6001600160a01b03808316600081815260126020818152604080842080546003845282862054949093528787166001600160a01b03198416811790915590519190951694919391928592917f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a461301b8284836135c1565b600063ffffffff82111561326a5760405162461bcd60e51b815260206004820152601a60248201527f474c5244543a434153545f455843454544535f33325f424954530000000000006044820152606401610d6d565b5090565b60006001600160601b0382111561326a5760405162461bcd60e51b815260206004820152601a60248201527f474c5244543a434153545f455843454544535f39365f424954530000000000006044820152606401610d6d565b6000806132d48385614520565b116132e05760006132e3565b60015b60ff166132f0838561446f565b610c959190614457565b6002546000908015610c9357610c8e6133138285614483565b61331b610b4e565b6132c7565b60006001600160a01b0383163b61333957506000610cfd565b6060836001600160a01b0316836040516133539190614242565b6000604051808303816000865af19150503d8060008114613390576040519150601f19603f3d011682016040523d82523d6000602084013e613395565b606091505b5090925090508180156112445750805115806112445750808060200190518101906112449190614061565b6000816133cf57506000919050565b600182608081901c156133e75760409190911b9060801c5b604081901c156133fc5760209190911b9060401c5b602081901c156134115760109190911b9060201c5b601081901c156134265760089190911b9060101c5b600881901c1561343b5760049190911b9060081c5b600481901c156134505760029190911b9060041c5b600281901c1561346257600182901b91505b60018285816134735761347361454a565b048301901c9150600182858161348b5761348b61454a565b048301901c915060018285816134a3576134a361454a565b048301901c915060018285816134bb576134bb61454a565b048301901c915060018285816134d3576134d361454a565b048301901c915060018285816134eb576134eb61454a565b048301901c915060018285816135035761350361454a565b048301901c91506112448283868161351d5761351d61454a565b04613d04565b6000613532600284841861446f565b610c9590848416614457565b6001600160a01b038316600090815260036020526040812080548392906135669084906144a2565b90915550506001600160a01b03808316600081815260036020526040908190208054850190555190918516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612e9d9085815260200190565b816001600160a01b0316836001600160a01b0316141580156135e35750600081115b1561305e576001600160a01b03831615613671576001600160a01b0383166000908152601360205260408120819061361e906138fd85613909565b91509150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051613666929190918252602082015260400190565b60405180910390a250505b6001600160a01b0382161561305e576001600160a01b038216600090815260136020526040812081906136a790613cf885613909565b91509150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516136ef929190918252602082015260400190565b60405180910390a25050505050565b6001600160a01b03831661374a5760405162461bcd60e51b815260206004820152601360248201527229222a1d211d2d22a927afa922a1a2a4ab22a960691b6044820152606401610d6d565b8461378b5760405162461bcd60e51b81526020600482015260116024820152705244543a423a5a45524f5f53484152455360781b6044820152606401610d6d565b836137cc5760405162461bcd60e51b81526020600482015260116024820152705244543a423a5a45524f5f41535345545360781b6044820152606401610d6d565b816001600160a01b0316816001600160a01b0316146137f0576137f0828287612fdd565b6137fa8286613d1a565b600084613805610b4e565b61380f91906144a2565b600981905590506000613820613d96565b9050836001600160a01b0316856001600160a01b0316846001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db898b60405161387a929190918252602082015260400190565b60405180910390a46040805183815260208101839052600080516020614585833981519152910160405180910390a16006546138c0906001600160a01b03168688613dbb565b6124d25760405162461bcd60e51b815260206004820152600e60248201526d29222a1d211d2a2920a729a322a960911b6044820152606401610d6d565b6000610c9582846144a2565b825460009081908181156139635761392687612f786001856144a2565b60408051606081018252915463ffffffff811683526001600160601b03600160201b820481166020850152600160801b9091041690820152613981565b60408051606081018252600080825260208201819052918101919091525b905080602001516001600160601b031693506139a184868863ffffffff16565b92506000821180156139b95750805163ffffffff1643145b15613a53576139c78361326e565b6139d688612f786001866144a2565b80546001600160601b0392909216600160201b026fffffffffffffffffffffffff0000000019909216919091179055613a1161179384610c65565b613a2088612f786001866144a2565b80546001600160601b0392909216600160801b026bffffffffffffffffffffffff60801b19909216919091179055613b17565b866040518060600160405280613a6843613214565b63ffffffff168152602001613a7c8661326e565b6001600160601b03168152602001613a9661179387610c65565b6001600160601b0390811690915282546001810184556000938452602093849020835191018054948401516040909401518316600160801b026bffffffffffffffffffffffff60801b1994909316600160201b026fffffffffffffffffffffffffffffffff1990951663ffffffff9092169190911793909317919091161790555b5050935093915050565b6001600160a01b038216613b6d5760405162461bcd60e51b815260206004820152601360248201527229222a1d269d2d22a927afa922a1a2a4ab22a960691b6044820152606401610d6d565b83613bae5760405162461bcd60e51b81526020600482015260116024820152705244543a4d3a5a45524f5f53484152455360781b6044820152606401610d6d565b82613bef5760405162461bcd60e51b81526020600482015260116024820152705244543a4d3a5a45524f5f41535345545360781b6044820152606401610d6d565b613bf98285613dee565b600083613c04610b4e565b613c0e9190614457565b600981905590506000613c1f613d96565b9050836001600160a01b0316836001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d78789604051613c6f929190918252602082015260400190565b60405180910390a36040805183815260208101839052600080516020614585833981519152910160405180910390a1600654613cb6906001600160a01b0316843088612dda565b6131925760405162461bcd60e51b81526020600482015260136024820152725244543a4d3a5452414e534645525f46524f4d60681b6044820152606401610d6d565b6000610c958284614457565b6000818310613d135781610c95565b5090919050565b6001600160a01b03821660009081526003602052604081208054839290613d429084906144a2565b90915550506002805482900390556040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6000600c5442600b81905511613dae57600a54613db1565b60005b600a819055905090565b6040516001600160a01b03831660248201526044810182905260009061124490859063a9059cbb60e01b90606401612e11565b8060026000828254613e009190614457565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101613d8a565b80356001600160a01b0381168114613e6957600080fd5b919050565b803560ff81168114613e6957600080fd5b600060208284031215613e9157600080fd5b610c9582613e52565b60008060408385031215613ead57600080fd5b613eb683613e52565b9150613ec460208401613e52565b90509250929050565b600080600060608486031215613ee257600080fd5b613eeb84613e52565b9250613ef960208501613e52565b9150604084013590509250925092565b600080600080600080600060e0888a031215613f2457600080fd5b613f2d88613e52565b9650613f3b60208901613e52565b95506040880135945060608801359350613f5760808901613e6e565b925060a0880135915060c0880135905092959891949750929550565b60008060408385031215613f8657600080fd5b613f8f83613e52565b91506020830135613f9f81614576565b809150509250929050565b60008060408385031215613fbd57600080fd5b613fc683613e52565b946020939093013593505050565b60008060008060008060c08789031215613fed57600080fd5b613ff687613e52565b9550602087013594506040870135935061401260608801613e6e565b92506080870135915060a087013590509295509295509295565b6000806040838503121561403f57600080fd5b61404883613e52565b9150602083013563ffffffff81168114613f9f57600080fd5b60006020828403121561407357600080fd5b8151610c9581614576565b60006020828403121561409057600080fd5b5035919050565b6000602082840312156140a957600080fd5b5051919050565b600080604083850312156140c357600080fd5b82359150613ec460208401613e52565b6000806000606084860312156140e857600080fd5b833592506140f860208501613e52565b915061410660408501613e52565b90509250925092565b6000806000806080858703121561412557600080fd5b8435935061413560208601613e52565b925061414360408601613e52565b9396929550929360600135925050565b60008060006060848603121561416857600080fd5b83359250613ef960208501613e52565b600080600080600080600060e0888a03121561419357600080fd5b87359650613f3b60208901613e52565b60008060008060008060c087890312156141bc57600080fd5b863595506141cc60208801613e52565b94506040870135935061401260608801613e6e565b600080604083850312156141f457600080fd5b50508035926020909101359150565b63ffffffff8082511683528060208301511660208401525060408101516001600160601b03808216604085015280606084015116606085015250505050565b600082516142548184602087016144b9565b9190910192915050565b600080835481600182811c91508083168061427a57607f831692505b602080841082141561429a57634e487b7160e01b86526022600452602486fd5b8180156142ae57600181146142bf576142ec565b60ff198616895284890196506142ec565b60008a81526020902060005b868110156142e45781548b8201529085019083016142cb565b505084890196505b509498975050505050505050565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6020808252825182820181905260009190848201906040850190845b8181101561437d5761436a838551614203565b9284019260809290920191600101614357565b50909695505050505050565b60208152600082518060208401526143a88160408501602087016144b9565b601f01601f19169190910160400192915050565b6020808252600a90820152691491150e9313d0d2d15160b21b604082015260600190565b6020808252601590820152742629222a1d21a0a62622a92fa727aa2fa7aba722a960591b604082015260600190565b60808101610cfd8284614203565b60a0810161442b8285614203565b8260808301529392505050565b60c081016144468286614203565b608082019390935260a00152919050565b6000821982111561446a5761446a614534565b500190565b60008261447e5761447e61454a565b500490565b600081600019048311821515161561449d5761449d614534565b500290565b6000828210156144b4576144b4614534565b500390565b60005b838110156144d45781810151838201526020016144bc565b8381111561301b5750506000910152565b600181811c908216806144f957607f821691505b6020821081141561451a57634e487b7160e01b600052602260045260246000fd5b50919050565b60008261452f5761452f61454a565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b801515811461160657600080fdfe68b521a89bf844ff03e484d89fd64ce292a698ec522170f0dad7ecd11c2dc8faa26469706673582212202bcddbab3c0412787a12419ee8566b5a922eae59af354056db055eb78427457364736f6c6343000807003300000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000f7a171c168a35ae858fd18bec5bb7c28134b823f000000000000000000000000c28eb2250d1ae32c7e74cfb6d6b86afc9beb65090000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000eff1000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000001b5374616b6564204f70656e2045636f73797374656d20546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000004784f504e00000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061046a5760003560e01c8063836a10401161024c578063c3cda52011610146578063dd62ed3e116100c3578063ec5de22811610087578063ec5de22814610acb578063ef8b30f714610ade578063f00aa90a14610af1578063f1127ed814610b04578063fab0079214610b3b57600080fd5b8063dd62ed3e14610a5c578063e13aa99014610a87578063e1deded114610a9a578063e1ef57ad14610aad578063e30c397814610ab857600080fd5b8063d0b06f5d1161010a578063d0b06f5d146109dd578063d3b5dc3b146109e6578063d505accf14610a0d578063d905777e14610a20578063dc02bde314610a4957600080fd5b8063c3cda52014610991578063c42069ec146109a4578063c63d75b614610666578063c6e6f592146109b7578063ce96cb77146109ca57600080fd5b8063a318c1a4116101d4578063b3d7f6b911610198578063b3d7f6b914610932578063b460af9414610945578063b4b5ea5714610958578063ba0876521461096b578063bc157ac11461097e57600080fd5b8063a318c1a4146108bd578063a457c2d7146108d0578063a9059cbb146108e3578063ade3142f146108f6578063ae04d45d1461091f57600080fd5b806393f3f1b61161021b57806393f3f1b61461085a57806394bf804d1461087c57806395d89b411461088f5780639ab24eb0146108975780639f40a7b3146108aa57600080fd5b8063836a10401461080e5780638da5cb5b146108215780638e539e8c146108345780639159b2061461084757600080fd5b80633a46b1a811610368578063587cde1e116102e55780636fcfff45116102a95780636fcfff451461077357806370a082311461079b578063782d6fe1146107bb57806379ba5097146107e65780637ecebe00146107ee57600080fd5b8063587cde1e146107085780635c19a95c1461073157806360dd37d9146107445780636b34128c146107575780636e553f651461076057600080fd5b80634823deea1161032c5780634823deea146106955780634cdad506146106b85780634e5a2328146106cb57806350921b23146106eb57806356765c51146106fe57600080fd5b80633a46b1a8146106415780633c2f7773146106545780633c9ae2ba1461065d578063402d267d1461066657806345b05d091461068057600080fd5b806314149bcb116103f657806327b380f3116103ba57806327b380f3146105a2578063313ce567146105c25780633644e515146105fb57806338d52e0f14610603578063395093511461062e57600080fd5b806314149bcb1461054357806318160ddd14610560578063181e7b3b146105695780631daea4431461057c57806323b872dd1461058f57600080fd5b8063094383201161043d57806309438320146104bc578063095ea7b3146104fb5780630a28a4771461051e5780630d6680871461053157806311f240ac1461053a57600080fd5b80630197d9721461046f57806301e1d1141461048c57806306fdde031461049457806307a2d13a146104a9575b600080fd5b6104796212750081565b6040519081526020015b60405180910390f35b610479610b4e565b61049c610bd7565b6040516104839190614389565b6104796104b736600461407e565b610c65565b6104cf6104ca366004613faa565b610c9c565b6040805163ffffffff90941684526001600160601b039283166020850152911690820152606001610483565b61050e610509366004613faa565b610cec565b6040519015158152602001610483565b61047961052c36600461407e565b610d03565b610479600f5481565b61047960095481565b61054b610d17565b60408051928352602083019190915201610483565b61047960025481565b6104796105773660046141e1565b610f8a565b61054b61058a3660046140b0565b610fdd565b61050e61059d366004613ecd565b61105d565b6105b56105b0366004613e7f565b61107f565b604051610483919061433b565b6105e97f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610483565b61047961112c565b600654610616906001600160a01b031681565b6040516001600160a01b039091168152602001610483565b61050e61063c366004613faa565b611187565b61047961064f366004613faa565b6111c3565b610479600c5481565b610479600a5481565b610479610674366004613e7f565b506001600160601b0390565b61069361068e36600461407e565b61124c565b005b61050e6106a3366004613e7f565b60116020526000908152604090205460ff1681565b6104796106c636600461407e565b611488565b6106de6106d9366004613faa565b611495565b604051610483919061440f565b6104796106f93660046141a3565b61153f565b6104796224ea0081565b610616610716366004613e7f565b6012602052600090815260409020546001600160a01b031681565b61069361073f366004613e7f565b6115fc565b610479610752366004614178565b611609565b610479600e5481565b61047961076e3660046140b0565b611717565b610786610781366004613e7f565b611762565b60405163ffffffff9091168152602001610483565b6104796107a9366004613e7f565b60036020526000908152604090205481565b6107ce6107c9366004613faa565b611784565b6040516001600160601b039091168152602001610483565b610693611798565b6104796107fc366004613e7f565b60056020526000908152604090205481565b61047961081c366004614153565b61183a565b600754610616906001600160a01b031681565b61047961084236600461407e565b611898565b610479610855366004613e7f565b6118fc565b61086d6108683660046140b0565b61191e565b60405161048393929190614438565b61047961088a3660046140b0565b611b20565b61049c611b60565b6104796108a5366004613e7f565b611b6d565b6104796108b836600461410f565b611bef565b6104796108cb36600461410f565b611c4e565b61050e6108de366004613faa565b611cad565b61050e6108f1366004613faa565b611cba565b610479610904366004613e7f565b6001600160a01b031660009081526010602052604090205490565b61069361092d36600461407e565b611cc7565b61047961094036600461407e565b611d7a565b6104796109533660046140d3565b611da1565b6107ce610966366004613e7f565b611e53565b6104796109793660046140d3565b611e61565b61047961098c366004614153565b611ea8565b61069361099f366004613fd4565b611f06565b6106936109b2366004613e7f565b6121a6565b6104796109c536600461407e565b612240565b6104796109d8366004613e7f565b61225e565b610479600b5481565b6104797f0000000000000000000000000000000000000000ffffffffffffffffffffffff81565b610693610a1b366004613f09565b612269565b610479610a2e366004613e7f565b6001600160a01b031660009081526003602052604090205490565b610693610a57366004613f73565b6124db565b610479610a6a366004613e9a565b600460209081526000928352604080842090915290825290205481565b61054b610a9536600461407e565b6125e7565b61054b610aa83660046140b0565b6127cd565b6104796303bfc40081565b600854610616906001600160a01b031681565b610693610ad936600461407e565b612836565b610479610aec36600461407e565b6128d8565b610693610aff36600461407e565b6128e3565b610b17610b1236600461402c565b612b32565b6040805163ffffffff90931683526001600160601b03909116602083015201610483565b610693610b4936600461407e565b612bbe565b600a5460009080610b6157505060095490565b600c54600b546000428310610b7f57610b7a82426144a2565b610b89565b610b8982846144a2565b6009549091507f0000000000000000000000000000000000000000ffffffffffffffffffffffff610bba8387614483565b610bc4919061446f565b610bce9190614457565b94505050505090565b60008054610be4906144e5565b80601f0160208091040260200160405190810160405280929190818152602001828054610c10906144e5565b8015610c5d5780601f10610c3257610100808354040283529160200191610c5d565b820191906000526020600020905b815481529060010190602001808311610c4057829003601f168201915b505050505081565b6002546000908015610c935780610c7a610b4e565b610c849085614483565b610c8e919061446f565b610c95565b825b9392505050565b60136020528160005260406000208181548110610cb857600080fd5b60009182526020909120015463ffffffff811692506001600160601b03600160201b820481169250600160801b9091041683565b6000610cf9338484612e48565b5060015b92915050565b6000610d108260006127cd565b5092915050565b600080610d274262015180614457565b600c541115610d765760405162461bcd60e51b81526020600482015260166024820152754c5244543a5556533a5354494c4c5f56455354494e4760501b60448201526064015b60405180910390fd5b600060025411610dbf5760405162461bcd60e51b81526020600482015260146024820152734c5244543a5556533a5a45524f5f535550504c5960601b6044820152606401610d6d565b610dc7610b4e565b60098190559050600062127500905042600c541115610dfe5742600c54610dee91906144a2565b610dfb9062127500614457565b90505b6006546040516370a0823160e01b815230600482015282917f0000000000000000000000000000000000000000ffffffffffffffffffffffff9185916001600160a01b0316906370a082319060240160206040518083038186803b158015610e6557600080fd5b505afa158015610e79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9d9190614097565b610ea791906144a2565b610eb19190614483565b610ebb919061446f565b600a819055925060008311610f125760405162461bcd60e51b815260206004820152601b60248201527f4c5244543a5556533a5a45524f5f49535355414e43455f5241544500000000006044820152606401610d6d565b8042600b819055610f239190614457565b600c556040805183815260208101859052600080516020614585833981519152910160405180910390a1600c5460405190815233907f8c84e3b4df93f5b7c8d4ab6647708f5b14cacc124e22908187e30695ec54bab39060200160405180910390a2509091565b6000806000610f9a601485612eaa565b6001600160601b031691506001600160601b0316915081600014610fd25781610fc38287614483565b610fcd919061446f565b610fd4565b845b95945050505050565b6001600160a01b038116600090815260116020526040812054819060ff16156110145761100984612fd2565b600091509150611056565b600061101f85612fd2565b90506064600e54606461103291906144a2565b61103c9083614483565b611046919061446f565b925061105283826144a2565b9150505b9250929050565b600061106a843384612fdd565b611075848484613021565b5060019392505050565b6001600160a01b0381166000908152601060209081526040808320805482518185028101850190935280835260609492939192909184015b82821015611121576000848152602090819020604080516080810182529185015463ffffffff8082168452600160201b820416838501526001600160601b03600160401b8204811692840192909252600160a01b90041660608201528252600190920191016110b7565b505050509050919050565b60007f000000000000000000000000000000000000000000000000000000000000000146146111625761115d613063565b905090565b507fa99afe48fb3dddb8edbb1156a68539a7b8acd870cd3ebbc1aeeca4d6a5bab2c190565b3360008181526004602090815260408083206001600160a01b03871684529091528120549091610cf99185906111be908690614457565b612e48565b600043821061120c5760405162461bcd60e51b815260206004820152601560248201527411d31491150e909313d0d2d7d393d517d352539151605a1b6044820152606401610d6d565b6001600160a01b038316600090815260136020526040812061122e9084612eaa565b506001600160601b031690506112448184610f8a565b949350505050565b600d5460011461126e5760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5533600090815260106020526040812080548390811061129457611294614560565b600091825260209182902060408051608081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b84048116928201839052600160a01b909304909216606083015290915061133d5760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a4e4f5f5749544844524157414c5f524551554553540000000000006044820152606401610d6d565b33600090815260106020526040902080548390811061135e5761135e614560565b600091825260208220018190556060820151611382906001600160601b0316612240565b905060008183604001516001600160601b031661139f91906144a2565b905080156113f65760006113b282610c65565b90506113c18282303030613112565b6040518181527f3d1d48d523494a7f0b705feab835de0a0cc492111d612282ed4d667f6ffd44ee9060200160405180910390a1505b811561144a57611407303384613021565b337f73f04af9dcc582a923ec15d3eea990fe34adabfff2879e28d44572e01a54abb661143284610c65565b60408051918252602082018690520160405180910390a25b6040518481527f6bbfb85db1d5494c2c8fe727a38cdfdbb1577ef2045fc2a5faf2117e9c02cdfc9060200160405180910390a150506001600d555050565b6000610d10826000610fdd565b6040805160808101825260008082526020808301829052828401829052606083018290526001600160a01b038616825260109052919091208054839081106114df576114df614560565b600091825260209182902060408051608081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b8404811692820192909252600160a01b9092041660608201529392505050565b6000600d546001146115635760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5560065460405163d505accf60e01b81526001600160a01b039091169063d505accf906115a490339030908c908b908b908b908b906004016142fa565b600060405180830381600087803b1580156115be57600080fd5b505af11580156115d2573d6000803e3d6000fd5b505050506115ed6115e2886128d8565b915081888833613153565b6001600d559695505050505050565b611606338261319a565b50565b6000600d5460011461162d5760405162461bcd60e51b8152600401610d6d906143bc565b6002600d558561163c89611d7a565b915081111561168d5760405162461bcd60e51b815260206004820152601b60248201527f5244543a4d57503a494e53554646494349454e545f5045524d495400000000006044820152606401610d6d565b60065460405163d505accf60e01b81526001600160a01b039091169063d505accf906116c990339030908b908b908b908b908b906004016142fa565b600060405180830381600087803b1580156116e357600080fd5b505af11580156116f7573d6000803e3d6000fd5b5050505061170788828933613153565b6001600d55979650505050505050565b6000600d5460011461173b5760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5561175761174c846128d8565b915081848433613153565b6001600d5592915050565b6001600160a01b038116600090815260136020526040812054610cfd90613214565b6000610c9561179384846111c3565b61326e565b6008546001600160a01b031633146117e25760405162461bcd60e51b815260206004820152600d60248201526c5244543a414f3a4e4f545f504f60981b6044820152606401610d6d565b60075460405133916001600160a01b0316907f357bdeb5828fa83945f38a88510ce5cd7d628dafb346d767efbc693149fdd97c90600090a3600780546001600160a01b03199081163317909155600880549091169055565b60006118468484611b20565b905081811115610c955760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a4d3a534c4950504147455f50524f54454354494f4e0000000000006044820152606401610d6d565b60004382106118e15760405162461bcd60e51b815260206004820152601560248201527411d31491150e909313d0d2d7d393d517d352539151605a1b6044820152606401610d6d565b6118ec601483612eaa565b506001600160601b031692915050565b6001600160a01b038116600090815260036020526040812054610cfd90610c65565b6040805160808101825260008082526020820181905291810182905260608101919091526001600160a01b038216600090815260106020526040812080548291908690811061196f5761196f614560565b6000918252602080832060408051608081018252919093015463ffffffff8082168352600160201b820416828401526001600160601b03600160401b8204811683860152600160a01b9091041660608201526001600160a01b0388168452601190915291205490935060ff16806119f0575042836000015163ffffffff1611155b15611a0c57505060608101516001600160601b03166000611b19565b8251600090611a2290429063ffffffff166144a2565b90506000846020015163ffffffff167f0000000000000000000000000000000000000000ffffffffffffffffffffffff83600e54611a609190614483565b611a6a9190614483565b611a74919061446f565b9050611aa17f0000000000000000000000000000000000000000ffffffffffffffffffffffff6064614483565b81611acd7f0000000000000000000000000000000000000000ffffffffffffffffffffffff6064614483565b611ad791906144a2565b86606001516001600160601b0316611aef9190614483565b611af9919061446f565b93508385606001516001600160601b0316611b1491906144a2565b925050505b9250925092565b6000600d54600114611b445760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5561175783611b5681611d7a565b9250828433613153565b60018054610be4906144e5565b6001600160a01b03811660009081526013602052604081205480611b945750600092915050565b6001600160a01b0383166000908152601360205260408120611bb76001846144a2565b81548110611bc757611bc7614560565b600091825260209091200154600160201b90046001600160601b031690506112448143610f8a565b6000611bfc858585611e61565b9050818110156112445760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a523a534c4950504147455f50524f54454354494f4e0000000000006044820152606401610d6d565b6000611c5b858585611da1565b9050818111156112445760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a573a534c4950504147455f50524f54454354494f4e0000000000006044820152606401610d6d565b6000610cf9338484612fdd565b6000610cf9338484613021565b6007546001600160a01b03163314611cf15760405162461bcd60e51b8152600401610d6d906143e0565b6303bfc400811115611d3e5760405162461bcd60e51b81526020600482015260166024820152754c5244543a494e56414c49445f4c4f434b5f54494d4560501b6044820152606401610d6d565b600f8190556040518181527f41a6f7a07efe36ac8a11a10901396cbbac41e0210de8914c1ca3891288b28f4e906020015b60405180910390a150565b6002546000908015610c9357610c8e611d91610b4e565b611d9b9085614483565b826132c7565b6000600d54600114611dc55760405162461bcd60e51b8152600401610d6d906143bc565b6002600d556000611dd685846127cd565b9092509050611de88286868633613112565b8015611e4657826001600160a01b0316846001600160a01b0316336001600160a01b03167f8bfaff93aece64b3b1f1c4200e3bca1488f82eabe59506e8339b44005a345fbf84604051611e3d91815260200190565b60405180910390a45b506001600d559392505050565b6000610cfd61179383611b6d565b6000600d54600114611e855760405162461bcd60e51b8152600401610d6d906143bc565b6002600d556000611e968584610fdd565b9092509050611de88583868633613112565b6000611eb48484611717565b905081811015610c955760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a443a534c4950504147455f50524f54454354494f4e0000000000006044820152606401610d6d565b42841015611f4a5760405162461bcd60e51b815260206004820152601160248201527011d31491150e911094ce91561412549151607a1b6044820152606401610d6d565b6fa2a8918ca85bafe22016d0b997e4df60600160ff1b038111801590611f8057508260ff16601b1480611f8057508260ff16601c145b611fc25760405162461bcd60e51b8152602060048201526013602482015272474c5244543a4442533a4d414c4c4541424c4560681b6044820152606401610d6d565b6000611fcc61112c565b604080517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60208201526001600160a01b038a1691810191909152606081018890526080810187905260a0016040516020818303038152906040528051906020012060405160200161205592919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa1580156120c0573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166121235760405162461bcd60e51b815260206004820152601b60248201527f474c5244543a4442533a494e56414c49445f5349474e415455524500000000006044820152606401610d6d565b6001600160a01b038116600090815260056020526040902080546001810190915587146121925760405162461bcd60e51b815260206004820152601760248201527f474c5244543a4442533a494e56414c49445f4e4f4e43450000000000000000006044820152606401610d6d565b61219c818961319a565b5050505050505050565b6007546001600160a01b031633146121f45760405162461bcd60e51b815260206004820152601160248201527029222a1d29a8279d2727aa2fa7aba722a960791b6044820152606401610d6d565b600880546001600160a01b0319166001600160a01b03831690811790915560405133907fa86864fa6b65f969d5ac8391ddaac6a0eba3f41386cbf6e78c3e4d6c59eb115f90600090a350565b6002546000908015610c9357612254610b4e565b610c848285614483565b6000610cfd826118fc565b428410156122ab5760405162461bcd60e51b815260206004820152600f60248201526e115490cc8c0e940e91561412549151608a1b6044820152606401610d6d565b6fa2a8918ca85bafe22016d0b997e4df60600160ff1b0381118015906122e157508260ff16601b14806122e157508260ff16601c145b6123215760405162461bcd60e51b815260206004820152601160248201527045524332303a503a4d414c4c4541424c4560781b6044820152606401610d6d565b600061232b61112c565b6001600160a01b0389811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938c166060840152608083018b905260a083019390935260c08083018a90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015612444573d6000803e3d6000fd5b505050602060405103519050886001600160a01b0316816001600160a01b031614801561247957506001600160a01b03891615155b6124c55760405162461bcd60e51b815260206004820152601960248201527f45524332303a503a494e56414c49445f5349474e4154555245000000000000006044820152606401610d6d565b50506124d2878787612e48565b50505050505050565b6007546001600160a01b031633146125055760405162461bcd60e51b8152600401610d6d906143e0565b6001600160a01b03821661254f5760405162461bcd60e51b8152602060048201526011602482015270131491150e96915493d7d050d0d3d55395607a1b6044820152606401610d6d565b801561257d576001600160a01b0382166000908152601160205260409020805460ff1916600117905561259e565b6001600160a01b0382166000908152601160205260409020805460ff191690555b816001600160a01b03167f8cb23b40e76bb3d8bd36536c73d565149be7b163c1c774a0c8e26e4616b84c0d826040516125db911515815260200190565b60405180910390a25050565b60075460009081906001600160a01b0316331461263a5760405162461bcd60e51b815260206004820152601160248201527029222a1d2aab299d2727aa2fa7aba722a960791b6044820152606401610d6d565b60025461267f5760405162461bcd60e51b81526020600482015260136024820152725244543a5556533a5a45524f5f535550504c5960681b6044820152606401610d6d565b612687610b4e565b60098190556006546040516370a0823160e01b815230600482015291925084917f0000000000000000000000000000000000000000ffffffffffffffffffffffff9184916001600160a01b03909116906370a082319060240160206040518083038186803b1580156126f857600080fd5b505afa15801561270c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127309190614097565b61273a91906144a2565b6127449190614483565b61274e919061446f565b600a81905591508242600b8190556127669190614457565b600c556040805182815260208101849052600080516020614585833981519152910160405180910390a1600c5460405190815233907f8c84e3b4df93f5b7c8d4ab6647708f5b14cacc124e22908187e30695ec54bab39060200160405180910390a2915091565b6001600160a01b038116600090815260116020526040812054819060ff16156127f957611009846132fa565b6000600e54606461280a91906144a2565b612815866064614483565b61281f919061446f565b905061282a816132fa565b925061105285826144a2565b6007546001600160a01b031633146128605760405162461bcd60e51b8152600401610d6d906143e0565b606481106128a35760405162461bcd60e51b815260206004820152601060248201526f4c5244543a494e56414c49445f46454560801b6044820152606401610d6d565b600e8190556040518181527f04714fe6809a3f2492f738090bf4055c802fd8f1a44cb9a58e85dcf2ed12821390602001611d6f565b6000610cfd82612240565b600d546001146129055760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5560008080612918843361191e565b925092509250600083604001516001600160601b03161161297b5760405162461bcd60e51b815260206004820152601a60248201527f4c5244543a4e4f5f5749544844524157414c5f524551554553540000000000006044820152606401610d6d565b82514290612993906224ea009063ffffffff16614457565b116129e05760405162461bcd60e51b815260206004820152601d60248201527f4c5244543a5749544844524157414c5f57494e444f575f434c4f5345440000006044820152606401610d6d565b336000908152601060205260409020805485908110612a0157612a01614560565b60009182526020822001819055612a1783612240565b905060008185604001516001600160601b0316612a3491906144a2565b90508015612a94576000612a4782610c65565b9050612a568282303030613112565b7f3d1d48d523494a7f0b705feab835de0a0cc492111d612282ed4d667f6ffd44ee612a8185836144a2565b60405190815260200160405180910390a1505b8115612ab257612aa5303384613021565b612ab28285333333613112565b8215612af2576040518381523390819081907f8bfaff93aece64b3b1f1c4200e3bca1488f82eabe59506e8339b44005a345fbf9060200160405180910390a45b6040518681527feb5addd1a4f9c1d06c0c1394e99b7ac1c8b708697da061fd48dec16fedd1b24f9060200160405180910390a150506001600d5550505050565b6001600160a01b038216600090815260136020526040812080548291829163ffffffff8616908110612b6657612b66614560565b600091825260209182902060408051606081018252919092015463ffffffff81168083526001600160601b03600160201b8304811695840195909552600160801b909104909316910181905290969095509350505050565b600d54600114612be05760405162461bcd60e51b8152600401610d6d906143bc565b6002600d5580612c285760405162461bcd60e51b8152602060048201526013602482015272131491150e9253959053125117d05353d55395606a1b6044820152606401610d6d565b33600090815260036020526040902054811115612c875760405162461bcd60e51b815260206004820152601960248201527f4c5244543a494e53554646494349454e545f42414c414e4345000000000000006044820152606401610d6d565b60006040518060800160405280600f5442612ca29190614457565b63ffffffff168152602001600f5463ffffffff168152602001836001600160601b03168152602001612cd384610c65565b6001600160601b03908116909152336000818152601060209081526040808320805460018101825590845292829020865193018054928701519187015160608801518716600160a01b029616600160401b026001600160a01b031667ffffffffffffffff63ffffffff938416600160201b0267ffffffffffffffff19909516939095169290921792909217929092169190911792909217909155909150612d7b903084613021565b336000908152601060205260409020547fad52132cbad731070e75516d3243ed9517052fd767f2ee04c1b78a7c80dcbf4e908290612dbb906001906144a2565b604051612dc992919061441d565b60405180910390a150506001600d55565b6040516001600160a01b0380851660248301528316604482015260648101829052600090610fd49086906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613320565b6001600160a01b0383811660008181526004602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b8154600090819081816005811115612f06576000612ec7846133c0565b612ed190856144a2565b600089815260209020909150879082015463ffffffff161115612ef657809150612f04565b612f01816001614457565b92505b505b80821015612f53576000612f1a8383613523565b600089815260209020909150879082015463ffffffff161115612f3f57809150612f4d565b612f4a816001614457565b92505b50612f06565b80612f675760008094509450505050611056565b6000612f8688612f786001856144a2565b600091825260209091200190565b60408051606081018252915463ffffffff811683526001600160601b03600160201b8204811660208501819052600160801b909204169290910182905296509450505050509250929050565b6000610cfd82610c65565b6001600160a01b03808416600090815260046020908152604080832093861683529290522054600019811461301b5761301b84846111be85856144a2565b50505050565b61302c83838361353e565b6001600160a01b0380841660009081526012602052604080822054858416835291205461305e929182169116836135c1565b505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051613095919061425e565b60408051918290038220828201825260018352603160f81b6020938401528151928301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b61311f85858585856136fe565b6001600160a01b03808316600090815260126020526040812054613145921690876135c1565b6124d260146138fd87613909565b61315f84848484613b21565b6001600160a01b038083166000908152601260205260408120546131849216866135c1565b6131926014613cf886613909565b505050505050565b6001600160a01b03808316600081815260126020818152604080842080546003845282862054949093528787166001600160a01b03198416811790915590519190951694919391928592917f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a461301b8284836135c1565b600063ffffffff82111561326a5760405162461bcd60e51b815260206004820152601a60248201527f474c5244543a434153545f455843454544535f33325f424954530000000000006044820152606401610d6d565b5090565b60006001600160601b0382111561326a5760405162461bcd60e51b815260206004820152601a60248201527f474c5244543a434153545f455843454544535f39365f424954530000000000006044820152606401610d6d565b6000806132d48385614520565b116132e05760006132e3565b60015b60ff166132f0838561446f565b610c959190614457565b6002546000908015610c9357610c8e6133138285614483565b61331b610b4e565b6132c7565b60006001600160a01b0383163b61333957506000610cfd565b6060836001600160a01b0316836040516133539190614242565b6000604051808303816000865af19150503d8060008114613390576040519150601f19603f3d011682016040523d82523d6000602084013e613395565b606091505b5090925090508180156112445750805115806112445750808060200190518101906112449190614061565b6000816133cf57506000919050565b600182608081901c156133e75760409190911b9060801c5b604081901c156133fc5760209190911b9060401c5b602081901c156134115760109190911b9060201c5b601081901c156134265760089190911b9060101c5b600881901c1561343b5760049190911b9060081c5b600481901c156134505760029190911b9060041c5b600281901c1561346257600182901b91505b60018285816134735761347361454a565b048301901c9150600182858161348b5761348b61454a565b048301901c915060018285816134a3576134a361454a565b048301901c915060018285816134bb576134bb61454a565b048301901c915060018285816134d3576134d361454a565b048301901c915060018285816134eb576134eb61454a565b048301901c915060018285816135035761350361454a565b048301901c91506112448283868161351d5761351d61454a565b04613d04565b6000613532600284841861446f565b610c9590848416614457565b6001600160a01b038316600090815260036020526040812080548392906135669084906144a2565b90915550506001600160a01b03808316600081815260036020526040908190208054850190555190918516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612e9d9085815260200190565b816001600160a01b0316836001600160a01b0316141580156135e35750600081115b1561305e576001600160a01b03831615613671576001600160a01b0383166000908152601360205260408120819061361e906138fd85613909565b91509150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051613666929190918252602082015260400190565b60405180910390a250505b6001600160a01b0382161561305e576001600160a01b038216600090815260136020526040812081906136a790613cf885613909565b91509150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516136ef929190918252602082015260400190565b60405180910390a25050505050565b6001600160a01b03831661374a5760405162461bcd60e51b815260206004820152601360248201527229222a1d211d2d22a927afa922a1a2a4ab22a960691b6044820152606401610d6d565b8461378b5760405162461bcd60e51b81526020600482015260116024820152705244543a423a5a45524f5f53484152455360781b6044820152606401610d6d565b836137cc5760405162461bcd60e51b81526020600482015260116024820152705244543a423a5a45524f5f41535345545360781b6044820152606401610d6d565b816001600160a01b0316816001600160a01b0316146137f0576137f0828287612fdd565b6137fa8286613d1a565b600084613805610b4e565b61380f91906144a2565b600981905590506000613820613d96565b9050836001600160a01b0316856001600160a01b0316846001600160a01b03167ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db898b60405161387a929190918252602082015260400190565b60405180910390a46040805183815260208101839052600080516020614585833981519152910160405180910390a16006546138c0906001600160a01b03168688613dbb565b6124d25760405162461bcd60e51b815260206004820152600e60248201526d29222a1d211d2a2920a729a322a960911b6044820152606401610d6d565b6000610c9582846144a2565b825460009081908181156139635761392687612f786001856144a2565b60408051606081018252915463ffffffff811683526001600160601b03600160201b820481166020850152600160801b9091041690820152613981565b60408051606081018252600080825260208201819052918101919091525b905080602001516001600160601b031693506139a184868863ffffffff16565b92506000821180156139b95750805163ffffffff1643145b15613a53576139c78361326e565b6139d688612f786001866144a2565b80546001600160601b0392909216600160201b026fffffffffffffffffffffffff0000000019909216919091179055613a1161179384610c65565b613a2088612f786001866144a2565b80546001600160601b0392909216600160801b026bffffffffffffffffffffffff60801b19909216919091179055613b17565b866040518060600160405280613a6843613214565b63ffffffff168152602001613a7c8661326e565b6001600160601b03168152602001613a9661179387610c65565b6001600160601b0390811690915282546001810184556000938452602093849020835191018054948401516040909401518316600160801b026bffffffffffffffffffffffff60801b1994909316600160201b026fffffffffffffffffffffffffffffffff1990951663ffffffff9092169190911793909317919091161790555b5050935093915050565b6001600160a01b038216613b6d5760405162461bcd60e51b815260206004820152601360248201527229222a1d269d2d22a927afa922a1a2a4ab22a960691b6044820152606401610d6d565b83613bae5760405162461bcd60e51b81526020600482015260116024820152705244543a4d3a5a45524f5f53484152455360781b6044820152606401610d6d565b82613bef5760405162461bcd60e51b81526020600482015260116024820152705244543a4d3a5a45524f5f41535345545360781b6044820152606401610d6d565b613bf98285613dee565b600083613c04610b4e565b613c0e9190614457565b600981905590506000613c1f613d96565b9050836001600160a01b0316836001600160a01b03167fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d78789604051613c6f929190918252602082015260400190565b60405180910390a36040805183815260208101839052600080516020614585833981519152910160405180910390a1600654613cb6906001600160a01b0316843088612dda565b6131925760405162461bcd60e51b81526020600482015260136024820152725244543a4d3a5452414e534645525f46524f4d60681b6044820152606401610d6d565b6000610c958284614457565b6000818310613d135781610c95565b5090919050565b6001600160a01b03821660009081526003602052604081208054839290613d429084906144a2565b90915550506002805482900390556040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6000600c5442600b81905511613dae57600a54613db1565b60005b600a819055905090565b6040516001600160a01b03831660248201526044810182905260009061124490859063a9059cbb60e01b90606401612e11565b8060026000828254613e009190614457565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101613d8a565b80356001600160a01b0381168114613e6957600080fd5b919050565b803560ff81168114613e6957600080fd5b600060208284031215613e9157600080fd5b610c9582613e52565b60008060408385031215613ead57600080fd5b613eb683613e52565b9150613ec460208401613e52565b90509250929050565b600080600060608486031215613ee257600080fd5b613eeb84613e52565b9250613ef960208501613e52565b9150604084013590509250925092565b600080600080600080600060e0888a031215613f2457600080fd5b613f2d88613e52565b9650613f3b60208901613e52565b95506040880135945060608801359350613f5760808901613e6e565b925060a0880135915060c0880135905092959891949750929550565b60008060408385031215613f8657600080fd5b613f8f83613e52565b91506020830135613f9f81614576565b809150509250929050565b60008060408385031215613fbd57600080fd5b613fc683613e52565b946020939093013593505050565b60008060008060008060c08789031215613fed57600080fd5b613ff687613e52565b9550602087013594506040870135935061401260608801613e6e565b92506080870135915060a087013590509295509295509295565b6000806040838503121561403f57600080fd5b61404883613e52565b9150602083013563ffffffff81168114613f9f57600080fd5b60006020828403121561407357600080fd5b8151610c9581614576565b60006020828403121561409057600080fd5b5035919050565b6000602082840312156140a957600080fd5b5051919050565b600080604083850312156140c357600080fd5b82359150613ec460208401613e52565b6000806000606084860312156140e857600080fd5b833592506140f860208501613e52565b915061410660408501613e52565b90509250925092565b6000806000806080858703121561412557600080fd5b8435935061413560208601613e52565b925061414360408601613e52565b9396929550929360600135925050565b60008060006060848603121561416857600080fd5b83359250613ef960208501613e52565b600080600080600080600060e0888a03121561419357600080fd5b87359650613f3b60208901613e52565b60008060008060008060c087890312156141bc57600080fd5b863595506141cc60208801613e52565b94506040870135935061401260608801613e6e565b600080604083850312156141f457600080fd5b50508035926020909101359150565b63ffffffff8082511683528060208301511660208401525060408101516001600160601b03808216604085015280606084015116606085015250505050565b600082516142548184602087016144b9565b9190910192915050565b600080835481600182811c91508083168061427a57607f831692505b602080841082141561429a57634e487b7160e01b86526022600452602486fd5b8180156142ae57600181146142bf576142ec565b60ff198616895284890196506142ec565b60008a81526020902060005b868110156142e45781548b8201529085019083016142cb565b505084890196505b509498975050505050505050565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6020808252825182820181905260009190848201906040850190845b8181101561437d5761436a838551614203565b9284019260809290920191600101614357565b50909695505050505050565b60208152600082518060208401526143a88160408501602087016144b9565b601f01601f19169190910160400192915050565b6020808252600a90820152691491150e9313d0d2d15160b21b604082015260600190565b6020808252601590820152742629222a1d21a0a62622a92fa727aa2fa7aba722a960591b604082015260600190565b60808101610cfd8284614203565b60a0810161442b8285614203565b8260808301529392505050565b60c081016144468286614203565b608082019390935260a00152919050565b6000821982111561446a5761446a614534565b500190565b60008261447e5761447e61454a565b500490565b600081600019048311821515161561449d5761449d614534565b500290565b6000828210156144b4576144b4614534565b500390565b60005b838110156144d45781810151838201526020016144bc565b8381111561301b5750506000910152565b600181811c908216806144f957607f821691505b6020821081141561451a57634e487b7160e01b600052602260045260246000fd5b50919050565b60008261452f5761452f61454a565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b801515811461160657600080fdfe68b521a89bf844ff03e484d89fd64ce292a698ec522170f0dad7ecd11c2dc8faa26469706673582212202bcddbab3c0412787a12419ee8566b5a922eae59af354056db055eb78427457364736f6c63430008070033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000f7a171c168a35ae858fd18bec5bb7c28134b823f000000000000000000000000c28eb2250d1ae32c7e74cfb6d6b86afc9beb65090000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000eff1000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000001b5374616b6564204f70656e2045636f73797374656d20546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000004784f504e00000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : name_ (string): Staked Open Ecosystem Token
Arg [1] : symbol_ (string): xOPN
Arg [2] : owner_ (address): 0xF7a171C168A35ae858FD18BEc5Bb7C28134B823F
Arg [3] : asset_ (address): 0xc28eb2250d1AE32c7E74CFb6d6b86afC9BEb6509
Arg [4] : precision_ (uint256): 79228162514264337593543950335
Arg [5] : instantWithdrawalFee_ (uint256): 90
Arg [6] : lockTime_ (uint256): 15724800
Arg [7] : initialSeed_ (uint256): 1000000000000000000

-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [2] : 000000000000000000000000f7a171c168a35ae858fd18bec5bb7c28134b823f
Arg [3] : 000000000000000000000000c28eb2250d1ae32c7e74cfb6d6b86afc9beb6509
Arg [4] : 0000000000000000000000000000000000000000ffffffffffffffffffffffff
Arg [5] : 000000000000000000000000000000000000000000000000000000000000005a
Arg [6] : 0000000000000000000000000000000000000000000000000000000000eff100
Arg [7] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [8] : 000000000000000000000000000000000000000000000000000000000000001b
Arg [9] : 5374616b6564204f70656e2045636f73797374656d20546f6b656e0000000000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [11] : 784f504e00000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

OVERVIEW

The OPEN Ticketing Ecosystem, which has issued over 5 million onchain tickets globally, offers tools for integrators, event organisers and artists to regain control of their ticketing and explore new financing, access and fan relationship opportunities.

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.