Sonic Blaze Testnet

Contract

0x3eF6583234e7515f9617363D7FA609b842b5F535

Overview

S Balance

Sonic Blaze LogoSonic Blaze LogoSonic Blaze Logo0 S

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Latest 2 internal transactions

Parent Transaction Hash Block From To
43749722024-12-15 20:56:416 days ago1734296201
0x3eF65832...842b5F535
 Contract Creation0 S
43749722024-12-15 20:56:416 days ago1734296201  Contract Creation0 S
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Governance

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 22 : Governance.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IERC20} from "openzeppelin/contracts/interfaces/IERC20.sol";
import {SafeERC20} from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "openzeppelin/contracts/utils/ReentrancyGuard.sol";

import {IGovernance} from "./interfaces/IGovernance.sol";
import {IInitiative} from "./interfaces/IInitiative.sol";
import {ILQTYStaking} from "./interfaces/ILQTYStaking.sol";

import {UserProxy} from "./UserProxy.sol";
import {UserProxyFactory} from "./UserProxyFactory.sol";

import {add, max} from "./utils/Math.sol";
import {_requireNoDuplicates, _requireNoNegatives} from "./utils/UniqueArray.sol";
import {Multicall} from "./utils/Multicall.sol";
import {WAD, PermitParams} from "./utils/Types.sol";
import {safeCallWithMinGas} from "./utils/SafeCallMinGas.sol";
import {Ownable} from "./utils/Ownable.sol";

/// @title Governance: Modular Initiative based Governance
contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, Ownable, IGovernance {
    using SafeERC20 for IERC20;

    uint256 constant MIN_GAS_TO_HOOK = 350_000;

    /// Replace this to ensure hooks have sufficient gas

    /// @inheritdoc IGovernance
    ILQTYStaking public immutable stakingV1;
    /// @inheritdoc IGovernance
    IERC20 public immutable lqty;
    /// @inheritdoc IGovernance
    IERC20 public immutable bold;
    /// @inheritdoc IGovernance
    uint256 public immutable EPOCH_START;
    /// @inheritdoc IGovernance
    uint256 public immutable EPOCH_DURATION;
    /// @inheritdoc IGovernance
    uint256 public immutable EPOCH_VOTING_CUTOFF;
    /// @inheritdoc IGovernance
    uint256 public immutable MIN_CLAIM;
    /// @inheritdoc IGovernance
    uint256 public immutable MIN_ACCRUAL;
    /// @inheritdoc IGovernance
    uint256 public immutable REGISTRATION_FEE;
    /// @inheritdoc IGovernance
    uint256 public immutable REGISTRATION_THRESHOLD_FACTOR;
    /// @inheritdoc IGovernance
    uint256 public immutable UNREGISTRATION_THRESHOLD_FACTOR;
    /// @inheritdoc IGovernance
    uint256 public immutable REGISTRATION_WARM_UP_PERIOD;
    /// @inheritdoc IGovernance
    uint256 public immutable UNREGISTRATION_AFTER_EPOCHS;
    /// @inheritdoc IGovernance
    uint256 public immutable VOTING_THRESHOLD_FACTOR;

    /// @inheritdoc IGovernance
    uint256 public boldAccrued;

    /// @inheritdoc IGovernance
    VoteSnapshot public votesSnapshot;
    /// @inheritdoc IGovernance
    mapping(address => InitiativeVoteSnapshot) public votesForInitiativeSnapshot;

    /// @inheritdoc IGovernance
    GlobalState public globalState;
    /// @inheritdoc IGovernance
    mapping(address => UserState) public userStates;
    /// @inheritdoc IGovernance
    mapping(address => InitiativeState) public initiativeStates;
    /// @inheritdoc IGovernance
    mapping(address => mapping(address => Allocation)) public lqtyAllocatedByUserToInitiative;
    /// @inheritdoc IGovernance
    mapping(address => uint16) public override registeredInitiatives;

    uint16 constant UNREGISTERED_INITIATIVE = type(uint16).max;

    // 100 Million LQTY will be necessary to make the rounding error cause 1 second of loss per operation
    uint120 public constant TIMESTAMP_PRECISION = 1e26;

    constructor(
        address _lqty,
        address _lusd,
        address _stakingV1,
        address _bold,
        Configuration memory _config,
        address _owner,
        address[] memory _initiatives
    ) UserProxyFactory(_lqty, _lusd, _stakingV1) Ownable(_owner) {
        stakingV1 = ILQTYStaking(_stakingV1);
        lqty = IERC20(_lqty);
        bold = IERC20(_bold);
        require(_config.minClaim <= _config.minAccrual, "Gov: min-claim-gt-min-accrual");
        REGISTRATION_FEE = _config.registrationFee;

        // Registration threshold must be below 100% of votes
        require(_config.registrationThresholdFactor < WAD, "Gov: registration-config");
        REGISTRATION_THRESHOLD_FACTOR = _config.registrationThresholdFactor;

        // Unregistration must be X times above the `votingThreshold`
        require(_config.unregistrationThresholdFactor > WAD, "Gov: unregistration-config");
        UNREGISTRATION_THRESHOLD_FACTOR = _config.unregistrationThresholdFactor;

        REGISTRATION_WARM_UP_PERIOD = _config.registrationWarmUpPeriod;
        UNREGISTRATION_AFTER_EPOCHS = _config.unregistrationAfterEpochs;

        // Voting threshold must be below 100% of votes
        require(_config.votingThresholdFactor < WAD, "Gov: voting-config");
        VOTING_THRESHOLD_FACTOR = _config.votingThresholdFactor;

        MIN_CLAIM = _config.minClaim;
        MIN_ACCRUAL = _config.minAccrual;
        EPOCH_START = _config.epochStart;
        require(_config.epochDuration > 0, "Gov: epoch-duration-zero");
        EPOCH_DURATION = _config.epochDuration;
        require(_config.epochVotingCutoff < _config.epochDuration, "Gov: epoch-voting-cutoff-gt-epoch-duration");
        EPOCH_VOTING_CUTOFF = _config.epochVotingCutoff;

        if (_initiatives.length > 0) {
            registerInitialInitiatives(_initiatives);
        }
    }

    function registerInitialInitiatives(address[] memory _initiatives) public onlyOwner {
        for (uint256 i = 0; i < _initiatives.length; i++) {
            // Register initial initiatives in the earliest possible epoch, which lets us make them votable immediately
            // post-deployment if we so choose, by backdating the first epoch at least EPOCH_DURATION in the past.
            registeredInitiatives[_initiatives[i]] = 1;

            emit RegisterInitiative(_initiatives[i], msg.sender, 1);
        }

        _renounceOwnership();
    }

    function _averageAge(uint120 _currentTimestamp, uint120 _averageTimestamp) internal pure returns (uint120) {
        if (_averageTimestamp == 0 || _currentTimestamp < _averageTimestamp) return 0;
        return _currentTimestamp - _averageTimestamp;
    }

    function _calculateAverageTimestamp(
        uint120 _prevOuterAverageTimestamp,
        uint120 _newInnerAverageTimestamp,
        uint88 _prevLQTYBalance,
        uint88 _newLQTYBalance
    ) internal view returns (uint120) {
        if (_newLQTYBalance == 0) return 0;

        // NOTE: Truncation
        // NOTE: u32 -> u120
        // While we upscale the Timestamp, the system will stop working at type(uint32).max
        // Because the rest of the type is used for precision
        uint120 currentTime = uint120(uint32(block.timestamp)) * uint120(TIMESTAMP_PRECISION);

        uint120 prevOuterAverageAge = _averageAge(currentTime, _prevOuterAverageTimestamp);
        uint120 newInnerAverageAge = _averageAge(currentTime, _newInnerAverageTimestamp);

        // 120 for timestamps = 2^32 * 1e18 | 2^32 * 1e26
        // 208 for voting power = 2^120 * 2^88
        // NOTE: 208 / X can go past u120!
        // Therefore we keep `newOuterAverageAge` as u208
        uint208 newOuterAverageAge;
        if (_prevLQTYBalance <= _newLQTYBalance) {
            uint88 deltaLQTY = _newLQTYBalance - _prevLQTYBalance;
            uint208 prevVotes = uint208(_prevLQTYBalance) * uint208(prevOuterAverageAge);
            uint208 newVotes = uint208(deltaLQTY) * uint208(newInnerAverageAge);
            uint208 votes = prevVotes + newVotes;
            newOuterAverageAge = votes / _newLQTYBalance;
        } else {
            uint88 deltaLQTY = _prevLQTYBalance - _newLQTYBalance;
            uint208 prevVotes = uint208(_prevLQTYBalance) * uint208(prevOuterAverageAge);
            uint208 newVotes = uint208(deltaLQTY) * uint208(newInnerAverageAge);
            uint208 votes = (prevVotes >= newVotes) ? prevVotes - newVotes : 0;
            newOuterAverageAge = votes / _newLQTYBalance;
        }

        if (newOuterAverageAge > currentTime) return 0;
        return uint120(currentTime - newOuterAverageAge);
    }

    /*//////////////////////////////////////////////////////////////
                                STAKING
    //////////////////////////////////////////////////////////////*/

    function _updateUserTimestamp(uint88 _lqtyAmount) private returns (UserProxy) {
        require(_lqtyAmount > 0, "Governance: zero-lqty-amount");

        // Assert that we have resetted here
        UserState memory userState = userStates[msg.sender];
        require(userState.allocatedLQTY == 0, "Governance: must-be-zero-allocation");

        address userProxyAddress = deriveUserProxyAddress(msg.sender);

        if (userProxyAddress.code.length == 0) {
            deployUserProxy();
        }

        UserProxy userProxy = UserProxy(payable(userProxyAddress));

        uint88 lqtyStaked = uint88(stakingV1.stakes(userProxyAddress));

        // update the average staked timestamp for LQTY staked by the user

        // NOTE: Upscale user TS by `TIMESTAMP_PRECISION`
        userState.averageStakingTimestamp = _calculateAverageTimestamp(
            userState.averageStakingTimestamp,
            uint120(block.timestamp) * uint120(TIMESTAMP_PRECISION),
            lqtyStaked,
            lqtyStaked + _lqtyAmount
        );
        userStates[msg.sender] = userState;

        emit DepositLQTY(msg.sender, _lqtyAmount);

        return userProxy;
    }

    /// @inheritdoc IGovernance
    function depositLQTY(uint88 _lqtyAmount) external {
        depositLQTY(_lqtyAmount, false, msg.sender);
    }

    function depositLQTY(uint88 _lqtyAmount, bool _doSendRewards, address _recipient) public nonReentrant {
        UserProxy userProxy = _updateUserTimestamp(_lqtyAmount);
        userProxy.stake(_lqtyAmount, msg.sender, _doSendRewards, _recipient);
    }

    /// @inheritdoc IGovernance
    function depositLQTYViaPermit(uint88 _lqtyAmount, PermitParams calldata _permitParams) external {
        depositLQTYViaPermit(_lqtyAmount, _permitParams, false, msg.sender);
    }

    function depositLQTYViaPermit(
        uint88 _lqtyAmount,
        PermitParams calldata _permitParams,
        bool _doSendRewards,
        address _recipient
    ) public nonReentrant {
        UserProxy userProxy = _updateUserTimestamp(_lqtyAmount);
        userProxy.stakeViaPermit(_lqtyAmount, msg.sender, _permitParams, _doSendRewards, _recipient);
    }

    /// @inheritdoc IGovernance
    function withdrawLQTY(uint88 _lqtyAmount) external {
        withdrawLQTY(_lqtyAmount, true, msg.sender);
    }

    function withdrawLQTY(uint88 _lqtyAmount, bool _doSendRewards, address _recipient) public nonReentrant {
        // check that user has reset before changing lqty balance
        UserState storage userState = userStates[msg.sender];
        require(userState.allocatedLQTY == 0, "Governance: must-allocate-zero");

        UserProxy userProxy = UserProxy(payable(deriveUserProxyAddress(msg.sender)));
        require(address(userProxy).code.length != 0, "Governance: user-proxy-not-deployed");

        uint88 lqtyStaked = uint88(stakingV1.stakes(address(userProxy)));

        (uint256 accruedLUSD, uint256 accruedETH) = userProxy.unstake(_lqtyAmount, _doSendRewards, _recipient);

        emit WithdrawLQTY(msg.sender, _lqtyAmount, accruedLUSD, accruedETH);
    }

    /// @inheritdoc IGovernance
    function claimFromStakingV1(address _rewardRecipient) external returns (uint256 accruedLUSD, uint256 accruedETH) {
        address payable userProxyAddress = payable(deriveUserProxyAddress(msg.sender));
        require(userProxyAddress.code.length != 0, "Governance: user-proxy-not-deployed");
        return UserProxy(userProxyAddress).unstake(0, true, _rewardRecipient);
    }

    /*//////////////////////////////////////////////////////////////
                                 VOTING
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IGovernance
    function epoch() public view returns (uint16) {
        if (block.timestamp < EPOCH_START) {
            return 0;
        }
        return uint16(((block.timestamp - EPOCH_START) / EPOCH_DURATION) + 1);
    }

    /// @inheritdoc IGovernance
    function epochStart() public view returns (uint32) {
        uint16 currentEpoch = epoch();
        if (currentEpoch == 0) return 0;
        return uint32(EPOCH_START + (currentEpoch - 1) * EPOCH_DURATION);
    }

    /// @inheritdoc IGovernance
    function secondsWithinEpoch() public view returns (uint32) {
        if (block.timestamp < EPOCH_START) return 0;
        return uint32((block.timestamp - EPOCH_START) % EPOCH_DURATION);
    }

    /// @inheritdoc IGovernance
    function lqtyToVotes(uint88 _lqtyAmount, uint120 _currentTimestamp, uint120 _averageTimestamp)
        public
        pure
        returns (uint208)
    {
        return uint208(_lqtyAmount) * uint208(_averageAge(_currentTimestamp, _averageTimestamp));
    }

    /*//////////////////////////////////////////////////////////////
                                 SNAPSHOTS
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IGovernance
    function getLatestVotingThreshold() public view returns (uint256) {
        uint256 snapshotVotes = votesSnapshot.votes;
        /// @audit technically can be out of synch

        return calculateVotingThreshold(snapshotVotes);
    }

    /// @dev Returns the most up to date voting threshold
    /// In contrast to `getLatestVotingThreshold` this function updates the snapshot
    /// This ensures that the value returned is always the latest
    function calculateVotingThreshold() public returns (uint256) {
        (VoteSnapshot memory snapshot,) = _snapshotVotes();

        return calculateVotingThreshold(snapshot.votes);
    }

    /// @dev Utility function to compute the threshold votes without recomputing the snapshot
    /// Note that `boldAccrued` is a cached value, this function works correctly only when called after an accrual
    function calculateVotingThreshold(uint256 _votes) public view returns (uint256) {
        if (_votes == 0) return 0;

        uint256 minVotes; // to reach MIN_CLAIM: snapshotVotes * MIN_CLAIM / boldAccrued
        uint256 payoutPerVote = boldAccrued * WAD / _votes;
        if (payoutPerVote != 0) {
            minVotes = MIN_CLAIM * WAD / payoutPerVote;
        }
        return max(_votes * VOTING_THRESHOLD_FACTOR / WAD, minVotes);
    }

    // Snapshots votes at the end of the previous epoch
    // Accrues funds until the first activity of the current epoch, which are valid throughout all of the current epoch
    function _snapshotVotes() internal returns (VoteSnapshot memory snapshot, GlobalState memory state) {
        bool shouldUpdate;
        (snapshot, state, shouldUpdate) = getTotalVotesAndState();

        if (shouldUpdate) {
            votesSnapshot = snapshot;
            uint256 boldBalance = bold.balanceOf(address(this));
            boldAccrued = (boldBalance < MIN_ACCRUAL) ? 0 : boldBalance;
            emit SnapshotVotes(snapshot.votes, snapshot.forEpoch);
        }
    }

    /// @notice Return the most up to date global snapshot and state as well as a flag to notify whether the state can be updated
    /// This is a convenience function to always retrieve the most up to date state values
    function getTotalVotesAndState()
        public
        view
        returns (VoteSnapshot memory snapshot, GlobalState memory state, bool shouldUpdate)
    {
        uint16 currentEpoch = epoch();
        snapshot = votesSnapshot;
        state = globalState;

        if (snapshot.forEpoch < currentEpoch - 1) {
            shouldUpdate = true;

            snapshot.votes = lqtyToVotes(
                state.countedVoteLQTY,
                uint120(epochStart()) * uint120(TIMESTAMP_PRECISION),
                state.countedVoteLQTYAverageTimestamp
            );
            snapshot.forEpoch = currentEpoch - 1;
        }
    }

    // Snapshots votes for an initiative for the previous epoch but only count the votes
    // if the received votes meet the voting threshold
    function _snapshotVotesForInitiative(address _initiative)
        internal
        returns (InitiativeVoteSnapshot memory initiativeSnapshot, InitiativeState memory initiativeState)
    {
        bool shouldUpdate;
        (initiativeSnapshot, initiativeState, shouldUpdate) = getInitiativeSnapshotAndState(_initiative);

        if (shouldUpdate) {
            votesForInitiativeSnapshot[_initiative] = initiativeSnapshot;
            emit SnapshotVotesForInitiative(_initiative, initiativeSnapshot.votes, initiativeSnapshot.forEpoch);
        }
    }

    /// @dev Given an initiative address, return it's most up to date snapshot and state as well as a flag to notify whether the state can be updated
    /// This is a convenience function to always retrieve the most up to date state values
    function getInitiativeSnapshotAndState(address _initiative)
        public
        view
        returns (
            InitiativeVoteSnapshot memory initiativeSnapshot,
            InitiativeState memory initiativeState,
            bool shouldUpdate
        )
    {
        // Get the storage data
        uint16 currentEpoch = epoch();
        initiativeSnapshot = votesForInitiativeSnapshot[_initiative];
        initiativeState = initiativeStates[_initiative];

        if (initiativeSnapshot.forEpoch < currentEpoch - 1) {
            shouldUpdate = true;

            uint120 start = uint120(epochStart()) * uint120(TIMESTAMP_PRECISION);
            uint208 votes =
                lqtyToVotes(initiativeState.voteLQTY, start, initiativeState.averageStakingTimestampVoteLQTY);
            uint208 vetos =
                lqtyToVotes(initiativeState.vetoLQTY, start, initiativeState.averageStakingTimestampVetoLQTY);
            // NOTE: Upscaling to u224 is safe
            initiativeSnapshot.votes = votes;
            initiativeSnapshot.vetos = vetos;

            initiativeSnapshot.forEpoch = currentEpoch - 1;
        }
    }

    /// @inheritdoc IGovernance
    function snapshotVotesForInitiative(address _initiative)
        external
        nonReentrant
        returns (VoteSnapshot memory voteSnapshot, InitiativeVoteSnapshot memory initiativeVoteSnapshot)
    {
        (voteSnapshot,) = _snapshotVotes();
        (initiativeVoteSnapshot,) = _snapshotVotesForInitiative(_initiative);
    }

    /*//////////////////////////////////////////////////////////////
                                 FSM
    //////////////////////////////////////////////////////////////*/

    enum InitiativeStatus {
        NONEXISTENT,
        /// This Initiative Doesn't exist | This is never returned
        WARM_UP,
        /// This epoch was just registered
        SKIP,
        /// This epoch will result in no rewards and no unregistering
        CLAIMABLE,
        /// This epoch will result in claiming rewards
        CLAIMED,
        /// The rewards for this epoch have been claimed
        UNREGISTERABLE,
        /// Can be unregistered
        DISABLED // It was already Unregistered

    }

    /// @notice Given an inititive address, updates all snapshots and return the initiative state
    ///     See the view version of `getInitiativeState` for the underlying logic on Initatives FSM
    function getInitiativeState(address _initiative)
        public
        returns (InitiativeStatus status, uint16 lastEpochClaim, uint256 claimableAmount)
    {
        (VoteSnapshot memory votesSnapshot_,) = _snapshotVotes();
        (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) =
            _snapshotVotesForInitiative(_initiative);

        return getInitiativeState(_initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState);
    }

    /// @dev Given an initiative address and its snapshot, determines the current state for an initiative
    function getInitiativeState(
        address _initiative,
        VoteSnapshot memory _votesSnapshot,
        InitiativeVoteSnapshot memory _votesForInitiativeSnapshot,
        InitiativeState memory _initiativeState
    ) public view returns (InitiativeStatus status, uint16 lastEpochClaim, uint256 claimableAmount) {
        // == Non existent Condition == //
        if (registeredInitiatives[_initiative] == 0) {
            return (InitiativeStatus.NONEXISTENT, 0, 0);
            /// By definition it has zero rewards
        }

        // == Just Registered Condition == //
        if (registeredInitiatives[_initiative] == epoch()) {
            return (InitiativeStatus.WARM_UP, 0, 0);
            /// Was registered this week, cannot have rewards
        }

        // Fetch last epoch at which we claimed
        lastEpochClaim = initiativeStates[_initiative].lastEpochClaim;

        // == Disabled Condition == //
        if (registeredInitiatives[_initiative] == UNREGISTERED_INITIATIVE) {
            return (InitiativeStatus.DISABLED, lastEpochClaim, 0);
            /// By definition it has zero rewards
        }

        // == Already Claimed Condition == //
        if (lastEpochClaim >= epoch() - 1) {
            // early return, we have already claimed
            return (InitiativeStatus.CLAIMED, lastEpochClaim, claimableAmount);
        }

        // NOTE: Pass the snapshot value so we get accurate result
        uint256 votingTheshold = calculateVotingThreshold(_votesSnapshot.votes);

        // If it's voted and can get rewards
        // Votes > calculateVotingThreshold
        // == Rewards Conditions (votes can be zero, logic is the same) == //

        // By definition if _votesForInitiativeSnapshot.votes > 0 then _votesSnapshot.votes > 0

        uint256 upscaledInitiativeVotes = uint256(_votesForInitiativeSnapshot.votes);
        uint256 upscaledInitiativeVetos = uint256(_votesForInitiativeSnapshot.vetos);
        uint256 upscaledTotalVotes = uint256(_votesSnapshot.votes);

        if (upscaledInitiativeVotes > votingTheshold && !(upscaledInitiativeVetos >= upscaledInitiativeVotes)) {
            /// @audit 2^208 means we only have 2^48 left
            /// Therefore we need to scale the value down by 4 orders of magnitude to make it fit
            assert(upscaledInitiativeVotes * 1e14 / (VOTING_THRESHOLD_FACTOR / 1e4) > upscaledTotalVotes);

            // 34 times when using 0.03e18 -> 33.3 + 1-> 33 + 1 = 34
            uint256 CUSTOM_PRECISION = WAD / VOTING_THRESHOLD_FACTOR + 1;

            /// @audit Because of the updated timestamp, we can run into overflows if we multiply by `boldAccrued`
            ///     We use `CUSTOM_PRECISION` for this reason, a smaller multiplicative value
            ///     The change SHOULD be safe because we already check for `threshold` before getting into these lines
            /// As an alternative, this line could be replaced by https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
            uint256 claim =
                upscaledInitiativeVotes * CUSTOM_PRECISION / upscaledTotalVotes * boldAccrued / CUSTOM_PRECISION;
            return (InitiativeStatus.CLAIMABLE, lastEpochClaim, claim);
        }

        // == Unregister Condition == //
        // e.g. if `UNREGISTRATION_AFTER_EPOCHS` is 4, the 4th epoch flip that would result in SKIP, will result in the initiative being `UNREGISTERABLE`
        if (
            (_initiativeState.lastEpochClaim + UNREGISTRATION_AFTER_EPOCHS < epoch() - 1)
                || upscaledInitiativeVetos > upscaledInitiativeVotes
                    && upscaledInitiativeVetos > votingTheshold * UNREGISTRATION_THRESHOLD_FACTOR / WAD
        ) {
            return (InitiativeStatus.UNREGISTERABLE, lastEpochClaim, 0);
        }

        // == Not meeting threshold Condition == //
        return (InitiativeStatus.SKIP, lastEpochClaim, 0);
    }

    /// @inheritdoc IGovernance
    function registerInitiative(address _initiative) external nonReentrant {
        bold.safeTransferFrom(msg.sender, address(this), REGISTRATION_FEE);

        require(_initiative != address(0), "Governance: zero-address");
        (InitiativeStatus status,,) = getInitiativeState(_initiative);
        require(status == InitiativeStatus.NONEXISTENT, "Governance: initiative-already-registered");

        address userProxyAddress = deriveUserProxyAddress(msg.sender);
        (VoteSnapshot memory snapshot,) = _snapshotVotes();
        UserState memory userState = userStates[msg.sender];

        // an initiative can be registered if the registrant has more voting power (LQTY * age)
        // than the registration threshold derived from the previous epoch's total global votes

        uint256 upscaledSnapshotVotes = uint256(snapshot.votes);
        require(
            lqtyToVotes(
                uint88(stakingV1.stakes(userProxyAddress)),
                uint120(epochStart()) * uint120(TIMESTAMP_PRECISION),
                userState.averageStakingTimestamp
            ) >= upscaledSnapshotVotes * REGISTRATION_THRESHOLD_FACTOR / WAD,
            "Governance: insufficient-lqty"
        );

        uint16 currentEpoch = epoch();

        registeredInitiatives[_initiative] = currentEpoch;

        /// @audit This ensures that the initiatives has UNREGISTRATION_AFTER_EPOCHS even after the first epoch
        initiativeStates[_initiative].lastEpochClaim = currentEpoch - 1;

        emit RegisterInitiative(_initiative, msg.sender, currentEpoch);

        // Replaces try / catch | Enforces sufficient gas is passed
        safeCallWithMinGas(
            _initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onRegisterInitiative, (currentEpoch))
        );
    }

    struct ResetInitiativeData {
        address initiative;
        int88 LQTYVotes;
        int88 LQTYVetos;
    }

    /// @dev Resets an initiative and return the previous votes
    /// NOTE: Technically we don't need vetos
    /// NOTE: Technically we want to populate the `ResetInitiativeData` only when `secondsWithinEpoch() > EPOCH_VOTING_CUTOFF`
    function _resetInitiatives(address[] calldata _initiativesToReset)
        internal
        returns (ResetInitiativeData[] memory)
    {
        ResetInitiativeData[] memory cachedData = new ResetInitiativeData[](_initiativesToReset.length);

        int88[] memory deltaLQTYVotes = new int88[](_initiativesToReset.length);
        int88[] memory deltaLQTYVetos = new int88[](_initiativesToReset.length);

        // Prepare reset data
        for (uint256 i; i < _initiativesToReset.length; i++) {
            Allocation memory alloc = lqtyAllocatedByUserToInitiative[msg.sender][_initiativesToReset[i]];
            require(alloc.voteLQTY > 0 || alloc.vetoLQTY > 0, "Governance: nothing to reset");

            // Must be below, else we cannot reset"
            // Makes cast safe
            /// @audit Check INVARIANT: property_ensure_user_alloc_cannot_dos
            assert(alloc.voteLQTY <= uint88(type(int88).max));
            assert(alloc.vetoLQTY <= uint88(type(int88).max));

            // Cache, used to enforce limits later
            cachedData[i] = ResetInitiativeData({
                initiative: _initiativesToReset[i],
                LQTYVotes: int88(alloc.voteLQTY),
                LQTYVetos: int88(alloc.vetoLQTY)
            });

            // -0 is still 0, so its fine to flip both
            deltaLQTYVotes[i] = -int88(cachedData[i].LQTYVotes);
            deltaLQTYVetos[i] = -int88(cachedData[i].LQTYVetos);
        }

        // RESET HERE || All initiatives will receive most updated data and 0 votes / vetos
        _allocateLQTY(_initiativesToReset, deltaLQTYVotes, deltaLQTYVetos);

        return cachedData;
    }

    /// @notice Reset the allocations for the initiatives being passed, must pass all initiatives else it will revert
    ///     NOTE: If you reset at the last day of the epoch, you won't be able to vote again
    ///         Use `allocateLQTY` to reset and vote
    function resetAllocations(address[] calldata _initiativesToReset, bool checkAll) external nonReentrant {
        _requireNoDuplicates(_initiativesToReset);
        _resetInitiatives(_initiativesToReset);

        // NOTE: In most cases, the check will pass
        // But if you allocate too many initiatives, we may run OOG
        // As such the check is optional here
        // All other calls to the system enforce this
        // So it's recommended that your last call to `resetAllocations` passes the check
        if (checkAll) {
            require(userStates[msg.sender].allocatedLQTY == 0, "Governance: must be a reset");
        }
    }

    /// @inheritdoc IGovernance
    function allocateLQTY(
        address[] calldata _initiativesToReset,
        address[] calldata _initiatives,
        int88[] calldata _absoluteLQTYVotes,
        int88[] calldata _absoluteLQTYVetos
    ) external nonReentrant {
        require(_initiatives.length == _absoluteLQTYVotes.length, "Length");
        require(_absoluteLQTYVetos.length == _absoluteLQTYVotes.length, "Length");

        // To ensure the change is safe, enforce uniqueness
        _requireNoDuplicates(_initiativesToReset);
        _requireNoDuplicates(_initiatives);

        // Explicit >= 0 checks for all values since we reset values below
        _requireNoNegatives(_absoluteLQTYVotes);
        _requireNoNegatives(_absoluteLQTYVetos);
        // If the goal is to remove all votes from an initiative, including in _initiativesToReset is enough
        _requireNoNOP(_absoluteLQTYVotes, _absoluteLQTYVetos);

        // You MUST always reset
        ResetInitiativeData[] memory cachedData = _resetInitiatives(_initiativesToReset);

        /// Invariant, 0 allocated = 0 votes
        UserState memory userState = userStates[msg.sender];
        require(userState.allocatedLQTY == 0, "must be a reset");

        // After cutoff you can only re-apply the same vote
        // Or vote less
        // Or abstain
        // You can always add a veto, hence we only validate the addition of Votes
        // And ignore the addition of vetos
        // Validate the data here to ensure that the voting is capped at the amount in the other case
        if (secondsWithinEpoch() > EPOCH_VOTING_CUTOFF) {
            // Cap the max votes to the previous cache value
            // This means that no new votes can happen here

            // Removing and VETOING is always accepted
            for (uint256 x; x < _initiatives.length; x++) {
                // If we find it, we ensure it cannot be an increase
                bool found;
                for (uint256 y; y < cachedData.length; y++) {
                    if (cachedData[y].initiative == _initiatives[x]) {
                        found = true;
                        require(_absoluteLQTYVotes[x] <= cachedData[y].LQTYVotes, "Cannot increase");
                        break;
                    }
                }

                // Else we assert that the change is a veto, because by definition the initiatives will have received zero votes past this line
                if (!found) {
                    require(_absoluteLQTYVotes[x] == 0, "Must be zero for new initiatives");
                }
            }
        }

        // Vote here, all values are now absolute changes
        _allocateLQTY(_initiatives, _absoluteLQTYVotes, _absoluteLQTYVetos);
    }

    /// @dev For each given initiative applies relative changes to the allocation
    /// NOTE: Given the current usage the function either: Resets the value to 0, or sets the value to a new value
    ///     Review the flows as the function could be used in many ways, but it ends up being used in just those 2 ways
    function _allocateLQTY(
        address[] memory _initiatives,
        int88[] memory _deltaLQTYVotes,
        int88[] memory _deltaLQTYVetos
    ) internal {
        require(
            _initiatives.length == _deltaLQTYVotes.length && _initiatives.length == _deltaLQTYVetos.length,
            "Governance: array-length-mismatch"
        );

        (VoteSnapshot memory votesSnapshot_, GlobalState memory state) = _snapshotVotes();
        uint16 currentEpoch = epoch();
        UserState memory userState = userStates[msg.sender];

        for (uint256 i = 0; i < _initiatives.length; i++) {
            address initiative = _initiatives[i];
            int88 deltaLQTYVotes = _deltaLQTYVotes[i];
            int88 deltaLQTYVetos = _deltaLQTYVetos[i];
            assert(deltaLQTYVotes != 0 || deltaLQTYVetos != 0);

            /// === Check FSM === ///
            // Can vote positively in SKIP, CLAIMABLE, CLAIMED and UNREGISTERABLE states
            // Force to remove votes if disabled
            // Can remove votes and vetos in every stage
            (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) =
                _snapshotVotesForInitiative(initiative);

            (InitiativeStatus status,,) =
                getInitiativeState(initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState);

            if (deltaLQTYVotes > 0 || deltaLQTYVetos > 0) {
                /// @audit You cannot vote on `unregisterable` but a vote may have been there
                require(
                    status == InitiativeStatus.SKIP || status == InitiativeStatus.CLAIMABLE
                        || status == InitiativeStatus.CLAIMED,
                    "Governance: active-vote-fsm"
                );
            }

            if (status == InitiativeStatus.DISABLED) {
                require(deltaLQTYVotes <= 0 && deltaLQTYVetos <= 0, "Must be a withdrawal");
            }

            /// === UPDATE ACCOUNTING === ///
            // == INITIATIVE STATE == //

            // deep copy of the initiative's state before the allocation
            InitiativeState memory prevInitiativeState = InitiativeState(
                initiativeState.voteLQTY,
                initiativeState.vetoLQTY,
                initiativeState.averageStakingTimestampVoteLQTY,
                initiativeState.averageStakingTimestampVetoLQTY,
                initiativeState.lastEpochClaim
            );

            // update the average staking timestamp for the initiative based on the user's average staking timestamp
            initiativeState.averageStakingTimestampVoteLQTY = _calculateAverageTimestamp(
                initiativeState.averageStakingTimestampVoteLQTY,
                userState.averageStakingTimestamp,
                /// @audit This is wrong unless we enforce a reset on deposit and withdrawal
                initiativeState.voteLQTY,
                add(initiativeState.voteLQTY, deltaLQTYVotes)
            );
            initiativeState.averageStakingTimestampVetoLQTY = _calculateAverageTimestamp(
                initiativeState.averageStakingTimestampVetoLQTY,
                userState.averageStakingTimestamp,
                /// @audit This is wrong unless we enforce a reset on deposit and withdrawal
                initiativeState.vetoLQTY,
                add(initiativeState.vetoLQTY, deltaLQTYVetos)
            );

            // allocate the voting and vetoing LQTY to the initiative
            initiativeState.voteLQTY = add(initiativeState.voteLQTY, deltaLQTYVotes);
            initiativeState.vetoLQTY = add(initiativeState.vetoLQTY, deltaLQTYVetos);

            // update the initiative's state
            initiativeStates[initiative] = initiativeState;

            // == GLOBAL STATE == //

            // TODO: Veto reducing total votes logic change
            // TODO: Accounting invariants
            // TODO: Let's say I want to cap the votes vs weights
            // Then by definition, I add the effective LQTY
            // And the effective TS
            // I remove the previous one
            // and add the next one
            // Veto > Vote
            // Reduce down by Vote (cap min)
            // If Vote > Veto
            // Increase by Veto - Veto (reduced max)

            // update the average staking timestamp for all counted voting LQTY
            /// Discount previous only if the initiative was not unregistered

            /// @audit We update the state only for non-disabled initiaitives
            /// Disabled initiaitves have had their totals subtracted already
            /// Math is also non associative so we cannot easily compare values
            if (status != InitiativeStatus.DISABLED) {
                /// @audit Trophy: `test_property_sum_of_lqty_global_user_matches_0`
                /// Removing votes from state desynchs the state until all users remove their votes from the initiative
                /// The invariant that holds is: the one that removes the initiatives that have been unregistered
                state.countedVoteLQTYAverageTimestamp = _calculateAverageTimestamp(
                    state.countedVoteLQTYAverageTimestamp,
                    prevInitiativeState.averageStakingTimestampVoteLQTY,
                    /// @audit We don't have a test that fails when this line is changed
                    state.countedVoteLQTY,
                    state.countedVoteLQTY - prevInitiativeState.voteLQTY
                );
                assert(state.countedVoteLQTY >= prevInitiativeState.voteLQTY);
                /// @audit INVARIANT: Never overflows
                state.countedVoteLQTY -= prevInitiativeState.voteLQTY;

                state.countedVoteLQTYAverageTimestamp = _calculateAverageTimestamp(
                    state.countedVoteLQTYAverageTimestamp,
                    initiativeState.averageStakingTimestampVoteLQTY,
                    state.countedVoteLQTY,
                    state.countedVoteLQTY + initiativeState.voteLQTY
                );

                state.countedVoteLQTY += initiativeState.voteLQTY;
            }

            // == USER ALLOCATION == //

            // allocate the voting and vetoing LQTY to the initiative
            Allocation memory allocation = lqtyAllocatedByUserToInitiative[msg.sender][initiative];
            allocation.voteLQTY = add(allocation.voteLQTY, deltaLQTYVotes);
            allocation.vetoLQTY = add(allocation.vetoLQTY, deltaLQTYVetos);
            allocation.atEpoch = currentEpoch;
            require(!(allocation.voteLQTY != 0 && allocation.vetoLQTY != 0), "Governance: vote-and-veto");
            lqtyAllocatedByUserToInitiative[msg.sender][initiative] = allocation;

            // == USER STATE == //

            userState.allocatedLQTY = add(userState.allocatedLQTY, deltaLQTYVotes + deltaLQTYVetos);

            emit AllocateLQTY(msg.sender, initiative, deltaLQTYVotes, deltaLQTYVetos, currentEpoch);

            // Replaces try / catch | Enforces sufficient gas is passed
            safeCallWithMinGas(
                initiative,
                MIN_GAS_TO_HOOK,
                0,
                abi.encodeCall(
                    IInitiative.onAfterAllocateLQTY, (currentEpoch, msg.sender, userState, allocation, initiativeState)
                )
            );
        }

        require(
            userState.allocatedLQTY == 0
                || userState.allocatedLQTY <= uint88(stakingV1.stakes(deriveUserProxyAddress(msg.sender))),
            "Governance: insufficient-or-allocated-lqty"
        );

        globalState = state;
        userStates[msg.sender] = userState;
    }

    /// @inheritdoc IGovernance
    function unregisterInitiative(address _initiative) external nonReentrant {
        /// Enforce FSM
        (VoteSnapshot memory votesSnapshot_, GlobalState memory state) = _snapshotVotes();
        (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) =
            _snapshotVotesForInitiative(_initiative);

        (InitiativeStatus status,,) =
            getInitiativeState(_initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState);
        require(status != InitiativeStatus.NONEXISTENT, "Governance: initiative-not-registered");
        require(status != InitiativeStatus.WARM_UP, "Governance: initiative-in-warm-up");
        require(status == InitiativeStatus.UNREGISTERABLE, "Governance: cannot-unregister-initiative");

        // Remove weight from current state
        uint16 currentEpoch = epoch();

        /// @audit Invariant: Must only claim once or unregister
        // NOTE: Safe to remove | See `check_claim_soundness`
        assert(initiativeState.lastEpochClaim < currentEpoch - 1);

        // recalculate the average staking timestamp for all counted voting LQTY if the initiative was counted in
        /// @audit Trophy: `test_property_sum_of_lqty_global_user_matches_0`
        // Removing votes from state desynchs the state until all users remove their votes from the initiative

        state.countedVoteLQTYAverageTimestamp = _calculateAverageTimestamp(
            state.countedVoteLQTYAverageTimestamp,
            initiativeState.averageStakingTimestampVoteLQTY,
            state.countedVoteLQTY,
            state.countedVoteLQTY - initiativeState.voteLQTY
        );
        assert(state.countedVoteLQTY >= initiativeState.voteLQTY);
        /// RECON: Overflow
        state.countedVoteLQTY -= initiativeState.voteLQTY;

        globalState = state;

        /// weeks * 2^16 > u32 so the contract will stop working before this is an issue
        registeredInitiatives[_initiative] = UNREGISTERED_INITIATIVE;

        emit UnregisterInitiative(_initiative, currentEpoch);

        // Replaces try / catch | Enforces sufficient gas is passed
        safeCallWithMinGas(
            _initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onUnregisterInitiative, (currentEpoch))
        );
    }

    /// @inheritdoc IGovernance
    function claimForInitiative(address _initiative) external nonReentrant returns (uint256) {
        // Accrue and update state
        (VoteSnapshot memory votesSnapshot_,) = _snapshotVotes();
        (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) =
            _snapshotVotesForInitiative(_initiative);

        // Compute values on accrued state
        (InitiativeStatus status,, uint256 claimableAmount) =
            getInitiativeState(_initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState);

        if (status != InitiativeStatus.CLAIMABLE) {
            return 0;
        }

        /// @audit INVARIANT: You can only claim for previous epoch
        assert(votesSnapshot_.forEpoch == epoch() - 1);

        /// All unclaimed rewards are always recycled
        /// Invariant `lastEpochClaim` is < epoch() - 1; |
        /// If `lastEpochClaim` is older than epoch() - 1 it means the initiative couldn't claim any rewards this epoch
        initiativeStates[_initiative].lastEpochClaim = epoch() - 1;

        // @audit INVARIANT, because of rounding errors the system can overpay
        /// We upscale the timestamp to reduce the impact of the loss
        /// However this is still possible
        uint256 available = bold.balanceOf(address(this));
        if (claimableAmount > available) {
            claimableAmount = available;
        }

        bold.safeTransfer(_initiative, claimableAmount);

        emit ClaimForInitiative(_initiative, claimableAmount, votesSnapshot_.forEpoch);

        // Replaces try / catch | Enforces sufficient gas is passed
        safeCallWithMinGas(
            _initiative,
            MIN_GAS_TO_HOOK,
            0,
            abi.encodeCall(IInitiative.onClaimForInitiative, (votesSnapshot_.forEpoch, claimableAmount))
        );

        return claimableAmount;
    }

    function _requireNoNOP(int88[] memory _absoluteLQTYVotes, int88[] memory _absoluteLQTYVetos) internal pure {
        for (uint256 i; i < _absoluteLQTYVotes.length; i++) {
            require(_absoluteLQTYVotes[i] > 0 || _absoluteLQTYVetos[i] > 0, "Governance: voting nothing");
        }
    }
}

File 2 of 22 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

File 3 of 22 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

File 4 of 22 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 5 of 22 : IGovernance.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

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

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

import {PermitParams} from "../utils/Types.sol";

interface IGovernance {
    event DepositLQTY(address user, uint256 depositedLQTY);
    event WithdrawLQTY(address user, uint256 withdrawnLQTY, uint256 accruedLUSD, uint256 accruedETH);

    event SnapshotVotes(uint240 votes, uint16 forEpoch);
    event SnapshotVotesForInitiative(address initiative, uint240 votes, uint16 forEpoch);

    event RegisterInitiative(address initiative, address registrant, uint16 atEpoch);
    event UnregisterInitiative(address initiative, uint16 atEpoch);

    event AllocateLQTY(address user, address initiative, int256 deltaVoteLQTY, int256 deltaVetoLQTY, uint16 atEpoch);
    event ClaimForInitiative(address initiative, uint256 bold, uint256 forEpoch);

    struct Configuration {
        uint128 registrationFee;
        uint128 registrationThresholdFactor;
        uint128 unregistrationThresholdFactor;
        uint16 registrationWarmUpPeriod;
        uint16 unregistrationAfterEpochs;
        uint128 votingThresholdFactor;
        uint88 minClaim;
        uint88 minAccrual;
        uint32 epochStart;
        uint32 epochDuration;
        uint32 epochVotingCutoff;
    }

    function registerInitialInitiatives(address[] memory _initiatives) external;

    /// @notice Address of the LQTY StakingV1 contract
    /// @return stakingV1 Address of the LQTY StakingV1 contract
    function stakingV1() external view returns (ILQTYStaking stakingV1);
    /// @notice Address of the LQTY token
    /// @return lqty Address of the LQTY token
    function lqty() external view returns (IERC20 lqty);
    /// @notice Address of the BOLD token
    /// @return bold Address of the BOLD token
    function bold() external view returns (IERC20 bold);
    /// @notice Timestamp at which the first epoch starts
    /// @return epochStart Timestamp at which the first epoch starts
    function EPOCH_START() external view returns (uint256 epochStart);
    /// @notice Duration of an epoch in seconds (e.g. 1 week)
    /// @return epochDuration Epoch duration
    function EPOCH_DURATION() external view returns (uint256 epochDuration);
    /// @notice Voting period of an epoch in seconds (e.g. 6 days)
    /// @return epochVotingCutoff Epoch voting cutoff
    function EPOCH_VOTING_CUTOFF() external view returns (uint256 epochVotingCutoff);
    /// @notice Minimum BOLD amount that has to be claimed, if an initiative doesn't have enough votes to meet the
    /// criteria then it's votes a excluded from the vote count and distribution
    /// @return minClaim Minimum claim amount
    function MIN_CLAIM() external view returns (uint256 minClaim);
    /// @notice Minimum amount of BOLD that have to be accrued for an epoch, otherwise accrual will be skipped for
    /// that epoch
    /// @return minAccrual Minimum amount of BOLD
    function MIN_ACCRUAL() external view returns (uint256 minAccrual);
    /// @notice Amount of BOLD to be paid in order to register a new initiative
    /// @return registrationFee Registration fee
    function REGISTRATION_FEE() external view returns (uint256 registrationFee);
    /// @notice Share of all votes that are necessary to register a new initiative
    /// @return registrationThresholdFactor Threshold factor
    function REGISTRATION_THRESHOLD_FACTOR() external view returns (uint256 registrationThresholdFactor);
    /// @notice Multiple of the voting threshold in vetos that are necessary to unregister an initiative
    /// @return unregistrationThresholdFactor Unregistration threshold factor
    function UNREGISTRATION_THRESHOLD_FACTOR() external view returns (uint256 unregistrationThresholdFactor);
    /// @notice Number of epochs an initiative has to exist before it can be unregistered
    /// @return registrationWarmUpPeriod Number of epochs
    function REGISTRATION_WARM_UP_PERIOD() external view returns (uint256 registrationWarmUpPeriod);
    /// @notice Number of epochs an initiative has to be inactive before it can be unregistered
    /// @return unregistrationAfterEpochs Number of epochs
    function UNREGISTRATION_AFTER_EPOCHS() external view returns (uint256 unregistrationAfterEpochs);
    /// @notice Share of all votes that are necessary for an initiative to be included in the vote count
    /// @return votingThresholdFactor Voting threshold factor
    function VOTING_THRESHOLD_FACTOR() external view returns (uint256 votingThresholdFactor);

    /// @notice Returns the amount of BOLD accrued since last epoch (last snapshot)
    /// @return boldAccrued BOLD accrued
    function boldAccrued() external view returns (uint256 boldAccrued);

    struct VoteSnapshot {
        uint240 votes; // Votes at epoch transition
        uint16 forEpoch; // Epoch for which the votes are counted
    }

    struct InitiativeVoteSnapshot {
        uint224 votes; // Votes at epoch transition
        uint16 forEpoch; // Epoch for which the votes are counted
        uint16 lastCountedEpoch; // Epoch at which which the votes where counted last in the global snapshot
        uint224 vetos; // Vetos at epoch transition
    }

    /// @notice Returns the vote count snapshot of the previous epoch
    /// @return votes Number of votes
    /// @return forEpoch Epoch for which the votes are counted
    function votesSnapshot() external view returns (uint240 votes, uint16 forEpoch);
    /// @notice Returns the vote count snapshot for an initiative of the previous epoch
    /// @param _initiative Address of the initiative
    /// @return votes Number of votes
    /// @return forEpoch Epoch for which the votes are counted
    /// @return lastCountedEpoch Epoch at which which the votes where counted last in the global snapshot
    function votesForInitiativeSnapshot(address _initiative)
        external
        view
        returns (uint224 votes, uint16 forEpoch, uint16 lastCountedEpoch, uint224 vetos);

    struct Allocation {
        uint88 voteLQTY; // LQTY allocated vouching for the initiative
        uint88 vetoLQTY; // LQTY vetoing the initiative
        uint16 atEpoch; // Epoch at which the allocation was last updated
    }

    struct UserState {
        uint88 allocatedLQTY; // LQTY allocated by the user
        uint120 averageStakingTimestamp; // Average timestamp at which LQTY was staked by the user
    }

    struct InitiativeState {
        uint88 voteLQTY; // LQTY allocated vouching for the initiative
        uint88 vetoLQTY; // LQTY allocated vetoing the initiative
        uint120 averageStakingTimestampVoteLQTY; // Average staking timestamp of the voting LQTY for the initiative
        uint120 averageStakingTimestampVetoLQTY; // Average staking timestamp of the vetoing LQTY for the initiative
        uint16 lastEpochClaim;
    }

    struct GlobalState {
        uint88 countedVoteLQTY; // Total LQTY that is included in vote counting
        uint120 countedVoteLQTYAverageTimestamp; // Average timestamp: derived initiativeAllocation.averageTimestamp
    }
    /// TODO: Bold balance? Prob cheaper

    /// @notice Returns the user's state
    /// @param _user Address of the user
    /// @return allocatedLQTY LQTY allocated by the user
    /// @return averageStakingTimestamp Average timestamp at which LQTY was staked (deposited) by the user
    function userStates(address _user) external view returns (uint88 allocatedLQTY, uint120 averageStakingTimestamp);
    /// @notice Returns the initiative's state
    /// @param _initiative Address of the initiative
    /// @return voteLQTY LQTY allocated vouching for the initiative
    /// @return vetoLQTY LQTY allocated vetoing the initiative
    /// @return averageStakingTimestampVoteLQTY // Average staking timestamp of the voting LQTY for the initiative
    /// @return averageStakingTimestampVetoLQTY // Average staking timestamp of the vetoing LQTY for the initiative
    /// @return lastEpochClaim // Last epoch at which rewards were claimed
    function initiativeStates(address _initiative)
        external
        view
        returns (
            uint88 voteLQTY,
            uint88 vetoLQTY,
            uint120 averageStakingTimestampVoteLQTY,
            uint120 averageStakingTimestampVetoLQTY,
            uint16 lastEpochClaim
        );
    /// @notice Returns the global state
    /// @return countedVoteLQTY Total LQTY that is included in vote counting
    /// @return countedVoteLQTYAverageTimestamp Average timestamp: derived initiativeAllocation.averageTimestamp
    function globalState() external view returns (uint88 countedVoteLQTY, uint120 countedVoteLQTYAverageTimestamp);
    /// @notice Returns the amount of voting and vetoing LQTY a user allocated to an initiative
    /// @param _user Address of the user
    /// @param _initiative Address of the initiative
    /// @return voteLQTY LQTY allocated vouching for the initiative
    /// @return vetoLQTY LQTY allocated vetoing the initiative
    /// @return atEpoch Epoch at which the allocation was last updated
    function lqtyAllocatedByUserToInitiative(address _user, address _initiative)
        external
        view
        returns (uint88 voteLQTY, uint88 vetoLQTY, uint16 atEpoch);

    /// @notice Returns when an initiative was registered
    /// @param _initiative Address of the initiative
    /// @return atEpoch Epoch at which the initiative was registered
    function registeredInitiatives(address _initiative) external view returns (uint16 atEpoch);

    /*//////////////////////////////////////////////////////////////
                                STAKING
    //////////////////////////////////////////////////////////////*/

    /// @notice Deposits LQTY
    /// @dev The caller has to approve this contract to spend the LQTY tokens
    /// @param _lqtyAmount Amount of LQTY to deposit
    function depositLQTY(uint88 _lqtyAmount) external;
    /// @notice Deposits LQTY via Permit
    /// @param _lqtyAmount Amount of LQTY to deposit
    /// @param _permitParams Permit parameters
    function depositLQTYViaPermit(uint88 _lqtyAmount, PermitParams memory _permitParams) external;
    /// @notice Withdraws LQTY and claims any accrued LUSD and ETH rewards from StakingV1
    /// @param _lqtyAmount Amount of LQTY to withdraw
    function withdrawLQTY(uint88 _lqtyAmount) external;
    /// @notice Claims staking rewards from StakingV1 without unstaking
    /// @param _rewardRecipient Address that will receive the rewards
    /// @return accruedLUSD Amount of LUSD accrued
    /// @return accruedETH Amount of ETH accrued
    function claimFromStakingV1(address _rewardRecipient) external returns (uint256 accruedLUSD, uint256 accruedETH);

    /*//////////////////////////////////////////////////////////////
                                 VOTING
    //////////////////////////////////////////////////////////////*/

    /// @notice Returns the current epoch number
    /// @return epoch Current epoch
    function epoch() external view returns (uint16 epoch);
    /// @notice Returns the timestamp at which the current epoch started
    /// @return epochStart Epoch start of the current epoch
    function epochStart() external view returns (uint32 epochStart);
    /// @notice Returns the number of seconds that have gone by since the current epoch started
    /// @return secondsWithinEpoch Seconds within the current epoch
    function secondsWithinEpoch() external view returns (uint32 secondsWithinEpoch);
    /// @notice Returns the number of votes per LQTY for a user
    /// @param _lqtyAmount Amount of LQTY to convert to votes
    /// @param _currentTimestamp Current timestamp
    /// @param _averageTimestamp Average timestamp at which the LQTY was staked
    /// @return votes Number of votes
    function lqtyToVotes(uint88 _lqtyAmount, uint120 _currentTimestamp, uint120 _averageTimestamp)
        external
        pure
        returns (uint208);

    /// @notice Voting threshold is the max. of either:
    ///   - 4% of the total voting LQTY in the previous epoch
    ///   - or the minimum number of votes necessary to claim at least MIN_CLAIM BOLD
    /// This value can be offsynch, use the non view `calculateVotingThreshold` to always retrieve the most up to date value
    /// @return votingThreshold Voting threshold
    function getLatestVotingThreshold() external view returns (uint256 votingThreshold);

    /// @notice Snapshots votes for the previous epoch and accrues funds for the current epoch
    /// @param _initiative Address of the initiative
    /// @return voteSnapshot Vote snapshot
    /// @return initiativeVoteSnapshot Vote snapshot of the initiative
    function snapshotVotesForInitiative(address _initiative)
        external
        returns (VoteSnapshot memory voteSnapshot, InitiativeVoteSnapshot memory initiativeVoteSnapshot);

    /// @notice Registers a new initiative
    /// @param _initiative Address of the initiative
    function registerInitiative(address _initiative) external;
    // /// @notice Unregisters an initiative if it didn't receive enough votes in the last 4 epochs
    // /// or if it received more vetos than votes and the number of vetos are greater than 3 times the voting threshold
    // /// @param _initiative Address of the initiative
    function unregisterInitiative(address _initiative) external;

    /// @notice Allocates the user's LQTY to initiatives
    /// @dev The user can only allocate to active initiatives (older than 1 epoch) and has to have enough unallocated
    /// LQTY available, the initiatives listed must be unique, and towards the end of the epoch a user can only maintain or reduce their votes
    /// @param _resetInitiatives Addresses of the initiatives the caller was previously allocated to, must be reset to prevent desynch of voting power
    /// @param _initiatives Addresses of the initiatives to allocate to, can match or be different from `_resetInitiatives`
    /// @param _absoluteLQTYVotes Delta LQTY to allocate to the initiatives as votes
    /// @param absoluteLQTYVetos Delta LQTY to allocate to the initiatives as vetos
    function allocateLQTY(
        address[] calldata _resetInitiatives,
        address[] memory _initiatives,
        int88[] memory _absoluteLQTYVotes,
        int88[] memory absoluteLQTYVetos
    ) external;

    /// @notice Splits accrued funds according to votes received between all initiatives
    /// @param _initiative Addresse of the initiative
    /// @return claimed Amount of BOLD claimed
    function claimForInitiative(address _initiative) external returns (uint256 claimed);
}

File 6 of 22 : IInitiative.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

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

interface IInitiative {
    /// @notice Callback hook that is called by Governance after the initiative was successfully registered
    /// @param _atEpoch Epoch at which the initiative is registered
    function onRegisterInitiative(uint16 _atEpoch) external;

    /// @notice Callback hook that is called by Governance after the initiative was unregistered
    /// @param _atEpoch Epoch at which the initiative is unregistered
    function onUnregisterInitiative(uint16 _atEpoch) external;

    /// @notice Callback hook that is called by Governance after the LQTY allocation is updated by a user
    /// @param _currentEpoch Epoch at which the LQTY allocation is updated
    /// @param _user Address of the user that updated their LQTY allocation
    /// @param _userState User state
    /// @param _allocation Allocation state from user to initiative
    /// @param _initiativeState Initiative state
    function onAfterAllocateLQTY(
        uint16 _currentEpoch,
        address _user,
        IGovernance.UserState calldata _userState,
        IGovernance.Allocation calldata _allocation,
        IGovernance.InitiativeState calldata _initiativeState
    ) external;

    /// @notice Callback hook that is called by Governance after the claim for the last epoch was distributed
    /// to the initiative
    /// @param _claimEpoch Epoch at which the claim was distributed
    /// @param _bold Amount of BOLD that was distributed
    function onClaimForInitiative(uint16 _claimEpoch, uint256 _bold) external;
}

File 7 of 22 : ILQTYStaking.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface ILQTYStaking {
    // --- Events --

    event LQTYTokenAddressSet(address _lqtyTokenAddress);
    event LUSDTokenAddressSet(address _lusdTokenAddress);
    event TroveManagerAddressSet(address _troveManager);
    event BorrowerOperationsAddressSet(address _borrowerOperationsAddress);
    event ActivePoolAddressSet(address _activePoolAddress);

    event StakeChanged(address indexed staker, uint256 newStake);
    event StakingGainsWithdrawn(address indexed staker, uint256 LUSDGain, uint256 ETHGain);
    event F_ETHUpdated(uint256 _F_ETH);
    event F_LUSDUpdated(uint256 _F_LUSD);
    event TotalLQTYStakedUpdated(uint256 _totalLQTYStaked);
    event EtherSent(address _account, uint256 _amount);
    event StakerSnapshotsUpdated(address _staker, uint256 _F_ETH, uint256 _F_LUSD);

    // --- Functions ---

    function setAddresses(
        address _lqtyTokenAddress,
        address _lusdTokenAddress,
        address _troveManagerAddress,
        address _borrowerOperationsAddress,
        address _activePoolAddress
    ) external;

    function stake(uint256 _LQTYamount) external;

    function unstake(uint256 _LQTYamount) external;

    function increaseF_ETH(uint256 _ETHFee) external;

    function increaseF_LUSD(uint256 _LQTYFee) external;

    function getPendingETHGain(address _user) external view returns (uint256);

    function getPendingLUSDGain(address _user) external view returns (uint256);

    function stakes(address _user) external view returns (uint256);

    function totalLQTYStaked() external view returns (uint256);
}

File 8 of 22 : UserProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IERC20} from "openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC20Permit} from "openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

import {IUserProxy} from "./interfaces/IUserProxy.sol";
import {ILQTYStaking} from "./interfaces/ILQTYStaking.sol";
import {PermitParams} from "./utils/Types.sol";

contract UserProxy is IUserProxy {
    /// @inheritdoc IUserProxy
    IERC20 public immutable lqty;
    /// @inheritdoc IUserProxy
    IERC20 public immutable lusd;

    /// @inheritdoc IUserProxy
    ILQTYStaking public immutable stakingV1;
    /// @inheritdoc IUserProxy
    address public immutable stakingV2;

    constructor(address _lqty, address _lusd, address _stakingV1) {
        lqty = IERC20(_lqty);
        lusd = IERC20(_lusd);
        stakingV1 = ILQTYStaking(_stakingV1);
        stakingV2 = msg.sender;
    }

    modifier onlyStakingV2() {
        require(msg.sender == stakingV2, "UserProxy: caller-not-stakingV2");
        _;
    }

    /// @inheritdoc IUserProxy
    function stake(uint256 _amount, address _lqtyFrom, bool _doSendRewards, address _recipient)
        public
        onlyStakingV2
        returns (uint256 lusdAmount, uint256 ethAmount)
    {
        uint256 initialLUSDAmount = lusd.balanceOf(address(this));
        uint256 initialETHAmount = address(this).balance;

        lqty.transferFrom(_lqtyFrom, address(this), _amount);
        stakingV1.stake(_amount);
        emit Stake(_amount, _lqtyFrom);

        if (_doSendRewards) {
            (lusdAmount, ethAmount) = _sendRewards(_recipient, initialLUSDAmount, initialETHAmount);
        }
    }

    /// @inheritdoc IUserProxy
    function stakeViaPermit(
        uint256 _amount,
        address _lqtyFrom,
        PermitParams calldata _permitParams,
        bool _doSendRewards,
        address _recipient
    ) public onlyStakingV2 returns (uint256 lusdAmount, uint256 ethAmount) {
        require(_lqtyFrom == _permitParams.owner, "UserProxy: owner-not-sender");

        uint256 initialLUSDAmount = lusd.balanceOf(address(this));
        uint256 initialETHAmount = address(this).balance;

        try IERC20Permit(address(lqty)).permit(
            _permitParams.owner,
            _permitParams.spender,
            _permitParams.value,
            _permitParams.deadline,
            _permitParams.v,
            _permitParams.r,
            _permitParams.s
        ) {} catch {}
        stake(_amount, _lqtyFrom, _doSendRewards, _recipient);

        if (_doSendRewards) {
            (lusdAmount, ethAmount) = _sendRewards(_recipient, initialLUSDAmount, initialETHAmount);
        }
    }

    /// @inheritdoc IUserProxy
    function unstake(uint256 _amount, bool _doSendRewards, address _recipient)
        public
        onlyStakingV2
        returns (uint256 lusdAmount, uint256 ethAmount)
    {
        uint256 initialLQTYAmount = lqty.balanceOf(address(this));
        uint256 initialLUSDAmount = lusd.balanceOf(address(this));
        uint256 initialETHAmount = address(this).balance;

        stakingV1.unstake(_amount);

        uint256 lqtyAmount = lqty.balanceOf(address(this));
        if (lqtyAmount > 0) lqty.transfer(_recipient, lqtyAmount);

        emit Unstake(_recipient, lqtyAmount - initialLQTYAmount, lqtyAmount);

        if (_doSendRewards) {
            (lusdAmount, ethAmount) = _sendRewards(_recipient, initialLUSDAmount, initialETHAmount);
        }
    }

    function _sendRewards(address _recipient, uint256 _initialLUSDAmount, uint256 _initialETHAmount)
        internal
        returns (uint256 lusdAmount, uint256 ethAmount)
    {
        lusdAmount = lusd.balanceOf(address(this));
        if (lusdAmount > 0) lusd.transfer(_recipient, lusdAmount);
        ethAmount = address(this).balance;
        if (ethAmount > 0) {
            (bool success,) = payable(_recipient).call{value: ethAmount}("");
            require(success, "UserProxy: eth-fail");
        }

        emit SendRewards(
            _recipient, lusdAmount - _initialLUSDAmount, lusdAmount, ethAmount - _initialETHAmount, ethAmount
        );
    }

    /// @inheritdoc IUserProxy
    function staked() external view returns (uint88) {
        return uint88(stakingV1.stakes(address(this)));
    }

    receive() external payable {}
}

File 9 of 22 : UserProxyFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Clones} from "openzeppelin/contracts/proxy/Clones.sol";

import {IUserProxyFactory} from "./interfaces/IUserProxyFactory.sol";
import {UserProxy} from "./UserProxy.sol";

contract UserProxyFactory is IUserProxyFactory {
    /// @inheritdoc IUserProxyFactory
    address public immutable userProxyImplementation;

    constructor(address _lqty, address _lusd, address _stakingV1) {
        userProxyImplementation = address(new UserProxy(_lqty, _lusd, _stakingV1));
    }

    /// @inheritdoc IUserProxyFactory
    function deriveUserProxyAddress(address _user) public view returns (address) {
        return Clones.predictDeterministicAddress(userProxyImplementation, bytes32(uint256(uint160(_user))));
    }

    /// @inheritdoc IUserProxyFactory
    function deployUserProxy() public returns (address) {
        // reverts if the user already has a proxy
        address userProxy = Clones.cloneDeterministic(userProxyImplementation, bytes32(uint256(uint160(msg.sender))));

        emit DeployUserProxy(msg.sender, userProxy);

        return userProxy;
    }
}

File 10 of 22 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

function add(uint88 a, int88 b) pure returns (uint88) {
    if (b < 0) {
        return a - abs(b);
    }
    return a + abs(b);
}

function max(uint256 a, uint256 b) pure returns (uint256) {
    return a > b ? a : b;
}

function abs(int88 a) pure returns (uint88) {
    return a < 0 ? uint88(uint256(-int256(a))) : uint88(a);
}

File 11 of 22 : UniqueArray.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @dev Checks that there's no duplicate addresses
/// @param arr - List to check for dups
function _requireNoDuplicates(address[] calldata arr) pure {
    uint256 arrLength = arr.length;
    if (arrLength == 0) return;

    // only up to len - 1 (no j to check if i == len - 1)
    for (uint i; i < arrLength - 1;) {
        for (uint j = i + 1; j < arrLength;) {
            require(arr[i] != arr[j], "dup");

            unchecked {
                ++j;
            }
        }

        unchecked {
            ++i;
        }
    }
}

function _requireNoNegatives(int88[] memory vals) pure {
    uint256 arrLength = vals.length;

    for (uint i; i < arrLength; i++) {
        require(vals[i] >= 0, "Cannot be negative");
    }
}

File 12 of 22 : Multicall.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.24;

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

/// Copied from: https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/Multicall.sol
/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall is IMulticall {
    /// @inheritdoc IMulticall
    function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);

            if (!success) {
                // Next 5 lines from https://ethereum.stackexchange.com/a/83577
                if (result.length < 68) revert();
                assembly {
                    result := add(result, 0x04)
                }
                revert(abi.decode(result, (string)));
            }

            results[i] = result;
        }
    }
}

File 13 of 22 : Types.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

struct PermitParams {
    address owner;
    address spender;
    uint256 value;
    uint256 deadline;
    uint8 v;
    bytes32 r;
    bytes32 s;
}

uint256 constant WAD = 1e18;

File 14 of 22 : SafeCallMinGas.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @notice Given the gas requirement, ensures that the current context has sufficient gas to perform a call + a fixed buffer
/// @dev Credits: https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/libraries/SafeCall.sol#L100-L107
function hasMinGas(uint256 _minGas, uint256 _reservedGas) view returns (bool) {
    bool _hasMinGas;
    assembly {
        // Equation: gas × 63 ≥ minGas × 64 + 63(40_000 + reservedGas)
        _hasMinGas := iszero(lt(mul(gas(), 63), add(mul(_minGas, 64), mul(add(40000, _reservedGas), 63))))
    }
    return _hasMinGas;
}

/// @dev Performs a call ignoring the recipient existing or not, passing the exact gas value, ignoring any return value
function safeCallWithMinGas(address _target, uint256 _gas, uint256 _value, bytes memory _calldata)
    returns (bool success)
{
    /// @audit This is not necessary
    ///     But this is basically a worst case estimate of mem exp cost + operations before the call
    require(hasMinGas(_gas, 1_000), "Must have minGas");

    // dispatch message to recipient
    // by assembly calling "handle" function
    // we call via assembly to avoid memcopying a very large returndata
    // returned by a malicious contract
    assembly {
        success :=
            call(
                _gas, // gas
                _target, // recipient
                _value, // ether value
                add(_calldata, 0x20), // inloc
                mload(_calldata), // inlen
                0, // outloc
                0 // outlen
            )

        // Ignore all return values
    }
    return (success);
}

File 15 of 22 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

/**
 * Based on OpenZeppelin's Ownable contract:
 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
 *
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting `initialOwner` as the initial owner.
     */
    constructor(address initialOwner) {
        _owner = initialOwner;
        emit OwnershipTransferred(address(0), initialOwner);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     *
     * NOTE: This function is not safe, as it doesn’t check owner is calling it.
     * Make sure you check it before calling it.
     */
    function _renounceOwnership() internal {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }
}

File 16 of 22 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 17 of 22 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 18 of 22 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

File 19 of 22 : IUserProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

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

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

import {PermitParams} from "../utils/Types.sol";

interface IUserProxy {
    event Stake(uint256 amount, address lqtyFrom);
    event Unstake(address indexed lqtyRecipient, uint256 lqtyReceived, uint256 lqtySent);
    event SendRewards(
        address indexed recipient,
        uint256 lusdAmountReceived,
        uint256 lusdAmountSent,
        uint256 ethAmountReceived,
        uint256 ethAmountSent
    );

    /// @notice Address of the LQTY token
    /// @return lqty Address of the LQTY token
    function lqty() external view returns (IERC20 lqty);
    /// @notice Address of the LUSD token
    /// @return lusd Address of the LUSD token
    function lusd() external view returns (IERC20 lusd);
    /// @notice Address of the V1 LQTY staking contract
    /// @return stakingV1 Address of the V1 LQTY staking contract
    function stakingV1() external view returns (ILQTYStaking stakingV1);
    /// @notice Address of the V2 LQTY staking contract
    /// @return stakingV2 Address of the V2 LQTY staking contract
    function stakingV2() external view returns (address stakingV2);

    /// @notice Stakes a given amount of LQTY tokens in the V1 staking contract
    /// @dev The LQTY tokens must be approved for transfer by the user
    /// @param _amount Amount of LQTY tokens to stake
    /// @param _lqtyFrom Address from which to transfer the LQTY tokens
    /// @param _doSendRewards If true, send rewards claimed from LQTY staking
    /// @param _recipient Address to which the tokens should be sent
    /// @return lusdAmount Amount of LUSD tokens claimed
    /// @return ethAmount Amount of ETH claimed
    function stake(uint256 _amount, address _lqtyFrom, bool _doSendRewards, address _recipient)
        external
        returns (uint256 lusdAmount, uint256 ethAmount);
    /// @notice Stakes a given amount of LQTY tokens in the V1 staking contract using a permit
    /// @param _amount Amount of LQTY tokens to stake
    /// @param _lqtyFrom Address from which to transfer the LQTY tokens
    /// @param _permitParams Parameters for the permit data
    /// @param _doSendRewards If true, send rewards claimed from LQTY staking
    /// @param _recipient Address to which the tokens should be sent
    /// @return lusdAmount Amount of LUSD tokens claimed
    /// @return ethAmount Amount of ETH claimed
    function stakeViaPermit(
        uint256 _amount,
        address _lqtyFrom,
        PermitParams calldata _permitParams,
        bool _doSendRewards,
        address _recipient
    ) external returns (uint256 lusdAmount, uint256 ethAmount);
    /// @notice Unstakes a given amount of LQTY tokens from the V1 staking contract and claims the accrued rewards
    /// @param _amount Amount of LQTY tokens to unstake
    /// @param _doSendRewards If true, send rewards claimed from LQTY staking
    /// @param _recipient Address to which the tokens should be sent
    /// @return lusdAmount Amount of LUSD tokens claimed
    /// @return ethAmount Amount of ETH claimed
    function unstake(uint256 _amount, bool _doSendRewards, address _recipient)
        external
        returns (uint256 lusdAmount, uint256 ethAmount);
    /// @notice Returns the current amount LQTY staked by a user in the V1 staking contract
    /// @return staked Amount of LQTY tokens staked
    function staked() external view returns (uint88);
}

File 20 of 22 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol)

pragma solidity ^0.8.20;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 */
library Clones {
    /**
     * @dev A clone instance deployment failed.
     */
    error ERC1167FailedCreateClone();

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        if (instance == address(0)) {
            revert ERC1167FailedCreateClone();
        }
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        if (instance == address(0)) {
            revert ERC1167FailedCreateClone();
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

File 21 of 22 : IUserProxyFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IUserProxyFactory {
    event DeployUserProxy(address indexed user, address indexed userProxy);

    /// @notice Address of the UserProxy implementation contract
    /// @return implementation Address of the UserProxy implementation contract
    function userProxyImplementation() external view returns (address implementation);

    /// @notice Derive the address of a user's proxy contract
    /// @param _user Address of the user
    /// @return userProxyAddress Address of the user's proxy contract
    function deriveUserProxyAddress(address _user) external view returns (address userProxyAddress);

    /// @notice Deploy a new UserProxy contract for the sender
    /// @return userProxyAddress Address of the deployed UserProxy contract
    function deployUserProxy() external returns (address userProxyAddress);
}

File 22 of 22 : IMulticall.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// Copied from: https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IMulticall.sol
/// @title Multicall interface
/// @notice Enables calling multiple methods in a single call to the contract
interface IMulticall {
    /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
    /// @dev The `msg.value` should not be trusted for any method callable from multicall.
    /// @param data The encoded function data for each of the calls to make to this contract
    /// @return results The results from each of the calls passed in via data
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}

Settings
{
  "remappings": [
    "@chimera/=lib/V2-gov/lib/chimera/src/",
    "@ensdomains/=lib/V2-gov/lib/v4-core/node_modules/@ensdomains/",
    "@openzeppelin/=lib/V2-gov/lib/v4-core/lib/openzeppelin-contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "Solady/=lib/Solady/src/",
    "V2-gov/=lib/V2-gov/",
    "chimera/=lib/V2-gov/lib/chimera/src/",
    "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/V2-gov/lib/v4-core/lib/forge-gas-snapshot/src/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "hardhat/=lib/V2-gov/lib/v4-core/node_modules/hardhat/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/V2-gov/lib/openzeppelin-contracts/",
    "solmate/=lib/V2-gov/lib/v4-core/lib/solmate/src/",
    "v4-core/=lib/V2-gov/lib/v4-core/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_lqty","type":"address"},{"internalType":"address","name":"_lusd","type":"address"},{"internalType":"address","name":"_stakingV1","type":"address"},{"internalType":"address","name":"_bold","type":"address"},{"components":[{"internalType":"uint128","name":"registrationFee","type":"uint128"},{"internalType":"uint128","name":"registrationThresholdFactor","type":"uint128"},{"internalType":"uint128","name":"unregistrationThresholdFactor","type":"uint128"},{"internalType":"uint16","name":"registrationWarmUpPeriod","type":"uint16"},{"internalType":"uint16","name":"unregistrationAfterEpochs","type":"uint16"},{"internalType":"uint128","name":"votingThresholdFactor","type":"uint128"},{"internalType":"uint88","name":"minClaim","type":"uint88"},{"internalType":"uint88","name":"minAccrual","type":"uint88"},{"internalType":"uint32","name":"epochStart","type":"uint32"},{"internalType":"uint32","name":"epochDuration","type":"uint32"},{"internalType":"uint32","name":"epochVotingCutoff","type":"uint32"}],"internalType":"struct IGovernance.Configuration","name":"_config","type":"tuple"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address[]","name":"_initiatives","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ERC1167FailedCreateClone","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"int256","name":"deltaVoteLQTY","type":"int256"},{"indexed":false,"internalType":"int256","name":"deltaVetoLQTY","type":"int256"},{"indexed":false,"internalType":"uint16","name":"atEpoch","type":"uint16"}],"name":"AllocateLQTY","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint256","name":"bold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forEpoch","type":"uint256"}],"name":"ClaimForInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"userProxy","type":"address"}],"name":"DeployUserProxy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositedLQTY","type":"uint256"}],"name":"DepositLQTY","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"address","name":"registrant","type":"address"},{"indexed":false,"internalType":"uint16","name":"atEpoch","type":"uint16"}],"name":"RegisterInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint240","name":"votes","type":"uint240"},{"indexed":false,"internalType":"uint16","name":"forEpoch","type":"uint16"}],"name":"SnapshotVotes","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint240","name":"votes","type":"uint240"},{"indexed":false,"internalType":"uint16","name":"forEpoch","type":"uint16"}],"name":"SnapshotVotesForInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint16","name":"atEpoch","type":"uint16"}],"name":"UnregisterInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawnLQTY","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accruedLUSD","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accruedETH","type":"uint256"}],"name":"WithdrawLQTY","type":"event"},{"inputs":[],"name":"EPOCH_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EPOCH_START","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EPOCH_VOTING_CUTOFF","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_ACCRUAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_CLAIM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRATION_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRATION_THRESHOLD_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRATION_WARM_UP_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIMESTAMP_PRECISION","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNREGISTRATION_AFTER_EPOCHS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNREGISTRATION_THRESHOLD_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTING_THRESHOLD_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_initiativesToReset","type":"address[]"},{"internalType":"address[]","name":"_initiatives","type":"address[]"},{"internalType":"int88[]","name":"_absoluteLQTYVotes","type":"int88[]"},{"internalType":"int88[]","name":"_absoluteLQTYVetos","type":"int88[]"}],"name":"allocateLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bold","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boldAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_votes","type":"uint256"}],"name":"calculateVotingThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calculateVotingThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"claimForInitiative","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardRecipient","type":"address"}],"name":"claimFromStakingV1","outputs":[{"internalType":"uint256","name":"accruedLUSD","type":"uint256"},{"internalType":"uint256","name":"accruedETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deployUserProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"}],"name":"depositLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"depositLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct PermitParams","name":"_permitParams","type":"tuple"}],"name":"depositLQTYViaPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct PermitParams","name":"_permitParams","type":"tuple"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"depositLQTYViaPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"deriveUserProxyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochStart","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"getInitiativeSnapshotAndState","outputs":[{"components":[{"internalType":"uint224","name":"votes","type":"uint224"},{"internalType":"uint16","name":"forEpoch","type":"uint16"},{"internalType":"uint16","name":"lastCountedEpoch","type":"uint16"},{"internalType":"uint224","name":"vetos","type":"uint224"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"initiativeSnapshot","type":"tuple"},{"components":[{"internalType":"uint88","name":"voteLQTY","type":"uint88"},{"internalType":"uint88","name":"vetoLQTY","type":"uint88"},{"internalType":"uint120","name":"averageStakingTimestampVoteLQTY","type":"uint120"},{"internalType":"uint120","name":"averageStakingTimestampVetoLQTY","type":"uint120"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"}],"internalType":"struct IGovernance.InitiativeState","name":"initiativeState","type":"tuple"},{"internalType":"bool","name":"shouldUpdate","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"},{"components":[{"internalType":"uint240","name":"votes","type":"uint240"},{"internalType":"uint16","name":"forEpoch","type":"uint16"}],"internalType":"struct IGovernance.VoteSnapshot","name":"_votesSnapshot","type":"tuple"},{"components":[{"internalType":"uint224","name":"votes","type":"uint224"},{"internalType":"uint16","name":"forEpoch","type":"uint16"},{"internalType":"uint16","name":"lastCountedEpoch","type":"uint16"},{"internalType":"uint224","name":"vetos","type":"uint224"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"_votesForInitiativeSnapshot","type":"tuple"},{"components":[{"internalType":"uint88","name":"voteLQTY","type":"uint88"},{"internalType":"uint88","name":"vetoLQTY","type":"uint88"},{"internalType":"uint120","name":"averageStakingTimestampVoteLQTY","type":"uint120"},{"internalType":"uint120","name":"averageStakingTimestampVetoLQTY","type":"uint120"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"}],"internalType":"struct IGovernance.InitiativeState","name":"_initiativeState","type":"tuple"}],"name":"getInitiativeState","outputs":[{"internalType":"enum Governance.InitiativeStatus","name":"status","type":"uint8"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"getInitiativeState","outputs":[{"internalType":"enum Governance.InitiativeStatus","name":"status","type":"uint8"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getLatestVotingThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalVotesAndState","outputs":[{"components":[{"internalType":"uint240","name":"votes","type":"uint240"},{"internalType":"uint16","name":"forEpoch","type":"uint16"}],"internalType":"struct IGovernance.VoteSnapshot","name":"snapshot","type":"tuple"},{"components":[{"internalType":"uint88","name":"countedVoteLQTY","type":"uint88"},{"internalType":"uint120","name":"countedVoteLQTYAverageTimestamp","type":"uint120"}],"internalType":"struct IGovernance.GlobalState","name":"state","type":"tuple"},{"internalType":"bool","name":"shouldUpdate","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalState","outputs":[{"internalType":"uint88","name":"countedVoteLQTY","type":"uint88"},{"internalType":"uint120","name":"countedVoteLQTYAverageTimestamp","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initiativeStates","outputs":[{"internalType":"uint88","name":"voteLQTY","type":"uint88"},{"internalType":"uint88","name":"vetoLQTY","type":"uint88"},{"internalType":"uint120","name":"averageStakingTimestampVoteLQTY","type":"uint120"},{"internalType":"uint120","name":"averageStakingTimestampVetoLQTY","type":"uint120"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lqty","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"lqtyAllocatedByUserToInitiative","outputs":[{"internalType":"uint88","name":"voteLQTY","type":"uint88"},{"internalType":"uint88","name":"vetoLQTY","type":"uint88"},{"internalType":"uint16","name":"atEpoch","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"internalType":"uint120","name":"_currentTimestamp","type":"uint120"},{"internalType":"uint120","name":"_averageTimestamp","type":"uint120"}],"name":"lqtyToVotes","outputs":[{"internalType":"uint208","name":"","type":"uint208"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_initiatives","type":"address[]"}],"name":"registerInitialInitiatives","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"registerInitiative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"registeredInitiatives","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_initiativesToReset","type":"address[]"},{"internalType":"bool","name":"checkAll","type":"bool"}],"name":"resetAllocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"secondsWithinEpoch","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"snapshotVotesForInitiative","outputs":[{"components":[{"internalType":"uint240","name":"votes","type":"uint240"},{"internalType":"uint16","name":"forEpoch","type":"uint16"}],"internalType":"struct IGovernance.VoteSnapshot","name":"voteSnapshot","type":"tuple"},{"components":[{"internalType":"uint224","name":"votes","type":"uint224"},{"internalType":"uint16","name":"forEpoch","type":"uint16"},{"internalType":"uint16","name":"lastCountedEpoch","type":"uint16"},{"internalType":"uint224","name":"vetos","type":"uint224"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"initiativeVoteSnapshot","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingV1","outputs":[{"internalType":"contract ILQTYStaking","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"unregisterInitiative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"userProxyImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userStates","outputs":[{"internalType":"uint88","name":"allocatedLQTY","type":"uint88"},{"internalType":"uint120","name":"averageStakingTimestamp","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votesForInitiativeSnapshot","outputs":[{"internalType":"uint224","name":"votes","type":"uint224"},{"internalType":"uint16","name":"forEpoch","type":"uint16"},{"internalType":"uint16","name":"lastCountedEpoch","type":"uint16"},{"internalType":"uint224","name":"vetos","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votesSnapshot","outputs":[{"internalType":"uint240","name":"votes","type":"uint240"},{"internalType":"uint16","name":"forEpoch","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"}],"name":"withdrawLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61026060405234801562000011575f80fd5b5060405162006ddc38038062006ddc8339810160408190526200003491620006e9565b8187878782828260405162000049906200057e565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103905ff08015801562000083573d5f803e3d5ffd5b506001600160a01b0390811660805260015f81815581546001600160a01b03191692871692831790915560405191945092505f8051602062006dbc83398151915291508290a3506001600160a01b0380861660a05287811660c090815290851660e0908152840151908401516001600160581b0391821691161115620001505760405162461bcd60e51b815260206004820152601d60248201527f476f763a206d696e2d636c61696d2d67742d6d696e2d6163637275616c00000060448201526064015b60405180910390fd5b82516001600160801b039081166101a0526020840151670de0b6b3a7640000911610620001c05760405162461bcd60e51b815260206004820152601860248201527f476f763a20726567697374726174696f6e2d636f6e6669670000000000000000604482015260640162000147565b60208301516001600160801b039081166101c0526040840151670de0b6b3a7640000911611620002335760405162461bcd60e51b815260206004820152601a60248201527f476f763a20756e726567697374726174696f6e2d636f6e666967000000000000604482015260640162000147565b60408301516001600160801b039081166101e052606084015161ffff908116610200526080850151166102205260a0840151670de0b6b3a7640000911610620002b45760405162461bcd60e51b8152602060048201526012602482015271476f763a20766f74696e672d636f6e66696760701b604482015260640162000147565b60a08301516001600160801b03166102405260c08301516001600160581b039081166101605260e084015116610180526101008084015163ffffffff908116909152610120840151166200034b5760405162461bcd60e51b815260206004820152601860248201527f476f763a2065706f63682d6475726174696f6e2d7a65726f0000000000000000604482015260640162000147565b610120808401805163ffffffff90811690925251610140850151908216911610620003cc5760405162461bcd60e51b815260206004820152602a60248201527f476f763a2065706f63682d766f74696e672d6375746f66662d67742d65706f636044820152693416b23ab930ba34b7b760b11b606482015260840162000147565b6101408084015163ffffffff169052805115620003ee57620003ee81620003fb565b5050505050505062000896565b620004106001546001600160a01b0316331490565b6200045e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000147565b5f5b81518110156200053957600160095f84848151811062000484576200048462000882565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548161ffff021916908361ffff1602179055507f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a828281518110620004fa57620004fa62000882565b602090810291909101810151604080516001600160a01b039092168252339282019290925260018183015290519081900360600190a160010162000460565b506200054462000547565b50565b6001546040515f916001600160a01b0316905f8051602062006dbc833981519152908390a3600180546001600160a01b0319169055565b6110508062005d6c83390190565b80516001600160a01b0381168114620005a3575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b60405161016081016001600160401b0381118282101715620005e257620005e2620005a8565b60405290565b80516001600160801b0381168114620005a3575f80fd5b805161ffff81168114620005a3575f80fd5b80516001600160581b0381168114620005a3575f80fd5b805163ffffffff81168114620005a3575f80fd5b5f82601f8301126200064c575f80fd5b815160206001600160401b03808311156200066b576200066b620005a8565b8260051b604051601f19603f83011681018181108482111715620006935762000693620005a8565b6040529384526020818701810194908101925087851115620006b3575f80fd5b6020870191505b84821015620006de57620006ce826200058c565b83529183019190830190620006ba565b979650505050505050565b5f805f805f805f87890361022081121562000702575f80fd5b6200070d896200058c565b97506200071d60208a016200058c565b96506200072d60408a016200058c565b95506200073d60608a016200058c565b945061016080607f198301121562000753575f80fd5b6200075d620005bc565b91506200076d60808b01620005e8565b82526200077d60a08b01620005e8565b60208301526200079060c08b01620005e8565b6040830152620007a360e08b01620005ff565b6060830152610100620007b8818c01620005ff565b6080840152610120620007cd818d01620005e8565b60a0850152610140620007e2818e0162000611565b60c0860152620007f4848e0162000611565b60e0860152620008086101808e0162000628565b838601526200081b6101a08e0162000628565b828601526200082e6101c08e0162000628565b9085015250919450620008489150506101e089016200058c565b6102008901519092506001600160401b0381111562000865575f80fd5b620008738a828b016200063c565b91505092959891949750929550565b634e487b7160e01b5f52603260045260245ffd5b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e051610200516102205161024051615370620009fc5f395f81816108d30152818161135b015281816113ac0152611ffa01525f8181610cf2015261144201525f61041001525f818161079f015261149301525f81816104be0152611bfa01525f818161076c0152611a9001525f8181610d560152612d7801525f818161048b0152611fb801525f818161033c01526117df01525f8181610b1a015281816110e901528181611521015261220901525f818161059001528181611127015281816114f401528181611545015281816121dc015261222d01525f81816107f101528181611a6c015281816126a0015281816127260152612d0b01525f61084301525f8181610d8901528181611c4c015281816128d80152818161328e015261410201525f818161091a015281816115800152612a6401526153705ff3fe608060405260043610610327575f3560e01c80638a00b67a116101a3578063a9952a0c116100f2578063cc904bec11610092578063e4a61d951161006d578063e4a61d9514610ce1578063e76c01e414610d14578063e772b76514610d45578063f556a79c14610d78575f80fd5b8063cc904bec14610c5d578063cc9baa1214610c94578063cf84a88c14610cb3575f80fd5b8063b1d8f274116100cd578063b1d8f27414610bbe578063bfd79cae14610beb578063c20fb59e14610c1f578063c3140c4914610c3e575f80fd5b8063a9952a0c14610b3c578063ac85912e14610b8a578063ac9650d814610b9e575f80fd5b8063900cf0cf1161015d578063985b11f911610138578063985b11f914610a9f578063a5c29ba514610ad5578063a5e8455d14610af4578063a70b9f0c14610b09575f80fd5b8063900cf0cf146109ea57806391050f40146109fe578063972e6d6614610a80575f80fd5b80638a00b67a146108c25780638b7d38a1146108f55780638d4f0b6c146109095780638da5cb5b1461093c5780638eb9399e146109595780638f32d59b146109c0575f80fd5b80634e0846c7116102795780636f844ae2116102195780637f6ec455116101f45780637f6ec455146108325780638113630d146108655780638777e0951461088457806388edf9be146108a3575f80fd5b80636f844ae2146107c1578063727d0f35146107e057806378a1bdd414610813575f80fd5b8063539054021161025457806353905402146106a05780635c79696c1461072f57806364b4f7511461075b5780636bca7c551461078e575f80fd5b80634e0846c7146105b257806350283275146105e0578063524fcc80146105f4575f80fd5b80631cd2a05d116102e457806338f3e285116102bf57806338f3e285146104ff5780633c12e04e146105415780633f37dc771461056057806346d62a631461057f575f80fd5b80631cd2a05d1461047a5780632591003a146104ad5780632c2cc7da146104e0575f80fd5b80630df9049b1461032b5780630ecc535f146103715780630f2f6aa5146103de5780631030e7b6146103ff57806315e5a1e5146104325780631b4ff3201461045b575b5f80fd5b348015610336575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b34801561037c575f80fd5b506103b761038b366004614602565b60066020525f90815260409020546001600160581b03811690600160581b90046001600160781b031682565b604080516001600160581b0390931683526001600160781b03909116602083015201610368565b3480156103e9575f80fd5b506103fd6103f8366004614602565b610dab565b005b34801561040a575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b34801561043d575f80fd5b506104466110c9565b60405163ffffffff9091168152602001610368565b348015610466575f80fd5b506103fd610475366004614631565b611151565b348015610485575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104b8575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104eb575f80fd5b506103fd6104fa366004614657565b611158565b34801561050a575f80fd5b5061052e610519366004614602565b60096020525f908152604090205461ffff1681565b60405161ffff9091168152602001610368565b34801561054c575f80fd5b506103fd61055b366004614631565b611204565b34801561056b575f80fd5b506103fd61057a3660046146af565b611210565b34801561058a575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105bd575f80fd5b506105d16105cc36600461483b565b611220565b6040516103689392919061492b565b3480156105eb575f80fd5b506104466114f1565b3480156105ff575f80fd5b5061065d61060e366004614602565b60076020525f9081526040902080546001909101546001600160581b0380831692600160581b900416906001600160781b0380821691600160781b810490911690600160f01b900461ffff1685565b604080516001600160581b0396871681529590941660208601526001600160781b039283169385019390935216606083015261ffff16608082015260a001610368565b3480156106ab575f80fd5b506106f66106ba366004614602565b60046020525f9081526040902080546001909101546001600160e01b038083169261ffff600160e01b8204811693600160f01b90920416911684565b60405161036894939291906001600160e01b03948516815261ffff93841660208201529190921660408201529116606082015260800190565b34801561073a575f80fd5b50610743611579565b6040516001600160a01b039091168152602001610368565b348015610766575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610799575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156107cc575f80fd5b506103fd6107db3660046149aa565b6115e2565b3480156107eb575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b34801561081e575f80fd5b506103fd61082d366004614602565b611a57565b34801561083d575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b348015610870575f80fd5b506103fd61087f366004614a64565b611e39565b34801561088f575f80fd5b5061035e61089e366004614b0b565b611f6f565b3480156108ae575f80fd5b506103fd6108bd366004614b22565b612037565b3480156108cd575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610900575f80fd5b5061035e6120c8565b348015610914575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b348015610947575f80fd5b506001546001600160a01b0316610743565b348015610964575f80fd5b5061096d6120ea565b6040805184516001600160f01b0316815260209485015161ffff168582015283516001600160581b03169181019190915292909101516001600160781b031660608301521515608082015260a001610368565b3480156109cb575f80fd5b506001546001600160a01b031633146040519015158152602001610368565b3480156109f5575f80fd5b5061052e6121d9565b348015610a09575f80fd5b50610a56610a18366004614b74565b600860209081525f92835260408084209091529082529020546001600160581b0380821691600160581b810490911690600160b01b900461ffff1683565b604080516001600160581b03948516815293909216602084015261ffff1690820152606001610368565b348015610a8b575f80fd5b506105d1610a9a366004614602565b612267565b348015610aaa575f80fd5b50610abd6a52b7d2dcc80cd2e400000081565b6040516001600160781b039091168152602001610368565b348015610ae0575f80fd5b506103fd610aef366004614b9c565b6122a1565b348015610aff575f80fd5b5061035e60025481565b348015610b14575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610b47575f80fd5b50600354610b68906001600160f01b03811690600160f01b900461ffff1682565b604080516001600160f01b03909316835261ffff909116602083015201610368565b348015610b95575f80fd5b5061035e61233b565b610bb1610bac366004614be8565b612352565b6040516103689190614c73565b348015610bc9575f80fd5b50610bdd610bd8366004614602565b612498565b604051610368929190614d10565b348015610bf6575f80fd5b50610c0a610c05366004614602565b6124f7565b60408051928352602083019190915201610368565b348015610c2a575f80fd5b5061035e610c39366004614602565b6125ad565b348015610c49575f80fd5b506103fd610c58366004614657565b612814565b348015610c68575f80fd5b50610c7c610c77366004614d3c565b612a2d565b6040516001600160d01b039091168152602001610368565b348015610c9f575f80fd5b50610743610cae366004614602565b612a5e565b348015610cbe575f80fd5b50610cd2610ccd366004614602565b612a98565b60405161036893929190614dc3565b348015610cec575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610d1f575f80fd5b506005546103b7906001600160581b03811690600160581b90046001600160781b031682565b348015610d50575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610d83575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b610db3612c68565b5f80610dbd612c90565b915091505f80610dcc85612e06565b915091505f610ddd86868585611220565b509091505f9050816006811115610df657610df6614917565b03610e565760405162461bcd60e51b815260206004820152602560248201527f476f7665726e616e63653a20696e69746961746976652d6e6f742d72656769736044820152641d195c995960da1b60648201526084015b60405180910390fd5b6001816006811115610e6a57610e6a614917565b03610ec15760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a20696e69746961746976652d696e2d7761726d2d756044820152600760fc1b6064820152608401610e4d565b6005816006811115610ed557610ed5614917565b14610f335760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a2063616e6e6f742d756e72656769737465722d696e604482015267697469617469766560c01b6064820152608401610e4d565b5f610f3c6121d9565b9050610f49600182614e04565b61ffff16836080015161ffff1610610f6357610f63614e1f565b6020850151604084015186518551610f8793929190610f829082614e33565b612f39565b6001600160781b03166020860152825185516001600160581b0391821691161015610fb457610fb4614e1f565b825185518690610fc5908390614e33565b6001600160581b039081169091528651600580546020808b01516001600160781b0316600160581b026001600160d01b031990921693909416929092179190911790556001600160a01b0389165f81815260098352604090819020805461ffff191661ffff90811790915581519283528516928201929092527f8231be6875060a0fe4f35eaa6e112ebc15b8c04993114fe41c976bcb0a8c5ad892500160405180910390a160405161ffff821660248201526110b690889062055730905f9060440160408051601f198184030181529190526020810180516001600160e01b0316630a9a78f360e11b1790526130f1565b505050505050506110c660015f55565b50565b5f806110d36121d9565b90508061ffff165f036110e7575f91505090565b7f0000000000000000000000000000000000000000000000000000000000000000611113600183614e04565b61ffff166111219190614e53565b61114b907f0000000000000000000000000000000000000000000000000000000000000000614e6a565b91505090565b6110c6815f335b611160612c68565b5f61116a84613152565b6040516338fb359960e01b81526001600160581b038616600482015233602482015284151560448201526001600160a01b038481166064830152919250908216906338fb35999060840160408051808303815f875af11580156111cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111f39190614e7d565b5050506111ff60015f55565b505050565b6110c681600133612814565b61121c82825f336122a1565b5050565b6001600160a01b0384165f908152600960205260408120548190819061ffff16810361125357505f9150819050806114e7565b61125b6121d9565b6001600160a01b0388165f9081526009602052604090205461ffff91821691160361128e5750600191505f9050806114e7565b6001600160a01b0387165f9081526007602090815260408083206001015460099092529091205461ffff600160f01b9092048216935061fffe199116016112da5750600691505f6114e7565b60016112e46121d9565b6112ee9190614e04565b61ffff168261ffff161061130557600492506114e7565b5f61131b875f01516001600160f01b0316611f6f565b8651606088015189519293506001600160e01b03918216929116906001600160f01b0316838311801561134d57508282105b15611428578061137f6127107f0000000000000000000000000000000000000000000000000000000000000000614eb3565b61138f85655af3107a4000614e53565b6113999190614eb3565b116113a6576113a6614e1f565b5f6113d97f0000000000000000000000000000000000000000000000000000000000000000670de0b6b3a7640000614eb3565b6113e4906001614e6a565b90505f816002548484886113f89190614e53565b6114029190614eb3565b61140c9190614e53565b6114169190614eb3565b6003995096506114e795505050505050565b60016114326121d9565b61143c9190614e04565b61ffff167f0000000000000000000000000000000000000000000000000000000000000000896080015161ffff166114749190614e6a565b10806114c5575082821180156114c55750670de0b6b3a76400006114b87f000000000000000000000000000000000000000000000000000000000000000086614e53565b6114c29190614eb3565b82115b156114db5750600595505f93506114e792505050565b50600295505f93505050505b9450945094915050565b5f7f000000000000000000000000000000000000000000000000000000000000000042101561151f57505f90565b7f000000000000000000000000000000000000000000000000000000000000000061156a7f000000000000000000000000000000000000000000000000000000000000000042614ec6565b6115749190614ed9565b905090565b5f806115a57f0000000000000000000000000000000000000000000000000000000000000000336133b3565b6040519091506001600160a01b0382169033907fda66ba232f4fb8c122b7026f55eeff1d0b9cf2560b7873b2bba6eaab4c3d5989905f90a3919050565b6115ea612c68565b8483146116225760405162461bcd60e51b8152602060048201526006602482015265098cadccee8d60d31b6044820152606401610e4d565b80831461165a5760405162461bcd60e51b8152602060048201526006602482015265098cadccee8d60d31b6044820152606401610e4d565b611664888861341e565b61166e868661341e565b6116a98484808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506134fb92505050565b6116e48282808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506134fb92505050565b61174f8484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f9201919091525061357092505050565b5f61175a8989613614565b335f908152600660209081526040918290208251808401909352546001600160581b038116808452600160581b9091046001600160781b03169183019190915291925090156117dd5760405162461bcd60e51b815260206004820152600f60248201526e1b5d5cdd0818994818481c995cd95d608a1b6044820152606401610e4d565b7f00000000000000000000000000000000000000000000000000000000000000006118066114f1565b63ffffffff1611156119a7575f5b878110156119a5575f805b845181101561191f578a8a8481811061183a5761183a614eec565b905060200201602081019061184f9190614602565b6001600160a01b031685828151811061186a5761186a614eec565b60200260200101515f01516001600160a01b031603611917576001915084818151811061189957611899614eec565b602002602001015160200151600a0b8989858181106118ba576118ba614eec565b90506020020160208101906118cf9190614f00565b600a0b13156119125760405162461bcd60e51b815260206004820152600f60248201526e43616e6e6f7420696e63726561736560881b6044820152606401610e4d565b61191f565b60010161181f565b508061199c5787878381811061193757611937614eec565b905060200201602081019061194c9190614f00565b600a0b1561199c5760405162461bcd60e51b815260206004820181905260248201527f4d757374206265207a65726f20666f72206e657720696e6974696174697665736044820152606401610e4d565b50600101611814565b505b611a428888808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284375f9201919091525050604080516020808b0282810182019093528a82529093508a9250899182918501908490808284375f920191909152506139b292505050565b5050611a4d60015f55565b5050505050505050565b611a5f612c68565b611ab46001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633307f0000000000000000000000000000000000000000000000000000000000000000614282565b6001600160a01b038116611b0a5760405162461bcd60e51b815260206004820152601860248201527f476f7665726e616e63653a207a65726f2d6164647265737300000000000000006044820152606401610e4d565b5f611b1482612267565b509091505f9050816006811115611b2d57611b2d614917565b14611b8c5760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e69746961746976652d616c72656164792d726044820152681959da5cdd195c995960ba1b6064820152608401610e4d565b5f611b9633612a5e565b90505f611ba1612c90565b50335f908152600660209081526040918290208251808401909352546001600160581b0381168352600160581b90046001600160781b0316908201528151919250906001600160f01b0316670de0b6b3a7640000611c1f7f000000000000000000000000000000000000000000000000000000000000000083614e53565b611c299190614eb3565b6040516305a4d3f160e21b81526001600160a01b038681166004830152611ce5917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa158015611c93573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cb79190614f20565b6a52b7d2dcc80cd2e4000000611ccb6110c9565b63ffffffff16611cdb9190614f37565b8560200151612a2d565b6001600160d01b03161015611d3c5760405162461bcd60e51b815260206004820152601d60248201527f476f7665726e616e63653a20696e73756666696369656e742d6c7174790000006044820152606401610e4d565b5f611d456121d9565b6001600160a01b0388165f908152600960205260409020805461ffff191661ffff83161790559050611d78600182614e04565b6001600160a01b0388165f8181526007602090815260409182902060010180546001600160f01b0316600160f01b61ffff96871602179055815192835233908301529183168183015290517f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a916060908290030190a160405161ffff821660248201526110b690889062055730905f9060440160408051601f198184030181529190526020810180516001600160e01b0316628152f560e81b1790526130f1565b6001546001600160a01b03163314611e935760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610e4d565b5f5b8151811015611f6657600160095f848481518110611eb557611eb5614eec565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548161ffff021916908361ffff1602179055507f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a828281518110611f2857611f28614eec565b602090810291909101810151604080516001600160a01b039092168252339282019290925260018183015290519081900360600190a1600101611e95565b506110c66142e9565b5f815f03611f7e57505f919050565b5f8083670de0b6b3a7640000600254611f979190614e53565b611fa19190614eb3565b90508015611fe95780611fdc670de0b6b3a76400007f0000000000000000000000000000000000000000000000000000000000000000614e53565b611fe69190614eb3565b91505b61202f670de0b6b3a764000061201f7f000000000000000000000000000000000000000000000000000000000000000087614e53565b6120299190614eb3565b83614332565b949350505050565b61203f612c68565b612049838361341e565b6120538383613614565b5080156120bf57335f908152600660205260409020546001600160581b0316156120bf5760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206d757374206265206120726573657400000000006044820152606401610e4d565b6111ff60015f55565b5f806120d2612c90565b50905061114b815f01516001600160f01b0316611f6f565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f8061211a6121d9565b6040805180820182526003546001600160f01b0381168252600160f01b900461ffff1660208083019190915282518084019093526005546001600160581b0381168452600160581b90046001600160781b031690830152955093509050612182600182614e04565b61ffff16846020015161ffff1610156121d3578251600192506121b3906a52b7d2dcc80cd2e4000000611ccb6110c9565b6001600160d01b031684526121c9600182614e04565b61ffff1660208501525b50909192565b5f7f000000000000000000000000000000000000000000000000000000000000000042101561220757505f90565b7f00000000000000000000000000000000000000000000000000000000000000006122527f000000000000000000000000000000000000000000000000000000000000000042614ec6565b61225c9190614eb3565b611574906001614e6a565b5f805f80612273612c90565b5090505f8061228187612e06565b9150915061229187848484611220565b9550955095505050509193909250565b6122a9612c68565b5f6122b385613152565b60405163eb876bf760e01b81529091506001600160a01b0382169063eb876bf7906122ea9088903390899089908990600401614f62565b60408051808303815f875af1158015612305573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123299190614e7d565b50505061233560015f55565b50505050565b6003545f906001600160f01b031661114b81611f6f565b6060816001600160401b0381111561236c5761236c6146e2565b60405190808252806020026020018201604052801561239f57816020015b606081526020019060019003908161238a5790505b5090505f5b82811015612491575f80308686858181106123c1576123c1614eec565b90506020028101906123d3919061500f565b6040516123e1929190615051565b5f60405180830381855af49150503d805f8114612419576040519150601f19603f3d011682016040523d82523d5f602084013e61241e565b606091505b50915091508161246957604481511015612436575f80fd5b600481019050808060200190518101906124509190615060565b60405162461bcd60e51b8152600401610e4d91906150ee565b8084848151811061247c5761247c614eec565b602090810291909101015250506001016123a4565b5092915050565b6040805180820182525f808252602080830182905283516080810185528281529081018290529283018190526060830152906124d2612c68565b6124da612c90565b5091506124e683612e06565b5090506124f260015f55565b915091565b5f805f61250333612a5e565b9050806001600160a01b03163b5f0361252e5760405162461bcd60e51b8152600401610e4d90615100565b604051633fdf42e360e01b81525f6004820152600160248201526001600160a01b038581166044830152821690633fdf42e39060640160408051808303815f875af115801561257f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125a39190614e7d565b9250925050915091565b5f6125b6612c68565b5f6125bf612c90565b5090505f806125cd85612e06565b915091505f806125df87868686611220565b919350909150600390508260068111156125fb576125fb614917565b1461260d575f95505050505050612806565b60016126176121d9565b6126219190614e04565b61ffff16856020015161ffff161461263b5761263b614e1f565b60016126456121d9565b61264f9190614e04565b6001600160a01b038881165f90815260076020526040808220600101805461ffff95909516600160f01b026001600160f01b039095169490941790935591516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa1580156126e7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061270b9190614f20565b905080821115612719578091505b61274d6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168984614347565b602086810151604080516001600160a01b038c16815292830185905261ffff90911682820152517fa1ee42ce2e3b0e300645a781ea3e7c494f626cfeb1c2cce20e2fa4a5da249d189181900360600190a16127fc88620557305f8960200151866040516024016127cd92919061ffff929092168252602082015260400190565b60408051601f198184030181529190526020810180516001600160e01b03166313440fab60e01b1790526130f1565b5090955050505050505b61280f60015f55565b919050565b61281c612c68565b335f90815260066020526040902080546001600160581b0316156128825760405162461bcd60e51b815260206004820152601e60248201527f476f7665726e616e63653a206d7573742d616c6c6f636174652d7a65726f00006044820152606401610e4d565b5f61288c33612a5e565b9050806001600160a01b03163b5f036128b75760405162461bcd60e51b8152600401610e4d90615100565b6040516305a4d3f160e21b81526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa15801561291f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129439190614f20565b604051633fdf42e360e01b81526001600160581b038816600482015286151560248201526001600160a01b0386811660448301529192505f91829190851690633fdf42e39060640160408051808303815f875af11580156129a6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129ca9190614e7d565b604080513381526001600160581b038c1660208201529081018390526060810182905291935091507f71330c97f647d5136a666c3cf5cef9ec255ec0468d9d7891929eff0f4845c4589060800160405180910390a150505050506111ff60015f55565b5f612a388383614378565b612a54906001600160781b03166001600160581b038616615143565b90505b9392505050565b5f612a927f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0384166143b6565b92915050565b604080516080810182525f8082526020820181905291810182905260608101919091526040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f80612aef6121d9565b6001600160a01b0386165f81815260046020908152604080832081516080808201845282546001600160e01b03808216845261ffff600160e01b8304811685890152600160f01b928390048116858801526001958601549091166060808601919091529888526007875296859020855160a08101875281546001600160581b038082168352600160581b90910416978101979097528401546001600160781b0380821696880196909652600160781b8104909516978601979097529590920490931693820193909352919650909450909150612bcb9082614e04565b61ffff16846020015161ffff161015612c6057600191505f6a52b7d2dcc80cd2e4000000612bf76110c9565b63ffffffff16612c079190614f37565b90505f612c1c855f0151838760400151612a2d565b90505f612c328660200151848860600151612a2d565b6001600160d01b038084168952811660608901529050612c53600185614e04565b61ffff1660208801525050505b509193909250565b60025f5403612c8a57604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f612cbf6120ea565b919450925090508015612e01578251602084015161ffff16600160f01b026001600160f01b03909116176003556040516370a0823160e01b81523060048201525f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612d50573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d749190614f20565b90507f00000000000000000000000000000000000000000000000000000000000000008110612da35780612da5565b5f5b600255835160208501516040517ff8c3496a83a68868434a061a876d356df06c7ef5bd8476f13d496b966855686b92612df79290916001600160f01b0392909216825261ffff16602082015260400190565b60405180910390a1505b509091565b604080516080810182525f8082526020820181905291810182905260608101919091526040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f612e5d84612a98565b919450925090508015612f33576001600160a01b0384165f818152600460209081526040918290208651815488840151898601516001600160e01b039384166001600160f01b03199093168317600160e01b61ffff938416908102919091176001600160f01b0316600160f01b93909216929092021784556060808b0151600190950180546001600160e01b0319169590941694909417909255845195865292850192909252918301527f6f22c33d2cf529b6a07e72fdf3fd25bb07828d8017cc4d47405c3ec9cadf6ffd910160405180910390a15b50915091565b5f816001600160581b03165f03612f5157505f61202f565b5f612f6d6a52b7d2dcc80cd2e400000063ffffffff4216614f37565b90505f612f7a8288614378565b90505f612f878388614378565b90505f856001600160581b0316876001600160581b031611613019575f612fae8888614e33565b90505f612fcd6001600160781b0386166001600160581b038b16615143565b90505f612fec6001600160781b0386166001600160581b038516615143565b90505f612ff98284615175565b905061300e6001600160581b038b1682615195565b9450505050506130ab565b5f6130248789614e33565b90505f6130436001600160781b0386166001600160581b038b16615143565b90505f6130626001600160781b0386166001600160581b038516615143565b90505f816001600160d01b0316836001600160d01b03161015613085575f61308f565b61308f82846151ba565b90506130a46001600160581b038b1682615195565b9450505050505b836001600160781b0316816001600160d01b031611156130d1575f94505050505061202f565b6130e4816001600160781b0386166151ba565b9998505050505050505050565b5f6130fe846103e8614415565b61313d5760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b6044820152606401610e4d565b5f80835160208501868989f195945050505050565b5f80826001600160581b0316116131ab5760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a207a65726f2d6c7174792d616d6f756e74000000006044820152606401610e4d565b335f908152600660209081526040918290208251808401909352546001600160581b038116808452600160581b9091046001600160781b031691830191909152156132445760405162461bcd60e51b815260206004820152602360248201527f476f7665726e616e63653a206d7573742d62652d7a65726f2d616c6c6f63617460448201526234b7b760e91b6064820152608401610e4d565b5f61324e33612a5e565b9050806001600160a01b03163b5f0361326b57613269611579565b505b6040516305a4d3f160e21b81526001600160a01b03808316600483015282915f917f000000000000000000000000000000000000000000000000000000000000000016906316934fc490602401602060405180830381865afa1580156132d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132f79190614f20565b905061332384602001516a52b7d2dcc80cd2e4000000426133189190614f37565b83610f828a826151da565b6001600160781b039081166020868101918252335f81815260068352604090819020895181549551909616600160581b026001600160d01b03199095166001600160581b0396871617949094179093558251908152928916908301527f841ac74294f07d75868ac51778dc366b1fa11e8934b969cd5b0185c0e91dabb1910160405180910390a150949350505050565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760095ff590506001600160a01b038116612a92576040516330be1a3d60e21b815260040160405180910390fd5b805f81900361342c57505050565b5f5b613439600183614ec6565b811015612335575f61344c826001614e6a565b90505b828110156134f25784848281811061346957613469614eec565b905060200201602081019061347e9190614602565b6001600160a01b031685858481811061349957613499614eec565b90506020020160208101906134ae9190614602565b6001600160a01b0316036134ea5760405162461bcd60e51b815260206004820152600360248201526206475760ec1b6044820152606401610e4d565b60010161344f565b5060010161342e565b80515f5b818110156111ff575f83828151811061351a5761351a614eec565b6020026020010151600a0b12156135685760405162461bcd60e51b815260206004820152601260248201527143616e6e6f74206265206e6567617469766560701b6044820152606401610e4d565b6001016134ff565b5f5b82518110156111ff575f83828151811061358e5761358e614eec565b6020026020010151600a0b13806135c057505f8282815181106135b3576135b3614eec565b6020026020010151600a0b135b61360c5760405162461bcd60e51b815260206004820152601a60248201527f476f7665726e616e63653a20766f74696e67206e6f7468696e670000000000006044820152606401610e4d565b600101613572565b60605f826001600160401b0381111561362f5761362f6146e2565b60405190808252806020026020018201604052801561367857816020015b604080516060810182525f80825260208083018290529282015282525f1990920191018161364d5790505b5090505f836001600160401b03811115613694576136946146e2565b6040519080825280602002602001820160405280156136bd578160200160208202803683370190505b5090505f846001600160401b038111156136d9576136d96146e2565b604051908082528060200260200182016040528015613702578160200160208202803683370190505b5090505f5b8581101561396857335f9081526008602052604081208189898581811061373057613730614eec565b90506020020160208101906137459190614602565b6001600160a01b0316815260208082019290925260409081015f20815160608101835290546001600160581b03808216808452600160581b830490911694830194909452600160b01b900461ffff169181019190915291501515806137b657505f81602001516001600160581b0316115b6138025760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a206e6f7468696e6720746f207265736574000000006044820152606401610e4d565b80516001600160571b036001600160581b03909116111561382557613825614e1f565b6001600160571b036001600160581b031681602001516001600160581b0316111561385257613852614e1f565b604051806060016040528089898581811061386f5761386f614eec565b90506020020160208101906138849190614602565b6001600160a01b03168152602001825f0151600a0b81526020018260200151600a0b8152508583815181106138bb576138bb614eec565b60200260200101819052508482815181106138d8576138d8614eec565b6020026020010151602001516138ed906151fa565b8483815181106138ff576138ff614eec565b6020026020010190600a0b9081600a0b8152505084828151811061392557613925614eec565b60200260200101516040015161393a906151fa565b83838151811061394c5761394c614eec565b600a9290920b6020928302919091019091015250600101613707565b506139a88686808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508692508591506139b29050565b5090949350505050565b815183511480156139c4575080518351145b613a1a5760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a2061727261792d6c656e6774682d6d69736d6174636044820152600d60fb1b6064820152608401610e4d565b5f80613a24612c90565b915091505f613a316121d9565b335f9081526006602090815260408083208151808301909252546001600160581b0381168252600160581b90046001600160781b0316918101919091529192505b87518110156140ed575f888281518110613a8e57613a8e614eec565b602002602001015190505f888381518110613aab57613aab614eec565b602002602001015190505f888481518110613ac857613ac8614eec565b6020026020010151905081600a0b5f141580613ae7575080600a0b5f14155b613af357613af3614e1f565b5f80613afe85612e06565b915091505f613b0f868c8585611220565b505090505f85600a0b1380613b2657505f84600a0b135b15613bc4576002816006811115613b3f57613b3f614917565b1480613b5c57506003816006811115613b5a57613b5a614917565b145b80613b7857506004816006811115613b7657613b76614917565b145b613bc45760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206163746976652d766f74652d66736d00000000006044820152606401610e4d565b6006816006811115613bd857613bd8614917565b03613c36575f85600a0b13158015613bf357505f84600a0b13155b613c365760405162461bcd60e51b8152602060048201526014602482015273135d5cdd0818994818481dda5d1a191c985dd85b60621b6044820152606401610e4d565b5f6040518060a00160405280845f01516001600160581b0316815260200184602001516001600160581b0316815260200184604001516001600160781b0316815260200184606001516001600160781b03168152602001846080015161ffff168152509050613cb983604001518a60200151855f0151610f82875f01518b614432565b6001600160781b0316604084015260608301516020808b015190850151613ce6929190610f82818a614432565b6001600160781b031660608401528251613d009087614432565b6001600160581b031683526020830151613d1a9086614432565b6001600160581b0390811660208086019182526001600160a01b038a165f90815260079091526040908190208651815493518516600160581b026001600160b01b0319909416941693909317919091178255840151600190910180546060860151608087015161ffff16600160f01b026001600160f01b036001600160781b03928316600160781b026001600160f01b0319909416929095169190911791909117929092169190911790556006826006811115613dd957613dd9614917565b14613e925760208b015160408201518c518351613dfd93929190610f829082614e33565b6001600160781b031660208c015280518b516001600160581b0391821691161015613e2a57613e2a614e1f565b80518b518c90613e3b908390614e33565b6001600160581b031690525060208b015160408401518c518551613e6693929190610f8290826151da565b6001600160781b031660208c015282518b518c90613e859083906151da565b6001600160581b03169052505b335f9081526008602090815260408083206001600160a01b038b168452825291829020825160608101845290546001600160581b03808216808452600160581b830490911693830193909352600160b01b900461ffff1692810192909252613efa9088614432565b6001600160581b031681526020810151613f149087614432565b6001600160581b03908116602083015261ffff8c16604083015281511615801590613f4b575060208101516001600160581b031615155b15613f985760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63653a20766f74652d616e642d7665746f000000000000006044820152606401610e4d565b335f9081526008602090815260408083206001600160a01b038c1684528252918290208351815492850151938501516001600160581b039182166001600160b01b031990941693909317600160581b91909416029290921761ffff60b01b1916600160b01b61ffff90921691909102179055895161401f9061401a888a61521e565b614432565b6001600160581b03168a52604080513381526001600160a01b038a166020820152600a89810b8284015288900b606082015261ffff8d16608082015290517ffc65ef0a2ea71843c9c13a7a7c0bdccb27001f82e748634c3155cba36faccc2c9181900360a00190a16140d888620557305f8e338f878b6040516024016140a995949392919061524f565b60408051601f198184030181529190526020810180516001600160e01b0316633d0fb77360e01b1790526130f1565b505060019097019650613a7295505050505050565b5080516001600160581b031615806141b757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166316934fc461413833612a5e565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa15801561417a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061419e9190614f20565b6001600160581b0316815f01516001600160581b031611155b6142165760405162461bcd60e51b815260206004820152602a60248201527f476f7665726e616e63653a20696e73756666696369656e742d6f722d616c6c6f60448201526963617465642d6c71747960b01b6064820152608401610e4d565b8251600580546020958601516001600160781b03908116600160581b9081026001600160d01b03199384166001600160581b039687161717909355335f908152600688526040902085518154969098015190911690920293169490911693909317179091555050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526123359186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061446b565b6001546040515f916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b5f8183116143405781612a57565b5090919050565b6040516001600160a01b038381166024830152604482018390526111ff91859182169063a9059cbb906064016142b7565b5f6001600160781b03821615806143a05750816001600160781b0316836001600160781b0316105b156143ac57505f612a92565b612a5782846152ca565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101205f90612a57565b5f80603f83619c4001026040850201603f5a021015949350505050565b5f8082600a0b121561445857614447826144cc565b6144519084614e33565b9050612a92565b614461826144cc565b612a5790846151da565b5f61447f6001600160a01b038416836144ea565b905080515f141580156144a35750808060200190518101906144a191906152ea565b155b156111ff57604051635274afe760e01b81526001600160a01b0384166004820152602401610e4d565b5f8082600a0b126144dd5781612a92565b81600a0b612a9290615305565b6060612a5783835f845f80856001600160a01b0316848660405161450e919061531f565b5f6040518083038185875af1925050503d805f8114614548576040519150601f19603f3d011682016040523d82523d5f602084013e61454d565b606091505b509150915061455d868383614567565b9695505050505050565b60608261457c57614577826145c3565b612a57565b815115801561459357506001600160a01b0384163b155b156145bc57604051639996b31560e01b81526001600160a01b0385166004820152602401610e4d565b5080612a57565b8051156145d35780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461280f575f80fd5b5f60208284031215614612575f80fd5b612a57826145ec565b80356001600160581b038116811461280f575f80fd5b5f60208284031215614641575f80fd5b612a578261461b565b80151581146110c6575f80fd5b5f805f60608486031215614669575f80fd5b6146728461461b565b925060208401356146828161464a565b9150614690604085016145ec565b90509250925092565b5f60e082840312156146a9575f80fd5b50919050565b5f8061010083850312156146c1575f80fd5b6146ca8361461b565b91506146d98460208501614699565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715614718576147186146e2565b60405290565b604051608081016001600160401b0381118282101715614718576147186146e2565b604051601f8201601f191681016001600160401b0381118282101715614768576147686146e2565b604052919050565b803561ffff8116811461280f575f80fd5b80356001600160e01b038116811461280f575f80fd5b80356001600160781b038116811461280f575f80fd5b5f60a082840312156147bd575f80fd5b60405160a081018181106001600160401b03821117156147df576147df6146e2565b6040529050806147ee8361461b565b81526147fc6020840161461b565b602082015261480d60408401614797565b604082015261481e60608401614797565b606082015261482f60808401614770565b60808201525092915050565b5f805f80848603610180811215614850575f80fd5b614859866145ec565b94506040601f198201121561486c575f80fd5b6148746146f6565b60208701356001600160f01b038116811461488d575f80fd5b815261489b60408801614770565b602082015293506080605f19820112156148b3575f80fd5b506148bc61471e565b6148c860608701614781565b81526148d660808701614770565b60208201526148e760a08701614770565b60408201526148f860c08701614781565b6060820152915061490c8660e087016147ad565b905092959194509250565b634e487b7160e01b5f52602160045260245ffd5b606081016007851061494b57634e487b7160e01b5f52602160045260245ffd5b93815261ffff92909216602083015260409091015290565b5f8083601f840112614973575f80fd5b5081356001600160401b03811115614989575f80fd5b6020830191508360208260051b85010111156149a3575f80fd5b9250929050565b5f805f805f805f806080898b0312156149c1575f80fd5b88356001600160401b03808211156149d7575f80fd5b6149e38c838d01614963565b909a50985060208b01359150808211156149fb575f80fd5b614a078c838d01614963565b909850965060408b0135915080821115614a1f575f80fd5b614a2b8c838d01614963565b909650945060608b0135915080821115614a43575f80fd5b50614a508b828c01614963565b999c989b5096995094979396929594505050565b5f6020808385031215614a75575f80fd5b82356001600160401b0380821115614a8b575f80fd5b818501915085601f830112614a9e575f80fd5b813581811115614ab057614ab06146e2565b8060051b9150614ac1848301614740565b8181529183018401918481019088841115614ada575f80fd5b938501935b83851015614aff57614af0856145ec565b82529385019390850190614adf565b98975050505050505050565b5f60208284031215614b1b575f80fd5b5035919050565b5f805f60408486031215614b34575f80fd5b83356001600160401b03811115614b49575f80fd5b614b5586828701614963565b9094509250506020840135614b698161464a565b809150509250925092565b5f8060408385031215614b85575f80fd5b614b8e836145ec565b91506146d9602084016145ec565b5f805f806101408587031215614bb0575f80fd5b614bb98561461b565b9350614bc88660208701614699565b9250610100850135614bd98161464a565b915061490c61012086016145ec565b5f8060208385031215614bf9575f80fd5b82356001600160401b03811115614c0e575f80fd5b614c1a85828601614963565b90969095509350505050565b5f5b83811015614c40578181015183820152602001614c28565b50505f910152565b5f8151808452614c5f816020860160208601614c26565b601f01601f19169290920160200192915050565b5f60208083016020845280855180835260408601915060408160051b8701019250602087015f5b82811015614cc857603f19888603018452614cb6858351614c48565b94509285019290850190600101614c9a565b5092979650505050505050565b80516001600160e01b03908116835260208083015161ffff908116918501919091526040808401519091169084015260609182015116910152565b82516001600160f01b0316815260208084015161ffff169082015260c08101612a576040830184614cd5565b5f805f60608486031215614d4e575f80fd5b614d578461461b565b9250614d6560208501614797565b915061469060408501614797565b6001600160581b038082511683528060208301511660208401525060408101516001600160781b038082166040850152806060840151166060850152505061ffff60808201511660808301525050565b6101408101614dd28286614cd5565b614ddf6080830185614d73565b821515610120830152949350505050565b634e487b7160e01b5f52601160045260245ffd5b61ffff82811682821603908082111561249157612491614df0565b634e487b7160e01b5f52600160045260245ffd5b6001600160581b0382811682821603908082111561249157612491614df0565b8082028115828204841417612a9257612a92614df0565b80820180821115612a9257612a92614df0565b5f8060408385031215614e8e575f80fd5b505080516020909101519092909150565b634e487b7160e01b5f52601260045260245ffd5b5f82614ec157614ec1614e9f565b500490565b81810381811115612a9257612a92614df0565b5f82614ee757614ee7614e9f565b500690565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614f10575f80fd5b813580600a0b8114612a57575f80fd5b5f60208284031215614f30575f80fd5b5051919050565b6001600160781b03818116838216028082169190828114614f5a57614f5a614df0565b505092915050565b6001600160581b03861681526001600160a01b03858116602083015261016082019080614f8e876145ec565b16604084015280614fa1602088016145ec565b1660608401525060408501356080830152606085013560a0830152608085013560ff81168114614fcf575f80fd5b60ff1660c08381019190915260a086013560e084015294909401356101008201529115156101208301526001600160a01b03166101409091015292915050565b5f808335601e19843603018112615024575f80fd5b8301803591506001600160401b0382111561503d575f80fd5b6020019150368190038213156149a3575f80fd5b818382375f9101908152919050565b5f60208284031215615070575f80fd5b81516001600160401b0380821115615086575f80fd5b818401915084601f830112615099575f80fd5b8151818111156150ab576150ab6146e2565b6150be601f8201601f1916602001614740565b91508082528560208285010111156150d4575f80fd5b6150e5816020840160208601614c26565b50949350505050565b602081525f612a576020830184614c48565b60208082526023908201527f476f7665726e616e63653a20757365722d70726f78792d6e6f742d6465706c6f6040820152621e595960ea1b606082015260800190565b6001600160d01b0382811682821681810283169291811582850482141761516c5761516c614df0565b50505092915050565b6001600160d01b0381811683821601908082111561249157612491614df0565b5f6001600160d01b03838116806151ae576151ae614e9f565b92169190910492915050565b6001600160d01b0382811682821603908082111561249157612491614df0565b6001600160581b0381811683821601908082111561249157612491614df0565b5f81600a0b6001600160571b0319810361521657615216614df0565b5f0392915050565b600a81810b9083900b016001600160571b0381136a7fffffffffffffffffffff1982121715612a9257612a92614df0565b61ffff86811682526001600160a01b0386166020830152610180820190615295604084018780516001600160581b031682526020908101516001600160781b0316910152565b84516001600160581b03908116608085015260208601511660a084015260408501511660c083015261455d60e0830184614d73565b6001600160781b0382811682821603908082111561249157612491614df0565b5f602082840312156152fa575f80fd5b8151612a578161464a565b5f600160ff1b820161531957615319614df0565b505f0390565b5f8251615330818460208701614c26565b919091019291505056fea26469706673582212207c8aca494c606851d6ce92ed1d9a36a4bbb9f7d57cefde59074d285e103bc4a664736f6c63430008180033610100604052348015610010575f80fd5b5060405161105038038061105083398101604081905261002f9161006b565b6001600160a01b0392831660805290821660a0521660c0523360e0526100ab565b80516001600160a01b0381168114610066575f80fd5b919050565b5f805f6060848603121561007d575f80fd5b61008684610050565b925061009460208501610050565b91506100a260408501610050565b90509250925092565b60805160a05160c05160e051610f0261014e5f395f818161016b015281816102ac015281816104fc015261086401525f81816101f0015281816102290152818161042c015261066701525f818161019e01528181610309015281816105e30152818161092501528181610ad30152610b6a01525f8181610120015281816103ab01528181610550015281816106dc0152818161077c01526109a30152610f025ff3fe60806040526004361061007c575f3560e01c8063886117361161004c578063886117361461015a57806399ad68a71461018d578063eb876bf7146101c0578063f556a79c146101df575f80fd5b80630b76619b1461008757806338fb3599146100bc5780633fdf42e3146100f05780637f6ec4551461010f575f80fd5b3661008357005b5f80fd5b348015610092575f80fd5b5061009b610212565b6040516affffffffffffffffffffff90911681526020015b60405180910390f35b3480156100c7575f80fd5b506100db6100d6366004610d0d565b61029f565b604080519283526020830191909152016100b3565b3480156100fb575f80fd5b506100db61010a366004610d59565b6104ef565b34801561011a575f80fd5b506101427f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100b3565b348015610165575f80fd5b506101427f000000000000000000000000000000000000000000000000000000000000000081565b348015610198575f80fd5b506101427f000000000000000000000000000000000000000000000000000000000000000081565b3480156101cb575f80fd5b506100db6101da366004610d94565b610857565b3480156101ea575f80fd5b506101427f000000000000000000000000000000000000000000000000000000000000000081565b6040516305a4d3f160e21b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906316934fc490602401602060405180830381865afa158015610276573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061029a9190610dfe565b905090565b5f80336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102f25760405162461bcd60e51b81526004016102e990610e15565b60405180910390fd5b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610356573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061037a9190610dfe565b6040516323b872dd60e01b81526001600160a01b038881166004830152306024830152604482018a905291925047917f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303815f875af11580156103f1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104159190610e4c565b5060405163534a7e1d60e11b8152600481018990527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a694fc3a906024015f604051808303815f87803b158015610475575f80fd5b505af1158015610487573d5f803e3d5ffd5b5050604080518b81526001600160a01b038b1660208201527f2bccdce62e5aec7ee273161a374088a6da4311d0e688784bde3c1cec8a3c003a935001905060405180910390a185156104e4576104de858383610ab2565b90945092505b505094509492505050565b5f80336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105395760405162461bcd60e51b81526004016102e990610e15565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561059d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105c19190610dfe565b6040516370a0823160e01b81523060048201529091505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610628573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061064c9190610dfe565b6040516305c2fbcf60e31b81526004810189905290915047907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e17de78906024015f604051808303815f87803b1580156106b0575f80fd5b505af11580156106c2573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201525f92507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691506370a0823190602401602060405180830381865afa15801561072a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061074e9190610dfe565b905080156107e85760405163a9059cbb60e01b81526001600160a01b038881166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303815f875af11580156107c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107e69190610e4c565b505b6001600160a01b0387167ff960dbf9e5d0682f7a298ed974e33a28b4464914b7a2bfac12ae419a9afeb28061081d8684610e6e565b60408051918252602082018590520160405180910390a2871561084b57610845878484610ab2565b90965094505b50505050935093915050565b5f80336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146108a15760405162461bcd60e51b81526004016102e990610e15565b6108ae6020860186610e93565b6001600160a01b0316866001600160a01b03161461090e5760405162461bcd60e51b815260206004820152601b60248201527f5573657250726f78793a206f776e65722d6e6f742d73656e646572000000000060448201526064016102e9565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610972573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109969190610dfe565b9050476001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d505accf6109d560208a018a610e93565b6109e560408b0160208c01610e93565b60408b013560608c01356109ff60a08e0160808f01610eac565b6040516001600160e01b031960e088901b1681526001600160a01b0395861660048201529490931660248501526044840191909152606483015260ff16608482015260a08a013560a482015260c08a013560c482015260e4015f604051808303815f87803b158015610a6f575f80fd5b505af1925050508015610a80575060015b50610a8d8989888861029f565b50508515610aa657610aa0858383610ab2565b90945092505b50509550959350505050565b6040516370a0823160e01b81523060048201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610b18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b3c9190610dfe565b91508115610bd65760405163a9059cbb60e01b81526001600160a01b038681166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303815f875af1158015610bb0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bd49190610e4c565b505b50478015610c75575f856001600160a01b0316826040515f6040518083038185875af1925050503d805f8114610c27576040519150601f19603f3d011682016040523d82523d5f602084013e610c2c565b606091505b5050905080610c735760405162461bcd60e51b8152602060048201526013602482015272155cd95c941c9bde1e4e88195d1a0b59985a5b606a1b60448201526064016102e9565b505b6001600160a01b0385167fc8706b1b13fe53cf2c02ef30ed1caa67ae1afbdaf0bf43da98358f104d9a37b0610caa8685610e6e565b84610cb58786610e6e565b604080519384526020840192909252908201526060810184905260800160405180910390a2935093915050565b80356001600160a01b0381168114610cf8575f80fd5b919050565b8015158114610d0a575f80fd5b50565b5f805f8060808587031215610d20575f80fd5b84359350610d3060208601610ce2565b92506040850135610d4081610cfd565b9150610d4e60608601610ce2565b905092959194509250565b5f805f60608486031215610d6b575f80fd5b833592506020840135610d7d81610cfd565b9150610d8b60408501610ce2565b90509250925092565b5f805f805f858703610160811215610daa575f80fd5b86359550610dba60208801610ce2565b945060e0603f1982011215610dcd575f80fd5b50604086019250610120860135610de381610cfd565b9150610df26101408701610ce2565b90509295509295909350565b5f60208284031215610e0e575f80fd5b5051919050565b6020808252601f908201527f5573657250726f78793a2063616c6c65722d6e6f742d7374616b696e67563200604082015260600190565b5f60208284031215610e5c575f80fd5b8151610e6781610cfd565b9392505050565b81810381811115610e8d57634e487b7160e01b5f52601160045260245ffd5b92915050565b5f60208284031215610ea3575f80fd5b610e6782610ce2565b5f60208284031215610ebc575f80fd5b813560ff81168114610e67575f80fdfea264697066735822122076abccf10289bcbd1e5e511c36bad58de1747880f5b6884481ba93813298c56864736f6c634300081800338be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0000000000000000000000000eeec870468b25d515f5a125c30574912f1626c98000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64630000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e9000000000000000000000000000000000000000000000000000000000000015180000000000000000000000000835e072530503ddda07b2f35e05dd3e647a0d9f200000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405260043610610327575f3560e01c80638a00b67a116101a3578063a9952a0c116100f2578063cc904bec11610092578063e4a61d951161006d578063e4a61d9514610ce1578063e76c01e414610d14578063e772b76514610d45578063f556a79c14610d78575f80fd5b8063cc904bec14610c5d578063cc9baa1214610c94578063cf84a88c14610cb3575f80fd5b8063b1d8f274116100cd578063b1d8f27414610bbe578063bfd79cae14610beb578063c20fb59e14610c1f578063c3140c4914610c3e575f80fd5b8063a9952a0c14610b3c578063ac85912e14610b8a578063ac9650d814610b9e575f80fd5b8063900cf0cf1161015d578063985b11f911610138578063985b11f914610a9f578063a5c29ba514610ad5578063a5e8455d14610af4578063a70b9f0c14610b09575f80fd5b8063900cf0cf146109ea57806391050f40146109fe578063972e6d6614610a80575f80fd5b80638a00b67a146108c25780638b7d38a1146108f55780638d4f0b6c146109095780638da5cb5b1461093c5780638eb9399e146109595780638f32d59b146109c0575f80fd5b80634e0846c7116102795780636f844ae2116102195780637f6ec455116101f45780637f6ec455146108325780638113630d146108655780638777e0951461088457806388edf9be146108a3575f80fd5b80636f844ae2146107c1578063727d0f35146107e057806378a1bdd414610813575f80fd5b8063539054021161025457806353905402146106a05780635c79696c1461072f57806364b4f7511461075b5780636bca7c551461078e575f80fd5b80634e0846c7146105b257806350283275146105e0578063524fcc80146105f4575f80fd5b80631cd2a05d116102e457806338f3e285116102bf57806338f3e285146104ff5780633c12e04e146105415780633f37dc771461056057806346d62a631461057f575f80fd5b80631cd2a05d1461047a5780632591003a146104ad5780632c2cc7da146104e0575f80fd5b80630df9049b1461032b5780630ecc535f146103715780630f2f6aa5146103de5780631030e7b6146103ff57806315e5a1e5146104325780631b4ff3201461045b575b5f80fd5b348015610336575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000001518081565b6040519081526020015b60405180910390f35b34801561037c575f80fd5b506103b761038b366004614602565b60066020525f90815260409020546001600160581b03811690600160581b90046001600160781b031682565b604080516001600160581b0390931683526001600160781b03909116602083015201610368565b3480156103e9575f80fd5b506103fd6103f8366004614602565b610dab565b005b34801561040a575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000481565b34801561043d575f80fd5b506104466110c9565b60405163ffffffff9091168152602001610368565b348015610466575f80fd5b506103fd610475366004614631565b611151565b348015610485575f80fd5b5061035e7f00000000000000000000000000000000000000000000001b1ae4d6e2ef50000081565b3480156104b8575f80fd5b5061035e7f00000000000000000000000000000000000000000000000000038d7ea4c6800081565b3480156104eb575f80fd5b506103fd6104fa366004614657565b611158565b34801561050a575f80fd5b5061052e610519366004614602565b60096020525f908152604090205461ffff1681565b60405161ffff9091168152602001610368565b34801561054c575f80fd5b506103fd61055b366004614631565b611204565b34801561056b575f80fd5b506103fd61057a3660046146af565b611210565b34801561058a575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105bd575f80fd5b506105d16105cc36600461483b565b611220565b6040516103689392919061492b565b3480156105eb575f80fd5b506104466114f1565b3480156105ff575f80fd5b5061065d61060e366004614602565b60076020525f9081526040902080546001909101546001600160581b0380831692600160581b900416906001600160781b0380821691600160781b810490911690600160f01b900461ffff1685565b604080516001600160581b0396871681529590941660208601526001600160781b039283169385019390935216606083015261ffff16608082015260a001610368565b3480156106ab575f80fd5b506106f66106ba366004614602565b60046020525f9081526040902080546001909101546001600160e01b038083169261ffff600160e01b8204811693600160f01b90920416911684565b60405161036894939291906001600160e01b03948516815261ffff93841660208201529190921660408201529116606082015260800190565b34801561073a575f80fd5b50610743611579565b6040516001600160a01b039091168152602001610368565b348015610766575f80fd5b5061035e7f0000000000000000000000000000000000000000000000056bc75e2d6310000081565b348015610799575f80fd5b5061035e7f00000000000000000000000000000000000000000000000029a2241af62c000081565b3480156107cc575f80fd5b506103fd6107db3660046149aa565b6115e2565b3480156107eb575f80fd5b506107437f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca646381565b34801561081e575f80fd5b506103fd61082d366004614602565b611a57565b34801561083d575f80fd5b506107437f000000000000000000000000eeec870468b25d515f5a125c30574912f1626c9881565b348015610870575f80fd5b506103fd61087f366004614a64565b611e39565b34801561088f575f80fd5b5061035e61089e366004614b0b565b611f6f565b3480156108ae575f80fd5b506103fd6108bd366004614b22565b612037565b3480156108cd575f80fd5b5061035e7f000000000000000000000000000000000000000000000000006a94d74f43000081565b348015610900575f80fd5b5061035e6120c8565b348015610914575f80fd5b506107437f0000000000000000000000007442782ab523d0ab806cb8da018561e40787aa6981565b348015610947575f80fd5b506001546001600160a01b0316610743565b348015610964575f80fd5b5061096d6120ea565b6040805184516001600160f01b0316815260209485015161ffff168582015283516001600160581b03169181019190915292909101516001600160781b031660608301521515608082015260a001610368565b3480156109cb575f80fd5b506001546001600160a01b031633146040519015158152602001610368565b3480156109f5575f80fd5b5061052e6121d9565b348015610a09575f80fd5b50610a56610a18366004614b74565b600860209081525f92835260408084209091529082529020546001600160581b0380821691600160581b810490911690600160b01b900461ffff1683565b604080516001600160581b03948516815293909216602084015261ffff1690820152606001610368565b348015610a8b575f80fd5b506105d1610a9a366004614602565b612267565b348015610aaa575f80fd5b50610abd6a52b7d2dcc80cd2e400000081565b6040516001600160781b039091168152602001610368565b348015610ae0575f80fd5b506103fd610aef366004614b9c565b6122a1565b348015610aff575f80fd5b5061035e60025481565b348015610b14575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000007e90081565b348015610b47575f80fd5b50600354610b68906001600160f01b03811690600160f01b900461ffff1682565b604080516001600160f01b03909316835261ffff909116602083015201610368565b348015610b95575f80fd5b5061035e61233b565b610bb1610bac366004614be8565b612352565b6040516103689190614c73565b348015610bc9575f80fd5b50610bdd610bd8366004614602565b612498565b604051610368929190614d10565b348015610bf6575f80fd5b50610c0a610c05366004614602565b6124f7565b60408051928352602083019190915201610368565b348015610c2a575f80fd5b5061035e610c39366004614602565b6125ad565b348015610c49575f80fd5b506103fd610c58366004614657565b612814565b348015610c68575f80fd5b50610c7c610c77366004614d3c565b612a2d565b6040516001600160d01b039091168152602001610368565b348015610c9f575f80fd5b50610743610cae366004614602565b612a5e565b348015610cbe575f80fd5b50610cd2610ccd366004614602565b612a98565b60405161036893929190614dc3565b348015610cec575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000481565b348015610d1f575f80fd5b506005546103b7906001600160581b03811690600160581b90046001600160781b031682565b348015610d50575f80fd5b5061035e7f00000000000000000000000000000000000000000000003635c9adc5dea0000081565b348015610d83575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b610db3612c68565b5f80610dbd612c90565b915091505f80610dcc85612e06565b915091505f610ddd86868585611220565b509091505f9050816006811115610df657610df6614917565b03610e565760405162461bcd60e51b815260206004820152602560248201527f476f7665726e616e63653a20696e69746961746976652d6e6f742d72656769736044820152641d195c995960da1b60648201526084015b60405180910390fd5b6001816006811115610e6a57610e6a614917565b03610ec15760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a20696e69746961746976652d696e2d7761726d2d756044820152600760fc1b6064820152608401610e4d565b6005816006811115610ed557610ed5614917565b14610f335760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a2063616e6e6f742d756e72656769737465722d696e604482015267697469617469766560c01b6064820152608401610e4d565b5f610f3c6121d9565b9050610f49600182614e04565b61ffff16836080015161ffff1610610f6357610f63614e1f565b6020850151604084015186518551610f8793929190610f829082614e33565b612f39565b6001600160781b03166020860152825185516001600160581b0391821691161015610fb457610fb4614e1f565b825185518690610fc5908390614e33565b6001600160581b039081169091528651600580546020808b01516001600160781b0316600160581b026001600160d01b031990921693909416929092179190911790556001600160a01b0389165f81815260098352604090819020805461ffff191661ffff90811790915581519283528516928201929092527f8231be6875060a0fe4f35eaa6e112ebc15b8c04993114fe41c976bcb0a8c5ad892500160405180910390a160405161ffff821660248201526110b690889062055730905f9060440160408051601f198184030181529190526020810180516001600160e01b0316630a9a78f360e11b1790526130f1565b505050505050506110c660015f55565b50565b5f806110d36121d9565b90508061ffff165f036110e7575f91505090565b7f000000000000000000000000000000000000000000000000000000000007e900611113600183614e04565b61ffff166111219190614e53565b61114b907f0000000000000000000000000000000000000000000000000000000000000000614e6a565b91505090565b6110c6815f335b611160612c68565b5f61116a84613152565b6040516338fb359960e01b81526001600160581b038616600482015233602482015284151560448201526001600160a01b038481166064830152919250908216906338fb35999060840160408051808303815f875af11580156111cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111f39190614e7d565b5050506111ff60015f55565b505050565b6110c681600133612814565b61121c82825f336122a1565b5050565b6001600160a01b0384165f908152600960205260408120548190819061ffff16810361125357505f9150819050806114e7565b61125b6121d9565b6001600160a01b0388165f9081526009602052604090205461ffff91821691160361128e5750600191505f9050806114e7565b6001600160a01b0387165f9081526007602090815260408083206001015460099092529091205461ffff600160f01b9092048216935061fffe199116016112da5750600691505f6114e7565b60016112e46121d9565b6112ee9190614e04565b61ffff168261ffff161061130557600492506114e7565b5f61131b875f01516001600160f01b0316611f6f565b8651606088015189519293506001600160e01b03918216929116906001600160f01b0316838311801561134d57508282105b15611428578061137f6127107f000000000000000000000000000000000000000000000000006a94d74f430000614eb3565b61138f85655af3107a4000614e53565b6113999190614eb3565b116113a6576113a6614e1f565b5f6113d97f000000000000000000000000000000000000000000000000006a94d74f430000670de0b6b3a7640000614eb3565b6113e4906001614e6a565b90505f816002548484886113f89190614e53565b6114029190614eb3565b61140c9190614e53565b6114169190614eb3565b6003995096506114e795505050505050565b60016114326121d9565b61143c9190614e04565b61ffff167f0000000000000000000000000000000000000000000000000000000000000004896080015161ffff166114749190614e6a565b10806114c5575082821180156114c55750670de0b6b3a76400006114b87f00000000000000000000000000000000000000000000000029a2241af62c000086614e53565b6114c29190614eb3565b82115b156114db5750600595505f93506114e792505050565b50600295505f93505050505b9450945094915050565b5f7f000000000000000000000000000000000000000000000000000000000000000042101561151f57505f90565b7f000000000000000000000000000000000000000000000000000000000007e90061156a7f000000000000000000000000000000000000000000000000000000000000000042614ec6565b6115749190614ed9565b905090565b5f806115a57f0000000000000000000000007442782ab523d0ab806cb8da018561e40787aa69336133b3565b6040519091506001600160a01b0382169033907fda66ba232f4fb8c122b7026f55eeff1d0b9cf2560b7873b2bba6eaab4c3d5989905f90a3919050565b6115ea612c68565b8483146116225760405162461bcd60e51b8152602060048201526006602482015265098cadccee8d60d31b6044820152606401610e4d565b80831461165a5760405162461bcd60e51b8152602060048201526006602482015265098cadccee8d60d31b6044820152606401610e4d565b611664888861341e565b61166e868661341e565b6116a98484808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506134fb92505050565b6116e48282808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506134fb92505050565b61174f8484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f9201919091525061357092505050565b5f61175a8989613614565b335f908152600660209081526040918290208251808401909352546001600160581b038116808452600160581b9091046001600160781b03169183019190915291925090156117dd5760405162461bcd60e51b815260206004820152600f60248201526e1b5d5cdd0818994818481c995cd95d608a1b6044820152606401610e4d565b7f00000000000000000000000000000000000000000000000000000000000151806118066114f1565b63ffffffff1611156119a7575f5b878110156119a5575f805b845181101561191f578a8a8481811061183a5761183a614eec565b905060200201602081019061184f9190614602565b6001600160a01b031685828151811061186a5761186a614eec565b60200260200101515f01516001600160a01b031603611917576001915084818151811061189957611899614eec565b602002602001015160200151600a0b8989858181106118ba576118ba614eec565b90506020020160208101906118cf9190614f00565b600a0b13156119125760405162461bcd60e51b815260206004820152600f60248201526e43616e6e6f7420696e63726561736560881b6044820152606401610e4d565b61191f565b60010161181f565b508061199c5787878381811061193757611937614eec565b905060200201602081019061194c9190614f00565b600a0b1561199c5760405162461bcd60e51b815260206004820181905260248201527f4d757374206265207a65726f20666f72206e657720696e6974696174697665736044820152606401610e4d565b50600101611814565b505b611a428888808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284375f9201919091525050604080516020808b0282810182019093528a82529093508a9250899182918501908490808284375f920191909152506139b292505050565b5050611a4d60015f55565b5050505050505050565b611a5f612c68565b611ab46001600160a01b037f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64631633307f0000000000000000000000000000000000000000000000056bc75e2d63100000614282565b6001600160a01b038116611b0a5760405162461bcd60e51b815260206004820152601860248201527f476f7665726e616e63653a207a65726f2d6164647265737300000000000000006044820152606401610e4d565b5f611b1482612267565b509091505f9050816006811115611b2d57611b2d614917565b14611b8c5760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e69746961746976652d616c72656164792d726044820152681959da5cdd195c995960ba1b6064820152608401610e4d565b5f611b9633612a5e565b90505f611ba1612c90565b50335f908152600660209081526040918290208251808401909352546001600160581b0381168352600160581b90046001600160781b0316908201528151919250906001600160f01b0316670de0b6b3a7640000611c1f7f00000000000000000000000000000000000000000000000000038d7ea4c6800083614e53565b611c299190614eb3565b6040516305a4d3f160e21b81526001600160a01b038681166004830152611ce5917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa158015611c93573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cb79190614f20565b6a52b7d2dcc80cd2e4000000611ccb6110c9565b63ffffffff16611cdb9190614f37565b8560200151612a2d565b6001600160d01b03161015611d3c5760405162461bcd60e51b815260206004820152601d60248201527f476f7665726e616e63653a20696e73756666696369656e742d6c7174790000006044820152606401610e4d565b5f611d456121d9565b6001600160a01b0388165f908152600960205260409020805461ffff191661ffff83161790559050611d78600182614e04565b6001600160a01b0388165f8181526007602090815260409182902060010180546001600160f01b0316600160f01b61ffff96871602179055815192835233908301529183168183015290517f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a916060908290030190a160405161ffff821660248201526110b690889062055730905f9060440160408051601f198184030181529190526020810180516001600160e01b0316628152f560e81b1790526130f1565b6001546001600160a01b03163314611e935760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610e4d565b5f5b8151811015611f6657600160095f848481518110611eb557611eb5614eec565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548161ffff021916908361ffff1602179055507f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a828281518110611f2857611f28614eec565b602090810291909101810151604080516001600160a01b039092168252339282019290925260018183015290519081900360600190a1600101611e95565b506110c66142e9565b5f815f03611f7e57505f919050565b5f8083670de0b6b3a7640000600254611f979190614e53565b611fa19190614eb3565b90508015611fe95780611fdc670de0b6b3a76400007f00000000000000000000000000000000000000000000001b1ae4d6e2ef500000614e53565b611fe69190614eb3565b91505b61202f670de0b6b3a764000061201f7f000000000000000000000000000000000000000000000000006a94d74f43000087614e53565b6120299190614eb3565b83614332565b949350505050565b61203f612c68565b612049838361341e565b6120538383613614565b5080156120bf57335f908152600660205260409020546001600160581b0316156120bf5760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206d757374206265206120726573657400000000006044820152606401610e4d565b6111ff60015f55565b5f806120d2612c90565b50905061114b815f01516001600160f01b0316611f6f565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f8061211a6121d9565b6040805180820182526003546001600160f01b0381168252600160f01b900461ffff1660208083019190915282518084019093526005546001600160581b0381168452600160581b90046001600160781b031690830152955093509050612182600182614e04565b61ffff16846020015161ffff1610156121d3578251600192506121b3906a52b7d2dcc80cd2e4000000611ccb6110c9565b6001600160d01b031684526121c9600182614e04565b61ffff1660208501525b50909192565b5f7f000000000000000000000000000000000000000000000000000000000000000042101561220757505f90565b7f000000000000000000000000000000000000000000000000000000000007e9006122527f000000000000000000000000000000000000000000000000000000000000000042614ec6565b61225c9190614eb3565b611574906001614e6a565b5f805f80612273612c90565b5090505f8061228187612e06565b9150915061229187848484611220565b9550955095505050509193909250565b6122a9612c68565b5f6122b385613152565b60405163eb876bf760e01b81529091506001600160a01b0382169063eb876bf7906122ea9088903390899089908990600401614f62565b60408051808303815f875af1158015612305573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123299190614e7d565b50505061233560015f55565b50505050565b6003545f906001600160f01b031661114b81611f6f565b6060816001600160401b0381111561236c5761236c6146e2565b60405190808252806020026020018201604052801561239f57816020015b606081526020019060019003908161238a5790505b5090505f5b82811015612491575f80308686858181106123c1576123c1614eec565b90506020028101906123d3919061500f565b6040516123e1929190615051565b5f60405180830381855af49150503d805f8114612419576040519150601f19603f3d011682016040523d82523d5f602084013e61241e565b606091505b50915091508161246957604481511015612436575f80fd5b600481019050808060200190518101906124509190615060565b60405162461bcd60e51b8152600401610e4d91906150ee565b8084848151811061247c5761247c614eec565b602090810291909101015250506001016123a4565b5092915050565b6040805180820182525f808252602080830182905283516080810185528281529081018290529283018190526060830152906124d2612c68565b6124da612c90565b5091506124e683612e06565b5090506124f260015f55565b915091565b5f805f61250333612a5e565b9050806001600160a01b03163b5f0361252e5760405162461bcd60e51b8152600401610e4d90615100565b604051633fdf42e360e01b81525f6004820152600160248201526001600160a01b038581166044830152821690633fdf42e39060640160408051808303815f875af115801561257f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125a39190614e7d565b9250925050915091565b5f6125b6612c68565b5f6125bf612c90565b5090505f806125cd85612e06565b915091505f806125df87868686611220565b919350909150600390508260068111156125fb576125fb614917565b1461260d575f95505050505050612806565b60016126176121d9565b6126219190614e04565b61ffff16856020015161ffff161461263b5761263b614e1f565b60016126456121d9565b61264f9190614e04565b6001600160a01b038881165f90815260076020526040808220600101805461ffff95909516600160f01b026001600160f01b039095169490941790935591516370a0823160e01b81523060048201527f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca6463909116906370a0823190602401602060405180830381865afa1580156126e7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061270b9190614f20565b905080821115612719578091505b61274d6001600160a01b037f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca6463168984614347565b602086810151604080516001600160a01b038c16815292830185905261ffff90911682820152517fa1ee42ce2e3b0e300645a781ea3e7c494f626cfeb1c2cce20e2fa4a5da249d189181900360600190a16127fc88620557305f8960200151866040516024016127cd92919061ffff929092168252602082015260400190565b60408051601f198184030181529190526020810180516001600160e01b03166313440fab60e01b1790526130f1565b5090955050505050505b61280f60015f55565b919050565b61281c612c68565b335f90815260066020526040902080546001600160581b0316156128825760405162461bcd60e51b815260206004820152601e60248201527f476f7665726e616e63653a206d7573742d616c6c6f636174652d7a65726f00006044820152606401610e4d565b5f61288c33612a5e565b9050806001600160a01b03163b5f036128b75760405162461bcd60e51b8152600401610e4d90615100565b6040516305a4d3f160e21b81526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa15801561291f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129439190614f20565b604051633fdf42e360e01b81526001600160581b038816600482015286151560248201526001600160a01b0386811660448301529192505f91829190851690633fdf42e39060640160408051808303815f875af11580156129a6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129ca9190614e7d565b604080513381526001600160581b038c1660208201529081018390526060810182905291935091507f71330c97f647d5136a666c3cf5cef9ec255ec0468d9d7891929eff0f4845c4589060800160405180910390a150505050506111ff60015f55565b5f612a388383614378565b612a54906001600160781b03166001600160581b038616615143565b90505b9392505050565b5f612a927f0000000000000000000000007442782ab523d0ab806cb8da018561e40787aa696001600160a01b0384166143b6565b92915050565b604080516080810182525f8082526020820181905291810182905260608101919091526040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f80612aef6121d9565b6001600160a01b0386165f81815260046020908152604080832081516080808201845282546001600160e01b03808216845261ffff600160e01b8304811685890152600160f01b928390048116858801526001958601549091166060808601919091529888526007875296859020855160a08101875281546001600160581b038082168352600160581b90910416978101979097528401546001600160781b0380821696880196909652600160781b8104909516978601979097529590920490931693820193909352919650909450909150612bcb9082614e04565b61ffff16846020015161ffff161015612c6057600191505f6a52b7d2dcc80cd2e4000000612bf76110c9565b63ffffffff16612c079190614f37565b90505f612c1c855f0151838760400151612a2d565b90505f612c328660200151848860600151612a2d565b6001600160d01b038084168952811660608901529050612c53600185614e04565b61ffff1660208801525050505b509193909250565b60025f5403612c8a57604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f612cbf6120ea565b919450925090508015612e01578251602084015161ffff16600160f01b026001600160f01b03909116176003556040516370a0823160e01b81523060048201525f906001600160a01b037f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca646316906370a0823190602401602060405180830381865afa158015612d50573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d749190614f20565b90507f00000000000000000000000000000000000000000000003635c9adc5dea000008110612da35780612da5565b5f5b600255835160208501516040517ff8c3496a83a68868434a061a876d356df06c7ef5bd8476f13d496b966855686b92612df79290916001600160f01b0392909216825261ffff16602082015260400190565b60405180910390a1505b509091565b604080516080810182525f8082526020820181905291810182905260608101919091526040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f612e5d84612a98565b919450925090508015612f33576001600160a01b0384165f818152600460209081526040918290208651815488840151898601516001600160e01b039384166001600160f01b03199093168317600160e01b61ffff938416908102919091176001600160f01b0316600160f01b93909216929092021784556060808b0151600190950180546001600160e01b0319169590941694909417909255845195865292850192909252918301527f6f22c33d2cf529b6a07e72fdf3fd25bb07828d8017cc4d47405c3ec9cadf6ffd910160405180910390a15b50915091565b5f816001600160581b03165f03612f5157505f61202f565b5f612f6d6a52b7d2dcc80cd2e400000063ffffffff4216614f37565b90505f612f7a8288614378565b90505f612f878388614378565b90505f856001600160581b0316876001600160581b031611613019575f612fae8888614e33565b90505f612fcd6001600160781b0386166001600160581b038b16615143565b90505f612fec6001600160781b0386166001600160581b038516615143565b90505f612ff98284615175565b905061300e6001600160581b038b1682615195565b9450505050506130ab565b5f6130248789614e33565b90505f6130436001600160781b0386166001600160581b038b16615143565b90505f6130626001600160781b0386166001600160581b038516615143565b90505f816001600160d01b0316836001600160d01b03161015613085575f61308f565b61308f82846151ba565b90506130a46001600160581b038b1682615195565b9450505050505b836001600160781b0316816001600160d01b031611156130d1575f94505050505061202f565b6130e4816001600160781b0386166151ba565b9998505050505050505050565b5f6130fe846103e8614415565b61313d5760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b6044820152606401610e4d565b5f80835160208501868989f195945050505050565b5f80826001600160581b0316116131ab5760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a207a65726f2d6c7174792d616d6f756e74000000006044820152606401610e4d565b335f908152600660209081526040918290208251808401909352546001600160581b038116808452600160581b9091046001600160781b031691830191909152156132445760405162461bcd60e51b815260206004820152602360248201527f476f7665726e616e63653a206d7573742d62652d7a65726f2d616c6c6f63617460448201526234b7b760e91b6064820152608401610e4d565b5f61324e33612a5e565b9050806001600160a01b03163b5f0361326b57613269611579565b505b6040516305a4d3f160e21b81526001600160a01b03808316600483015282915f917f000000000000000000000000000000000000000000000000000000000000000016906316934fc490602401602060405180830381865afa1580156132d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132f79190614f20565b905061332384602001516a52b7d2dcc80cd2e4000000426133189190614f37565b83610f828a826151da565b6001600160781b039081166020868101918252335f81815260068352604090819020895181549551909616600160581b026001600160d01b03199095166001600160581b0396871617949094179093558251908152928916908301527f841ac74294f07d75868ac51778dc366b1fa11e8934b969cd5b0185c0e91dabb1910160405180910390a150949350505050565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760095ff590506001600160a01b038116612a92576040516330be1a3d60e21b815260040160405180910390fd5b805f81900361342c57505050565b5f5b613439600183614ec6565b811015612335575f61344c826001614e6a565b90505b828110156134f25784848281811061346957613469614eec565b905060200201602081019061347e9190614602565b6001600160a01b031685858481811061349957613499614eec565b90506020020160208101906134ae9190614602565b6001600160a01b0316036134ea5760405162461bcd60e51b815260206004820152600360248201526206475760ec1b6044820152606401610e4d565b60010161344f565b5060010161342e565b80515f5b818110156111ff575f83828151811061351a5761351a614eec565b6020026020010151600a0b12156135685760405162461bcd60e51b815260206004820152601260248201527143616e6e6f74206265206e6567617469766560701b6044820152606401610e4d565b6001016134ff565b5f5b82518110156111ff575f83828151811061358e5761358e614eec565b6020026020010151600a0b13806135c057505f8282815181106135b3576135b3614eec565b6020026020010151600a0b135b61360c5760405162461bcd60e51b815260206004820152601a60248201527f476f7665726e616e63653a20766f74696e67206e6f7468696e670000000000006044820152606401610e4d565b600101613572565b60605f826001600160401b0381111561362f5761362f6146e2565b60405190808252806020026020018201604052801561367857816020015b604080516060810182525f80825260208083018290529282015282525f1990920191018161364d5790505b5090505f836001600160401b03811115613694576136946146e2565b6040519080825280602002602001820160405280156136bd578160200160208202803683370190505b5090505f846001600160401b038111156136d9576136d96146e2565b604051908082528060200260200182016040528015613702578160200160208202803683370190505b5090505f5b8581101561396857335f9081526008602052604081208189898581811061373057613730614eec565b90506020020160208101906137459190614602565b6001600160a01b0316815260208082019290925260409081015f20815160608101835290546001600160581b03808216808452600160581b830490911694830194909452600160b01b900461ffff169181019190915291501515806137b657505f81602001516001600160581b0316115b6138025760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a206e6f7468696e6720746f207265736574000000006044820152606401610e4d565b80516001600160571b036001600160581b03909116111561382557613825614e1f565b6001600160571b036001600160581b031681602001516001600160581b0316111561385257613852614e1f565b604051806060016040528089898581811061386f5761386f614eec565b90506020020160208101906138849190614602565b6001600160a01b03168152602001825f0151600a0b81526020018260200151600a0b8152508583815181106138bb576138bb614eec565b60200260200101819052508482815181106138d8576138d8614eec565b6020026020010151602001516138ed906151fa565b8483815181106138ff576138ff614eec565b6020026020010190600a0b9081600a0b8152505084828151811061392557613925614eec565b60200260200101516040015161393a906151fa565b83838151811061394c5761394c614eec565b600a9290920b6020928302919091019091015250600101613707565b506139a88686808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508692508591506139b29050565b5090949350505050565b815183511480156139c4575080518351145b613a1a5760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a2061727261792d6c656e6774682d6d69736d6174636044820152600d60fb1b6064820152608401610e4d565b5f80613a24612c90565b915091505f613a316121d9565b335f9081526006602090815260408083208151808301909252546001600160581b0381168252600160581b90046001600160781b0316918101919091529192505b87518110156140ed575f888281518110613a8e57613a8e614eec565b602002602001015190505f888381518110613aab57613aab614eec565b602002602001015190505f888481518110613ac857613ac8614eec565b6020026020010151905081600a0b5f141580613ae7575080600a0b5f14155b613af357613af3614e1f565b5f80613afe85612e06565b915091505f613b0f868c8585611220565b505090505f85600a0b1380613b2657505f84600a0b135b15613bc4576002816006811115613b3f57613b3f614917565b1480613b5c57506003816006811115613b5a57613b5a614917565b145b80613b7857506004816006811115613b7657613b76614917565b145b613bc45760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206163746976652d766f74652d66736d00000000006044820152606401610e4d565b6006816006811115613bd857613bd8614917565b03613c36575f85600a0b13158015613bf357505f84600a0b13155b613c365760405162461bcd60e51b8152602060048201526014602482015273135d5cdd0818994818481dda5d1a191c985dd85b60621b6044820152606401610e4d565b5f6040518060a00160405280845f01516001600160581b0316815260200184602001516001600160581b0316815260200184604001516001600160781b0316815260200184606001516001600160781b03168152602001846080015161ffff168152509050613cb983604001518a60200151855f0151610f82875f01518b614432565b6001600160781b0316604084015260608301516020808b015190850151613ce6929190610f82818a614432565b6001600160781b031660608401528251613d009087614432565b6001600160581b031683526020830151613d1a9086614432565b6001600160581b0390811660208086019182526001600160a01b038a165f90815260079091526040908190208651815493518516600160581b026001600160b01b0319909416941693909317919091178255840151600190910180546060860151608087015161ffff16600160f01b026001600160f01b036001600160781b03928316600160781b026001600160f01b0319909416929095169190911791909117929092169190911790556006826006811115613dd957613dd9614917565b14613e925760208b015160408201518c518351613dfd93929190610f829082614e33565b6001600160781b031660208c015280518b516001600160581b0391821691161015613e2a57613e2a614e1f565b80518b518c90613e3b908390614e33565b6001600160581b031690525060208b015160408401518c518551613e6693929190610f8290826151da565b6001600160781b031660208c015282518b518c90613e859083906151da565b6001600160581b03169052505b335f9081526008602090815260408083206001600160a01b038b168452825291829020825160608101845290546001600160581b03808216808452600160581b830490911693830193909352600160b01b900461ffff1692810192909252613efa9088614432565b6001600160581b031681526020810151613f149087614432565b6001600160581b03908116602083015261ffff8c16604083015281511615801590613f4b575060208101516001600160581b031615155b15613f985760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63653a20766f74652d616e642d7665746f000000000000006044820152606401610e4d565b335f9081526008602090815260408083206001600160a01b038c1684528252918290208351815492850151938501516001600160581b039182166001600160b01b031990941693909317600160581b91909416029290921761ffff60b01b1916600160b01b61ffff90921691909102179055895161401f9061401a888a61521e565b614432565b6001600160581b03168a52604080513381526001600160a01b038a166020820152600a89810b8284015288900b606082015261ffff8d16608082015290517ffc65ef0a2ea71843c9c13a7a7c0bdccb27001f82e748634c3155cba36faccc2c9181900360a00190a16140d888620557305f8e338f878b6040516024016140a995949392919061524f565b60408051601f198184030181529190526020810180516001600160e01b0316633d0fb77360e01b1790526130f1565b505060019097019650613a7295505050505050565b5080516001600160581b031615806141b757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166316934fc461413833612a5e565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa15801561417a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061419e9190614f20565b6001600160581b0316815f01516001600160581b031611155b6142165760405162461bcd60e51b815260206004820152602a60248201527f476f7665726e616e63653a20696e73756666696369656e742d6f722d616c6c6f60448201526963617465642d6c71747960b01b6064820152608401610e4d565b8251600580546020958601516001600160781b03908116600160581b9081026001600160d01b03199384166001600160581b039687161717909355335f908152600688526040902085518154969098015190911690920293169490911693909317179091555050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526123359186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061446b565b6001546040515f916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b5f8183116143405781612a57565b5090919050565b6040516001600160a01b038381166024830152604482018390526111ff91859182169063a9059cbb906064016142b7565b5f6001600160781b03821615806143a05750816001600160781b0316836001600160781b0316105b156143ac57505f612a92565b612a5782846152ca565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101205f90612a57565b5f80603f83619c4001026040850201603f5a021015949350505050565b5f8082600a0b121561445857614447826144cc565b6144519084614e33565b9050612a92565b614461826144cc565b612a5790846151da565b5f61447f6001600160a01b038416836144ea565b905080515f141580156144a35750808060200190518101906144a191906152ea565b155b156111ff57604051635274afe760e01b81526001600160a01b0384166004820152602401610e4d565b5f8082600a0b126144dd5781612a92565b81600a0b612a9290615305565b6060612a5783835f845f80856001600160a01b0316848660405161450e919061531f565b5f6040518083038185875af1925050503d805f8114614548576040519150601f19603f3d011682016040523d82523d5f602084013e61454d565b606091505b509150915061455d868383614567565b9695505050505050565b60608261457c57614577826145c3565b612a57565b815115801561459357506001600160a01b0384163b155b156145bc57604051639996b31560e01b81526001600160a01b0385166004820152602401610e4d565b5080612a57565b8051156145d35780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461280f575f80fd5b5f60208284031215614612575f80fd5b612a57826145ec565b80356001600160581b038116811461280f575f80fd5b5f60208284031215614641575f80fd5b612a578261461b565b80151581146110c6575f80fd5b5f805f60608486031215614669575f80fd5b6146728461461b565b925060208401356146828161464a565b9150614690604085016145ec565b90509250925092565b5f60e082840312156146a9575f80fd5b50919050565b5f8061010083850312156146c1575f80fd5b6146ca8361461b565b91506146d98460208501614699565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715614718576147186146e2565b60405290565b604051608081016001600160401b0381118282101715614718576147186146e2565b604051601f8201601f191681016001600160401b0381118282101715614768576147686146e2565b604052919050565b803561ffff8116811461280f575f80fd5b80356001600160e01b038116811461280f575f80fd5b80356001600160781b038116811461280f575f80fd5b5f60a082840312156147bd575f80fd5b60405160a081018181106001600160401b03821117156147df576147df6146e2565b6040529050806147ee8361461b565b81526147fc6020840161461b565b602082015261480d60408401614797565b604082015261481e60608401614797565b606082015261482f60808401614770565b60808201525092915050565b5f805f80848603610180811215614850575f80fd5b614859866145ec565b94506040601f198201121561486c575f80fd5b6148746146f6565b60208701356001600160f01b038116811461488d575f80fd5b815261489b60408801614770565b602082015293506080605f19820112156148b3575f80fd5b506148bc61471e565b6148c860608701614781565b81526148d660808701614770565b60208201526148e760a08701614770565b60408201526148f860c08701614781565b6060820152915061490c8660e087016147ad565b905092959194509250565b634e487b7160e01b5f52602160045260245ffd5b606081016007851061494b57634e487b7160e01b5f52602160045260245ffd5b93815261ffff92909216602083015260409091015290565b5f8083601f840112614973575f80fd5b5081356001600160401b03811115614989575f80fd5b6020830191508360208260051b85010111156149a3575f80fd5b9250929050565b5f805f805f805f806080898b0312156149c1575f80fd5b88356001600160401b03808211156149d7575f80fd5b6149e38c838d01614963565b909a50985060208b01359150808211156149fb575f80fd5b614a078c838d01614963565b909850965060408b0135915080821115614a1f575f80fd5b614a2b8c838d01614963565b909650945060608b0135915080821115614a43575f80fd5b50614a508b828c01614963565b999c989b5096995094979396929594505050565b5f6020808385031215614a75575f80fd5b82356001600160401b0380821115614a8b575f80fd5b818501915085601f830112614a9e575f80fd5b813581811115614ab057614ab06146e2565b8060051b9150614ac1848301614740565b8181529183018401918481019088841115614ada575f80fd5b938501935b83851015614aff57614af0856145ec565b82529385019390850190614adf565b98975050505050505050565b5f60208284031215614b1b575f80fd5b5035919050565b5f805f60408486031215614b34575f80fd5b83356001600160401b03811115614b49575f80fd5b614b5586828701614963565b9094509250506020840135614b698161464a565b809150509250925092565b5f8060408385031215614b85575f80fd5b614b8e836145ec565b91506146d9602084016145ec565b5f805f806101408587031215614bb0575f80fd5b614bb98561461b565b9350614bc88660208701614699565b9250610100850135614bd98161464a565b915061490c61012086016145ec565b5f8060208385031215614bf9575f80fd5b82356001600160401b03811115614c0e575f80fd5b614c1a85828601614963565b90969095509350505050565b5f5b83811015614c40578181015183820152602001614c28565b50505f910152565b5f8151808452614c5f816020860160208601614c26565b601f01601f19169290920160200192915050565b5f60208083016020845280855180835260408601915060408160051b8701019250602087015f5b82811015614cc857603f19888603018452614cb6858351614c48565b94509285019290850190600101614c9a565b5092979650505050505050565b80516001600160e01b03908116835260208083015161ffff908116918501919091526040808401519091169084015260609182015116910152565b82516001600160f01b0316815260208084015161ffff169082015260c08101612a576040830184614cd5565b5f805f60608486031215614d4e575f80fd5b614d578461461b565b9250614d6560208501614797565b915061469060408501614797565b6001600160581b038082511683528060208301511660208401525060408101516001600160781b038082166040850152806060840151166060850152505061ffff60808201511660808301525050565b6101408101614dd28286614cd5565b614ddf6080830185614d73565b821515610120830152949350505050565b634e487b7160e01b5f52601160045260245ffd5b61ffff82811682821603908082111561249157612491614df0565b634e487b7160e01b5f52600160045260245ffd5b6001600160581b0382811682821603908082111561249157612491614df0565b8082028115828204841417612a9257612a92614df0565b80820180821115612a9257612a92614df0565b5f8060408385031215614e8e575f80fd5b505080516020909101519092909150565b634e487b7160e01b5f52601260045260245ffd5b5f82614ec157614ec1614e9f565b500490565b81810381811115612a9257612a92614df0565b5f82614ee757614ee7614e9f565b500690565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614f10575f80fd5b813580600a0b8114612a57575f80fd5b5f60208284031215614f30575f80fd5b5051919050565b6001600160781b03818116838216028082169190828114614f5a57614f5a614df0565b505092915050565b6001600160581b03861681526001600160a01b03858116602083015261016082019080614f8e876145ec565b16604084015280614fa1602088016145ec565b1660608401525060408501356080830152606085013560a0830152608085013560ff81168114614fcf575f80fd5b60ff1660c08381019190915260a086013560e084015294909401356101008201529115156101208301526001600160a01b03166101409091015292915050565b5f808335601e19843603018112615024575f80fd5b8301803591506001600160401b0382111561503d575f80fd5b6020019150368190038213156149a3575f80fd5b818382375f9101908152919050565b5f60208284031215615070575f80fd5b81516001600160401b0380821115615086575f80fd5b818401915084601f830112615099575f80fd5b8151818111156150ab576150ab6146e2565b6150be601f8201601f1916602001614740565b91508082528560208285010111156150d4575f80fd5b6150e5816020840160208601614c26565b50949350505050565b602081525f612a576020830184614c48565b60208082526023908201527f476f7665726e616e63653a20757365722d70726f78792d6e6f742d6465706c6f6040820152621e595960ea1b606082015260800190565b6001600160d01b0382811682821681810283169291811582850482141761516c5761516c614df0565b50505092915050565b6001600160d01b0381811683821601908082111561249157612491614df0565b5f6001600160d01b03838116806151ae576151ae614e9f565b92169190910492915050565b6001600160d01b0382811682821603908082111561249157612491614df0565b6001600160581b0381811683821601908082111561249157612491614df0565b5f81600a0b6001600160571b0319810361521657615216614df0565b5f0392915050565b600a81810b9083900b016001600160571b0381136a7fffffffffffffffffffff1982121715612a9257612a92614df0565b61ffff86811682526001600160a01b0386166020830152610180820190615295604084018780516001600160581b031682526020908101516001600160781b0316910152565b84516001600160581b03908116608085015260208601511660a084015260408501511660c083015261455d60e0830184614d73565b6001600160781b0382811682821603908082111561249157612491614df0565b5f602082840312156152fa575f80fd5b8151612a578161464a565b5f600160ff1b820161531957615319614df0565b505f0390565b5f8251615330818460208701614c26565b919091019291505056fea26469706673582212207c8aca494c606851d6ce92ed1d9a36a4bbb9f7d57cefde59074d285e103bc4a664736f6c63430008180033

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

000000000000000000000000eeec870468b25d515f5a125c30574912f1626c98000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64630000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e9000000000000000000000000000000000000000000000000000000000000015180000000000000000000000000835e072530503ddda07b2f35e05dd3e647a0d9f200000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _lqty (address): 0xeEEc870468b25D515F5a125C30574912F1626C98
Arg [1] : _lusd (address): 0xf0076A2160247884DA5AB0A5625F192cD3cA6463
Arg [2] : _stakingV1 (address): 0x0000000000000000000000000000000000000000
Arg [3] : _bold (address): 0xf0076A2160247884DA5AB0A5625F192cD3cA6463
Arg [4] : _config (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [5] : _owner (address): 0x835E072530503DddA07B2F35e05DD3e647a0D9F2

-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 000000000000000000000000eeec870468b25d515f5a125c30574912f1626c98
Arg [1] : 000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca6463
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca6463
Arg [4] : 0000000000000000000000000000000000000000000000056bc75e2d63100000
Arg [5] : 00000000000000000000000000000000000000000000000000038d7ea4c68000
Arg [6] : 00000000000000000000000000000000000000000000000029a2241af62c0000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [9] : 000000000000000000000000000000000000000000000000006a94d74f430000
Arg [10] : 00000000000000000000000000000000000000000000001b1ae4d6e2ef500000
Arg [11] : 00000000000000000000000000000000000000000000003635c9adc5dea00000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [13] : 000000000000000000000000000000000000000000000000000000000007e900
Arg [14] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [15] : 000000000000000000000000835e072530503ddda07b2f35e05dd3e647a0d9f2
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000220
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

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