ETH Price: $2,924.92 (-2.14%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Multi Delegate C...239922252025-12-11 21:49:474 days ago1765489787IN
Liquity V2 : Old Governance
0 ETH0.000385572.11110959
Multi Delegate C...237984912025-11-14 16:01:3532 days ago1763136095IN
Liquity V2 : Old Governance
0 ETH0.00020441.11926361
Multi Delegate C...237938412025-11-14 0:25:5932 days ago1763079959IN
Liquity V2 : Old Governance
0 ETH0.000128870.17791833
Multi Delegate C...236414592025-10-23 16:42:3553 days ago1761237755IN
Liquity V2 : Old Governance
0 ETH0.000047780.26164177
Multi Delegate C...235557312025-10-11 16:39:3566 days ago1760200775IN
Liquity V2 : Old Governance
0 ETH0.000399432.1870098
Multi Delegate C...235521092025-10-11 4:30:3566 days ago1760157035IN
Liquity V2 : Old Governance
0 ETH0.001116892.67211132
Multi Delegate C...234417342025-09-25 18:06:4781 days ago1758823607IN
Liquity V2 : Old Governance
0 ETH0.001759099.63251985
Multi Delegate C...234139002025-09-21 20:47:4785 days ago1758487667IN
Liquity V2 : Old Governance
0 ETH0.000491751.13915498
Multi Delegate C...233428862025-09-11 22:45:1195 days ago1757630711IN
Liquity V2 : Old Governance
0 ETH0.000034130.20201786
Multi Delegate C...233124472025-09-07 16:35:23100 days ago1757262923IN
Liquity V2 : Old Governance
0 ETH0.000190920.25889445
Multi Delegate C...232757052025-09-02 13:25:59105 days ago1756819559IN
Liquity V2 : Old Governance
0 ETH0.000793651.3397162
Claim From Staki...232756942025-09-02 13:23:47105 days ago1756819427IN
Liquity V2 : Old Governance
0 ETH0.000061650.5703928
Multi Delegate C...232597252025-08-31 7:46:47107 days ago1756626407IN
Liquity V2 : Old Governance
0 ETH0.000023090.14872865
Multi Delegate C...232591532025-08-31 5:51:59107 days ago1756619519IN
Liquity V2 : Old Governance
0 ETH0.000109830.25947059
Multi Delegate C...232560602025-08-30 19:31:23107 days ago1756582283IN
Liquity V2 : Old Governance
0 ETH0.000203431.20415898
Multi Delegate C...231336022025-08-13 17:24:11124 days ago1755105851IN
Liquity V2 : Old Governance
0 ETH0.001818243.41541829
Multi Delegate C...230854732025-08-07 0:01:35131 days ago1754524895IN
Liquity V2 : Old Governance
0 ETH0.000144180.19516041
Multi Delegate C...230632842025-08-03 21:40:11134 days ago1754257211IN
Liquity V2 : Old Governance
0 ETH0.000034470.20406348
Multi Delegate C...230612132025-08-03 14:44:11135 days ago1754232251IN
Liquity V2 : Old Governance
0 ETH0.000187760.24971869
Multi Delegate C...230217052025-07-29 2:06:59140 days ago1753754819IN
Liquity V2 : Old Governance
0 ETH0.000270580.40034928
Multi Delegate C...230120182025-07-27 17:36:47141 days ago1753637807IN
Liquity V2 : Old Governance
0 ETH0.000076710.42004105
Multi Delegate C...229685952025-07-21 15:55:23148 days ago1753113323IN
Liquity V2 : Old Governance
0 ETH0.004180526.80961833
Multi Delegate C...229588832025-07-20 7:22:23149 days ago1752996143IN
Liquity V2 : Old Governance
0 ETH0.00138662.64528518
Multi Delegate C...229516432025-07-19 7:05:23150 days ago1752908723IN
Liquity V2 : Old Governance
0 ETH0.000200891.1
Deploy User Prox...229387922025-07-17 12:02:23152 days ago1752753743IN
Liquity V2 : Old Governance
0 ETH0.000225033.50790662
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x3d602d80229387922025-07-17 12:02:23152 days ago1752753743
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80225182102025-05-19 16:30:59211 days ago1747672259
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80225174882025-05-19 14:05:23211 days ago1747663523
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80224934542025-05-16 5:06:47214 days ago1747372007
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80224771042025-05-13 21:55:35216 days ago1747173335
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80224196962025-05-05 19:43:35224 days ago1746474215
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80224182362025-05-05 14:49:11225 days ago1746456551
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80224169172025-05-05 10:22:23225 days ago1746440543
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80224100492025-05-04 11:17:47226 days ago1746357467
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80223883412025-05-01 10:15:11229 days ago1746094511
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80223638822025-04-28 0:08:47232 days ago1745798927
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80223548232025-04-26 17:52:11233 days ago1745689931
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80223514652025-04-26 6:37:59234 days ago1745649479
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80223504082025-04-26 3:05:47234 days ago1745636747
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80223459362025-04-25 12:07:35235 days ago1745582855
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80223398452025-04-24 15:45:59236 days ago1745509559
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80222963022025-04-18 13:52:23242 days ago1744984343
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80222692132025-04-14 19:07:35245 days ago1744657655
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80222505552025-04-12 4:42:23248 days ago1744432943
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80222428752025-04-11 3:01:47249 days ago1744340507
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80222327652025-04-09 17:12:59250 days ago1744218779
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80222143152025-04-07 3:22:47253 days ago1743996167
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80221971532025-04-04 17:49:11255 days ago1743788951
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80221816672025-04-02 13:56:23258 days ago1743602183
Liquity V2 : Old Governance
 Contract Creation0 ETH
0x3d602d80221748322025-04-01 15:04:23259 days ago1743519863
Liquity V2 : Old Governance
 Contract Creation0 ETH
View All Internal Transactions
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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
File 1 of 23 : 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, UNREGISTERED_INITIATIVE} 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, sub, max} from "./utils/Math.sol";
import {_requireNoDuplicates, _requireNoNegatives} from "./utils/UniqueArray.sol";
import {MultiDelegateCall} from "./utils/MultiDelegateCall.sol";
import {WAD, PermitParams} from "./utils/Types.sol";
import {safeCallWithMinGas} from "./utils/SafeCallMinGas.sol";
import {Ownable} from "./utils/Ownable.sol";
import {_lqtyToVotes} from "./utils/VotingPower.sol";

/// @title Governance: Modular Initiative based Governance
contract Governance is MultiDelegateCall, 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 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 => uint256) public override registeredInitiatives;

    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;
        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;
        require(_config.epochStart <= block.timestamp, "Gov: cannot-start-in-future");
        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;

            bool success = safeCallWithMinGas(
                _initiatives[i], MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onRegisterInitiative, (1))
            );

            emit RegisterInitiative(_initiatives[i], msg.sender, 1, success ? HookStatus.Succeeded : HookStatus.Failed);
        }

        _renounceOwnership();
    }

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

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

        address userProxyAddress = deriveUserProxyAddress(msg.sender);

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

        UserProxy userProxy = UserProxy(payable(userProxyAddress));

        // update the vote power trackers
        userStates[msg.sender].unallocatedLQTY += _lqtyAmount;
        userStates[msg.sender].unallocatedOffset += block.timestamp * _lqtyAmount;

        return userProxy;
    }

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

    function depositLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) public nonReentrant {
        UserProxy userProxy = _increaseUserVoteTrackers(_lqtyAmount);

        (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent) =
            userProxy.stake(_lqtyAmount, msg.sender, _doSendRewards, _recipient);

        emit DepositLQTY(msg.sender, _recipient, _lqtyAmount, lusdReceived, lusdSent, ethReceived, ethSent);
    }

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

    function depositLQTYViaPermit(
        uint256 _lqtyAmount,
        PermitParams calldata _permitParams,
        bool _doSendRewards,
        address _recipient
    ) public nonReentrant {
        UserProxy userProxy = _increaseUserVoteTrackers(_lqtyAmount);

        (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent) =
            userProxy.stakeViaPermit(_lqtyAmount, msg.sender, _permitParams, _doSendRewards, _recipient);

        emit DepositLQTY(msg.sender, _recipient, _lqtyAmount, lusdReceived, lusdSent, ethReceived, ethSent);
    }

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

    function withdrawLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) public nonReentrant {
        UserState storage userState = userStates[msg.sender];

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

        // check if user has enough unallocated lqty
        require(_lqtyAmount <= userState.unallocatedLQTY, "Governance: insufficient-unallocated-lqty");

        // Update the offset tracker
        if (_lqtyAmount < userState.unallocatedLQTY) {
            // The offset decrease is proportional to the partial lqty decrease
            uint256 offsetDecrease = _lqtyAmount * userState.unallocatedOffset / userState.unallocatedLQTY;
            userState.unallocatedOffset -= offsetDecrease;
        } else {
            // if _lqtyAmount == userState.unallocatedLqty, zero the offset tracker
            userState.unallocatedOffset = 0;
        }

        // Update the user's LQTY tracker
        userState.unallocatedLQTY -= _lqtyAmount;

        (
            uint256 lqtyReceived,
            uint256 lqtySent,
            uint256 lusdReceived,
            uint256 lusdSent,
            uint256 ethReceived,
            uint256 ethSent
        ) = userProxy.unstake(_lqtyAmount, _doSendRewards, _recipient);

        emit WithdrawLQTY(msg.sender, _recipient, lqtyReceived, lqtySent, lusdReceived, lusdSent, ethReceived, ethSent);
    }

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

        uint256 lqtyReceived;
        uint256 lqtySent;
        uint256 lusdReceived;
        uint256 ethReceived;

        (lqtyReceived, lqtySent, lusdReceived, lusdSent, ethReceived, ethSent) =
            UserProxy(userProxyAddress).unstake(0, true, _rewardRecipient);

        emit WithdrawLQTY(
            msg.sender, _rewardRecipient, lqtyReceived, lqtySent, lusdReceived, lusdSent, ethReceived, ethSent
        );
    }

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

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

    /// @inheritdoc IGovernance
    function epochStart() public view returns (uint256) {
        return EPOCH_START + (epoch() - 1) * EPOCH_DURATION;
    }

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

    /// @inheritdoc IGovernance
    function lqtyToVotes(uint256 _lqtyAmount, uint256 _timestamp, uint256 _offset) public pure returns (uint256) {
        return _lqtyToVotes(_lqtyAmount, _timestamp, _offset);
    }

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

    /// @inheritdoc IGovernance
    function getLatestVotingThreshold() public view returns (uint256) {
        uint256 snapshotVotes = votesSnapshot.votes;

        return calculateVotingThreshold(snapshotVotes);
    }

    /// @inheritdoc IGovernance
    function calculateVotingThreshold() public returns (uint256) {
        (VoteSnapshot memory snapshot,) = _snapshotVotes();

        return calculateVotingThreshold(snapshot.votes);
    }

    /// @inheritdoc IGovernance
    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, boldAccrued);
        }
    }

    /// @inheritdoc IGovernance
    function getTotalVotesAndState()
        public
        view
        returns (VoteSnapshot memory snapshot, GlobalState memory state, bool shouldUpdate)
    {
        uint256 currentEpoch = epoch();
        snapshot = votesSnapshot;
        state = globalState;

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

            snapshot.votes = lqtyToVotes(state.countedVoteLQTY, epochStart(), state.countedVoteOffset);
            snapshot.forEpoch = currentEpoch - 1;
        }
    }

    // Snapshots votes for an initiative for the previous epoch
    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.vetos, initiativeSnapshot.forEpoch
            );
        }
    }

    /// @inheritdoc IGovernance
    function getInitiativeSnapshotAndState(address _initiative)
        public
        view
        returns (
            InitiativeVoteSnapshot memory initiativeSnapshot,
            InitiativeState memory initiativeState,
            bool shouldUpdate
        )
    {
        // Get the storage data
        uint256 currentEpoch = epoch();
        initiativeSnapshot = votesForInitiativeSnapshot[_initiative];
        initiativeState = initiativeStates[_initiative];

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

            uint256 start = epochStart();
            uint256 votes = lqtyToVotes(initiativeState.voteLQTY, start, initiativeState.voteOffset);
            uint256 vetos = lqtyToVotes(initiativeState.vetoLQTY, start, initiativeState.vetoOffset);
            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
    //////////////////////////////////////////////////////////////*/

    /// @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, uint256 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, uint256 lastEpochClaim, uint256 claimableAmount) {
        uint256 initiativeRegistrationEpoch = registeredInitiatives[_initiative];

        // == Non existent Condition == //
        if (initiativeRegistrationEpoch == 0) {
            return (InitiativeStatus.NONEXISTENT, 0, 0);
            /// By definition it has zero rewards
        }

        uint256 currentEpoch = epoch();

        // == Just Registered Condition == //
        if (initiativeRegistrationEpoch == currentEpoch) {
            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 (initiativeRegistrationEpoch == UNREGISTERED_INITIATIVE) {
            return (InitiativeStatus.DISABLED, lastEpochClaim, 0);
            /// By definition it has zero rewards
        }

        // == Already Claimed Condition == //
        if (lastEpochClaim >= currentEpoch - 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
        if (
            _votesForInitiativeSnapshot.votes > votingTheshold
                && _votesForInitiativeSnapshot.votes > _votesForInitiativeSnapshot.vetos
        ) {
            uint256 claim = _votesForInitiativeSnapshot.votes * boldAccrued / _votesSnapshot.votes;

            return (InitiativeStatus.CLAIMABLE, lastEpochClaim, claim);
        }

        // == Unregister Condition == //
        // e.g. if `UNREGISTRATION_AFTER_EPOCHS` is 4, the initiative will become unregisterable after spending 4 epochs
        // while being in one of the following conditions:
        //  - in `SKIP` state (not having received enough votes to cross the voting threshold)
        //  - in `CLAIMABLE` state (having received enough votes to cross the voting threshold) but never being claimed
        if (
            (_initiativeState.lastEpochClaim + UNREGISTRATION_AFTER_EPOCHS < currentEpoch - 1)
                || _votesForInitiativeSnapshot.vetos > _votesForInitiativeSnapshot.votes
                    && _votesForInitiativeSnapshot.vetos > 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 {
        uint256 currentEpoch = epoch();
        require(currentEpoch > 2, "Governance: registration-not-yet-enabled");

        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];

        bold.safeTransferFrom(msg.sender, address(this), REGISTRATION_FEE);

        // 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 = snapshot.votes;

        uint256 totalUserOffset = userState.allocatedOffset + userState.unallocatedOffset;
        require(
            // Check against the user's total voting power, so include both allocated and unallocated LQTY
            lqtyToVotes(stakingV1.stakes(userProxyAddress), epochStart(), totalUserOffset)
                >= upscaledSnapshotVotes * REGISTRATION_THRESHOLD_FACTOR / WAD,
            "Governance: insufficient-lqty"
        );

        registeredInitiatives[_initiative] = currentEpoch;

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

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

        emit RegisterInitiative(
            _initiative, msg.sender, currentEpoch, success ? HookStatus.Succeeded : HookStatus.Failed
        );
    }

    struct ResetInitiativeData {
        address initiative;
        int256 LQTYVotes;
        int256 LQTYVetos;
        int256 OffsetVotes;
        int256 OffsetVetos;
    }

    /// @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);

        int256[] memory deltaLQTYVotes = new int256[](_initiativesToReset.length);
        int256[] memory deltaLQTYVetos = new int256[](_initiativesToReset.length);
        int256[] memory deltaOffsetVotes = new int256[](_initiativesToReset.length);
        int256[] memory deltaOffsetVetos = new int256[](_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");

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

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

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

        return cachedData;
    }

    /// @inheritdoc IGovernance
    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,
        int256[] calldata _absoluteLQTYVotes,
        int256[] calldata _absoluteLQTYVetos
    ) external nonReentrant {
        require(
            _initiatives.length == _absoluteLQTYVotes.length && _absoluteLQTYVotes.length == _absoluteLQTYVetos.length,
            "Governance: array-length-mismatch"
        );

        // 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);
        _requireNoSimultaneousVoteAndVeto(_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");
        require(userState.unallocatedLQTY != 0, "Governance: insufficient-or-allocated-lqty"); // avoid div-by-zero

        // 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");
                }
            }
        }

        int256[] memory absoluteOffsetVotes = new int256[](_initiatives.length);
        int256[] memory absoluteOffsetVetos = new int256[](_initiatives.length);

        // Calculate the offset portions that correspond to each LQTY vote and veto portion
        // By recalculating `unallocatedLQTY` & `unallocatedOffset` after each step, we ensure that rounding error
        // doesn't accumulate in `unallocatedOffset`.
        // However, it should be noted that this makes the exact offset allocations dependent on the ordering of the
        // `_initiatives` array.
        for (uint256 x; x < _initiatives.length; x++) {
            // Either _absoluteLQTYVotes[x] or _absoluteLQTYVetos[x] is guaranteed to be zero
            (int256[] calldata lqtyAmounts, int256[] memory offsets) = _absoluteLQTYVotes[x] > 0
                ? (_absoluteLQTYVotes, absoluteOffsetVotes)
                : (_absoluteLQTYVetos, absoluteOffsetVetos);

            uint256 lqtyAmount = uint256(lqtyAmounts[x]);
            uint256 offset = userState.unallocatedOffset * lqtyAmount / userState.unallocatedLQTY;

            userState.unallocatedLQTY -= lqtyAmount;
            userState.unallocatedOffset -= offset;

            offsets[x] = int256(offset);
        }

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

    // Avoid "stack too deep" by placing these variables in memory
    struct AllocateLQTYMemory {
        VoteSnapshot votesSnapshot_;
        GlobalState state;
        UserState userState;
        InitiativeVoteSnapshot votesForInitiativeSnapshot_;
        InitiativeState initiativeState;
        InitiativeState prevInitiativeState;
        Allocation allocation;
        uint256 currentEpoch;
        int256 deltaLQTYVotes;
        int256 deltaLQTYVetos;
        int256 deltaOffsetVotes;
        int256 deltaOffsetVetos;
    }

    /// @dev For each given initiative applies relative changes to the allocation
    /// @dev Assumes that all the input arrays are of equal length
    /// @dev 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,
        int256[] memory _deltaLQTYVotes,
        int256[] memory _deltaLQTYVetos,
        int256[] memory _deltaOffsetVotes,
        int256[] memory _deltaOffsetVetos
    ) internal {
        AllocateLQTYMemory memory vars;
        (vars.votesSnapshot_, vars.state) = _snapshotVotes();
        vars.currentEpoch = epoch();
        vars.userState = userStates[msg.sender];

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

            vars.deltaOffsetVotes = _deltaOffsetVotes[i];
            vars.deltaOffsetVetos = _deltaOffsetVetos[i];

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

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

            if (vars.deltaLQTYVotes > 0 || vars.deltaLQTYVetos > 0) {
                /// 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(vars.deltaLQTYVotes <= 0 && vars.deltaLQTYVetos <= 0, "Must be a withdrawal");
            }

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

            // deep copy of the initiative's state before the allocation
            vars.prevInitiativeState = InitiativeState(
                vars.initiativeState.voteLQTY,
                vars.initiativeState.voteOffset,
                vars.initiativeState.vetoLQTY,
                vars.initiativeState.vetoOffset,
                vars.initiativeState.lastEpochClaim
            );

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

            // Update the initiative's vote and veto offsets
            vars.initiativeState.voteOffset = add(vars.initiativeState.voteOffset, vars.deltaOffsetVotes);
            vars.initiativeState.vetoOffset = add(vars.initiativeState.vetoOffset, vars.deltaOffsetVetos);

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

            // == GLOBAL STATE == //

            /// We update the state only for non-disabled initiatives
            /// Disabled initiatves have had their totals subtracted already
            if (status != InitiativeStatus.DISABLED) {
                assert(vars.state.countedVoteLQTY >= vars.prevInitiativeState.voteLQTY);

                // Remove old initative LQTY and offset from global count
                vars.state.countedVoteLQTY -= vars.prevInitiativeState.voteLQTY;
                vars.state.countedVoteOffset -= vars.prevInitiativeState.voteOffset;

                // Add new initative LQTY and offset to global count
                vars.state.countedVoteLQTY += vars.initiativeState.voteLQTY;
                vars.state.countedVoteOffset += vars.initiativeState.voteOffset;
            }

            // == USER ALLOCATION TO INITIATIVE == //

            // Record the vote and veto LQTY and offsets by user to initative
            vars.allocation = lqtyAllocatedByUserToInitiative[msg.sender][initiative];
            // Update offsets
            vars.allocation.voteOffset = add(vars.allocation.voteOffset, vars.deltaOffsetVotes);
            vars.allocation.vetoOffset = add(vars.allocation.vetoOffset, vars.deltaOffsetVetos);

            // Update votes and vetos
            vars.allocation.voteLQTY = add(vars.allocation.voteLQTY, vars.deltaLQTYVotes);
            vars.allocation.vetoLQTY = add(vars.allocation.vetoLQTY, vars.deltaLQTYVetos);

            vars.allocation.atEpoch = vars.currentEpoch;

            // Voting power allocated to initiatives should never be negative, else it might break reward allocation
            // schemes such as `BribeInitiative` which distribute rewards in proportion to voting power allocated.
            assert(vars.allocation.voteLQTY * block.timestamp >= vars.allocation.voteOffset);
            assert(vars.allocation.vetoLQTY * block.timestamp >= vars.allocation.vetoOffset);

            lqtyAllocatedByUserToInitiative[msg.sender][initiative] = vars.allocation;

            // == USER STATE == //

            // Remove from the user's unallocated LQTY and offset
            vars.userState.unallocatedLQTY =
                sub(vars.userState.unallocatedLQTY, (vars.deltaLQTYVotes + vars.deltaLQTYVetos));
            vars.userState.unallocatedOffset =
                sub(vars.userState.unallocatedOffset, (vars.deltaOffsetVotes + vars.deltaOffsetVetos));

            // Add to the user's allocated LQTY and offset
            vars.userState.allocatedLQTY =
                add(vars.userState.allocatedLQTY, (vars.deltaLQTYVotes + vars.deltaLQTYVetos));
            vars.userState.allocatedOffset =
                add(vars.userState.allocatedOffset, (vars.deltaOffsetVotes + vars.deltaOffsetVetos));

            HookStatus hookStatus;

            // See https://github.com/liquity/V2-gov/issues/125
            // A malicious initiative could try to dissuade voters from casting vetos by consuming as much gas as
            // possible in the `onAfterAllocateLQTY` hook when detecting vetos.
            // We deem that the risks of calling into malicous initiatives upon veto allocation far outweigh the
            // benefits of notifying benevolent initiatives of vetos.
            if (vars.allocation.vetoLQTY == 0) {
                // Replaces try / catch | Enforces sufficient gas is passed
                hookStatus = safeCallWithMinGas(
                    initiative,
                    MIN_GAS_TO_HOOK,
                    0,
                    abi.encodeCall(
                        IInitiative.onAfterAllocateLQTY,
                        (vars.currentEpoch, msg.sender, vars.userState, vars.allocation, vars.initiativeState)
                    )
                ) ? HookStatus.Succeeded : HookStatus.Failed;
            } else {
                hookStatus = HookStatus.NotCalled;
            }

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

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

        globalState = vars.state;
        userStates[msg.sender] = vars.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.UNREGISTERABLE, "Governance: cannot-unregister-initiative");

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

        // NOTE: Safe to remove | See `check_claim_soundness`
        assert(initiativeState.lastEpochClaim < currentEpoch - 1);

        assert(state.countedVoteLQTY >= initiativeState.voteLQTY);
        assert(state.countedVoteOffset >= initiativeState.voteOffset);

        state.countedVoteLQTY -= initiativeState.voteLQTY;
        state.countedVoteOffset -= initiativeState.voteOffset;

        globalState = state;

        /// Epoch will never reach 2^256 - 1
        registeredInitiatives[_initiative] = UNREGISTERED_INITIATIVE;

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

        emit UnregisterInitiative(_initiative, currentEpoch, success ? HookStatus.Succeeded : HookStatus.Failed);
    }

    /// @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;
        }

        /// 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;

        /// 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);

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

        emit ClaimForInitiative(
            _initiative, claimableAmount, votesSnapshot_.forEpoch, success ? HookStatus.Succeeded : HookStatus.Failed
        );

        return claimableAmount;
    }

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

    function _requireNoSimultaneousVoteAndVeto(int256[] memory _absoluteLQTYVotes, int256[] memory _absoluteLQTYVetos)
        internal
        pure
    {
        for (uint256 i; i < _absoluteLQTYVotes.length; i++) {
            require(_absoluteLQTYVotes[i] == 0 || _absoluteLQTYVetos[i] == 0, "Governance: vote-and-veto");
        }
    }
}

File 2 of 23 : 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";

// 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;
    }
}

// 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 23 : 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";

uint256 constant UNREGISTERED_INITIATIVE = type(uint256).max;

interface IGovernance {
    enum HookStatus {
        Failed,
        Succeeded,
        NotCalled
    }

    /// @notice Emitted when a user deposits LQTY
    /// @param user The account depositing LQTY
    /// @param rewardRecipient The account receiving the LUSD/ETH rewards earned from staking in V1, if claimed
    /// @param lqtyAmount The amount of LQTY being deposited
    /// @return lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
    /// @return lusdSent Amount of LUSD tokens sent to `rewardRecipient` (may include previously received LUSD)
    /// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
    /// @return ethSent Amount of ETH sent to `rewardRecipient` (may include previously received ETH)
    event DepositLQTY(
        address indexed user,
        address rewardRecipient,
        uint256 lqtyAmount,
        uint256 lusdReceived,
        uint256 lusdSent,
        uint256 ethReceived,
        uint256 ethSent
    );

    /// @notice Emitted when a user withdraws LQTY or claims V1 staking rewards
    /// @param user The account withdrawing LQTY or claiming V1 staking rewards
    /// @param recipient The account receiving the LQTY withdrawn, and if claimed, the LUSD/ETH rewards earned from staking in V1
    /// @return lqtyReceived Amount of LQTY tokens actually withdrawn (may be lower than the `_lqtyAmount` passed to `withdrawLQTY`)
    /// @return lqtySent Amount of LQTY tokens sent to `recipient` (may include LQTY sent to the user's proxy from sources other than V1 staking)
    /// @return lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
    /// @return lusdSent Amount of LUSD tokens sent to `recipient` (may include previously received LUSD)
    /// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
    /// @return ethSent Amount of ETH sent to `recipient` (may include previously received ETH)
    event WithdrawLQTY(
        address indexed user,
        address recipient,
        uint256 lqtyReceived,
        uint256 lqtySent,
        uint256 lusdReceived,
        uint256 lusdSent,
        uint256 ethReceived,
        uint256 ethSent
    );

    event SnapshotVotes(uint256 votes, uint256 forEpoch, uint256 boldAccrued);
    event SnapshotVotesForInitiative(address indexed initiative, uint256 votes, uint256 vetos, uint256 forEpoch);

    event RegisterInitiative(address initiative, address registrant, uint256 atEpoch, HookStatus hookStatus);
    event UnregisterInitiative(address initiative, uint256 atEpoch, HookStatus hookStatus);

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

    struct Configuration {
        uint256 registrationFee;
        uint256 registrationThresholdFactor;
        uint256 unregistrationThresholdFactor;
        uint256 unregistrationAfterEpochs;
        uint256 votingThresholdFactor;
        uint256 minClaim;
        uint256 minAccrual;
        uint256 epochStart;
        uint256 epochDuration;
        uint256 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 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 {
        uint256 votes; // Votes at epoch transition
        uint256 forEpoch; // Epoch for which the votes are counted
    }

    struct InitiativeVoteSnapshot {
        uint256 votes; // Votes at epoch transition
        uint256 forEpoch; // Epoch for which the votes are counted
        uint256 lastCountedEpoch; // Epoch at which which the votes where counted last in the global snapshot
        uint256 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 (uint256 votes, uint256 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 (uint256 votes, uint256 forEpoch, uint256 lastCountedEpoch, uint256 vetos);

    struct Allocation {
        uint256 voteLQTY; // LQTY allocated vouching for the initiative
        uint256 voteOffset; // Offset associated with LQTY vouching for the initiative
        uint256 vetoLQTY; // LQTY vetoing the initiative
        uint256 vetoOffset; // Offset associated with LQTY vetoing the initiative
        uint256 atEpoch; // Epoch at which the allocation was last updated
    }

    struct UserState {
        uint256 unallocatedLQTY; // LQTY deposited and unallocated
        uint256 unallocatedOffset; // The offset sum corresponding to the unallocated LQTY
        uint256 allocatedLQTY; // LQTY allocated by the user to initatives
        uint256 allocatedOffset; // The offset sum corresponding to the allocated LQTY
    }

    struct InitiativeState {
        uint256 voteLQTY; // LQTY allocated vouching for the initiative
        uint256 voteOffset; // Offset associated with LQTY vouching for to the initative
        uint256 vetoLQTY; // LQTY allocated vetoing the initiative
        uint256 vetoOffset; // Offset associated with LQTY veoting the initative
        uint256 lastEpochClaim;
    }

    struct GlobalState {
        uint256 countedVoteLQTY; // Total LQTY that is included in vote counting
        uint256 countedVoteOffset; // Offset associated with the counted vote LQTY
    }

    /// @notice Returns the user's state
    /// @return unallocatedLQTY LQTY deposited and unallocated
    /// @return unallocatedOffset Offset associated with unallocated LQTY
    /// @return allocatedLQTY allocated by the user to initatives
    /// @return allocatedOffset Offset associated with allocated LQTY
    function userStates(address _user)
        external
        view
        returns (uint256 unallocatedLQTY, uint256 unallocatedOffset, uint256 allocatedLQTY, uint256 allocatedOffset);
    /// @notice Returns the initiative's state
    /// @param _initiative Address of the initiative
    /// @return voteLQTY LQTY allocated vouching for the initiative
    /// @return voteOffset Offset associated with voteLQTY
    /// @return vetoLQTY LQTY allocated vetoing the initiative
    /// @return vetoOffset Offset associated with vetoLQTY
    /// @return lastEpochClaim // Last epoch at which rewards were claimed
    function initiativeStates(address _initiative)
        external
        view
        returns (uint256 voteLQTY, uint256 voteOffset, uint256 vetoLQTY, uint256 vetoOffset, uint256 lastEpochClaim);
    /// @notice Returns the global state
    /// @return countedVoteLQTY Total LQTY that is included in vote counting
    /// @return countedVoteOffset Offset associated with countedVoteLQTY
    function globalState() external view returns (uint256 countedVoteLQTY, uint256 countedVoteOffset);
    /// @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 voteOffset The offset associated with voteLQTY
    /// @return vetoLQTY allocated vetoing the initiative
    /// @return vetoOffset the offset associated with vetoLQTY
    /// @return atEpoch Epoch at which the allocation was last updated
    function lqtyAllocatedByUserToInitiative(address _user, address _initiative)
        external
        view
        returns (uint256 voteLQTY, uint256 voteOffset, uint256 vetoLQTY, uint256 vetoOffset, uint256 atEpoch);

    /// @notice Returns when an initiative was registered
    /// @param _initiative Address of the initiative
    /// @return atEpoch If `_initiative` is an active initiative, returns the epoch at which it was registered.
    ///                 If `_initiative` hasn't been registered, returns 0.
    ///                 If `_initiative` has been unregistered, returns `UNREGISTERED_INITIATIVE`.
    function registeredInitiatives(address _initiative) external view returns (uint256 atEpoch);

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

    /// @notice Deposits LQTY
    /// @dev The caller has to approve their `UserProxy` address to spend the LQTY tokens
    /// @param _lqtyAmount Amount of LQTY to deposit
    function depositLQTY(uint256 _lqtyAmount) external;

    /// @notice Deposits LQTY
    /// @dev The caller has to approve their `UserProxy` address to spend the LQTY tokens
    /// @param _lqtyAmount Amount of LQTY to deposit
    /// @param _doSendRewards If true, send rewards claimed from LQTY staking
    /// @param _recipient Address to which the tokens should be sent
    function depositLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) external;

    /// @notice Deposits LQTY via Permit
    /// @param _lqtyAmount Amount of LQTY to deposit
    /// @param _permitParams Permit parameters
    function depositLQTYViaPermit(uint256 _lqtyAmount, PermitParams calldata _permitParams) external;

    /// @notice Deposits LQTY via Permit
    /// @param _lqtyAmount Amount of LQTY to deposit
    /// @param _permitParams Permit parameters
    /// @param _doSendRewards If true, send rewards claimed from LQTY staking
    /// @param _recipient Address to which the tokens should be sent
    function depositLQTYViaPermit(
        uint256 _lqtyAmount,
        PermitParams calldata _permitParams,
        bool _doSendRewards,
        address _recipient
    ) external;

    /// @notice Withdraws LQTY and claims any accrued LUSD and ETH rewards from StakingV1
    /// @param _lqtyAmount Amount of LQTY to withdraw
    function withdrawLQTY(uint256 _lqtyAmount) external;

    /// @notice Withdraws LQTY and claims any accrued LUSD and ETH rewards from StakingV1
    /// @param _lqtyAmount Amount of LQTY to withdraw
    /// @param _doSendRewards If true, send rewards claimed from LQTY staking
    /// @param _recipient Address to which the tokens should be sent
    function withdrawLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) external;

    /// @notice Claims staking rewards from StakingV1 without unstaking
    /// @dev Note: in the unlikely event that the caller's `UserProxy` holds any LQTY tokens, they will also be sent to `_rewardRecipient`
    /// @param _rewardRecipient Address that will receive the rewards
    /// @return lusdSent Amount of LUSD tokens sent to `_rewardRecipient` (may include previously received LUSD)
    /// @return ethSent Amount of ETH sent to `_rewardRecipient` (may include previously received ETH)
    function claimFromStakingV1(address _rewardRecipient) external returns (uint256 lusdSent, uint256 ethSent);

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

    /// @notice Returns the current epoch number
    /// @return epoch Current epoch
    function epoch() external view returns (uint256 epoch);
    /// @notice Returns the timestamp at which the current epoch started
    /// @return epochStart Epoch start of the current epoch
    function epochStart() external view returns (uint256 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 (uint256 secondsWithinEpoch);

    /// @notice Returns the voting power for an entity (i.e. user or initiative) at a given timestamp
    /// @param _lqtyAmount Amount of LQTY associated with the entity
    /// @param _timestamp Timestamp at which to calculate voting power
    /// @param _offset The entity's offset sum
    /// @return votes Number of votes
    function lqtyToVotes(uint256 _lqtyAmount, uint256 _timestamp, uint256 _offset) external pure returns (uint256);

    /// @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() external returns (uint256);

    /// @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) external view returns (uint256);

    /// @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()
        external
        view
        returns (VoteSnapshot memory snapshot, GlobalState memory state, bool shouldUpdate);

    /// @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)
        external
        view
        returns (
            InitiativeVoteSnapshot memory initiativeSnapshot,
            InitiativeState memory initiativeState,
            bool shouldUpdate
        );

    /// @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);

    /*//////////////////////////////////////////////////////////////
                                 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

    }

    function getInitiativeState(address _initiative)
        external
        returns (InitiativeStatus status, uint256 lastEpochClaim, uint256 claimableAmount);

    function getInitiativeState(
        address _initiative,
        VoteSnapshot memory _votesSnapshot,
        InitiativeVoteSnapshot memory _votesForInitiativeSnapshot,
        InitiativeState memory _initiativeState
    ) external view returns (InitiativeStatus status, uint256 lastEpochClaim, uint256 claimableAmount);

    /// @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 _initiativesToReset 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 LQTY to allocate to the initiatives as votes
    /// @param _absoluteLQTYVetos LQTY to allocate to the initiatives as vetos
    function allocateLQTY(
        address[] calldata _initiativesToReset,
        address[] memory _initiatives,
        int256[] memory _absoluteLQTYVotes,
        int256[] memory _absoluteLQTYVetos
    ) external;
    /// @notice Deallocates the user's LQTY from initiatives
    /// @param _initiativesToReset Addresses of initiatives to deallocate LQTY from
    /// @param _checkAll When true, the call will revert if there is still some allocated LQTY left after deallocating
    ///                  from all the addresses in `_initiativesToReset`
    function resetAllocations(address[] calldata _initiativesToReset, bool _checkAll) 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);
}

// 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(uint256 _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(uint256 _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(
        uint256 _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(uint256 _claimEpoch, uint256 _bold) external;
}

// 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);
}

// 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 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent)
    {
        uint256 initialLUSDAmount = lusd.balanceOf(address(this));
        uint256 initialETHAmount = address(this).balance;

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

        uint256 lusdAmount = lusd.balanceOf(address(this));
        uint256 ethAmount = address(this).balance;

        lusdReceived = lusdAmount - initialLUSDAmount;
        ethReceived = ethAmount - initialETHAmount;

        if (_doSendRewards) (lusdSent, ethSent) = _sendRewards(_recipient, lusdAmount, ethAmount);
    }

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

        try IERC20Permit(address(lqty)).permit(
            _permitParams.owner,
            _permitParams.spender,
            _permitParams.value,
            _permitParams.deadline,
            _permitParams.v,
            _permitParams.r,
            _permitParams.s
        ) {} catch {}

        return stake(_amount, _lqtyFrom, _doSendRewards, _recipient);
    }

    /// @inheritdoc IUserProxy
    function unstake(uint256 _amount, bool _doSendRewards, address _recipient)
        external
        onlyStakingV2
        returns (
            uint256 lqtyReceived,
            uint256 lqtySent,
            uint256 lusdReceived,
            uint256 lusdSent,
            uint256 ethReceived,
            uint256 ethSent
        )
    {
        uint256 initialLQTYAmount = lqty.balanceOf(address(this));
        uint256 initialLUSDAmount = lusd.balanceOf(address(this));
        uint256 initialETHAmount = address(this).balance;

        stakingV1.unstake(_amount);

        lqtySent = lqty.balanceOf(address(this));
        uint256 lusdAmount = lusd.balanceOf(address(this));
        uint256 ethAmount = address(this).balance;

        lqtyReceived = lqtySent - initialLQTYAmount;
        lusdReceived = lusdAmount - initialLUSDAmount;
        ethReceived = ethAmount - initialETHAmount;

        if (lqtySent > 0) lqty.transfer(_recipient, lqtySent);
        if (_doSendRewards) (lusdSent, ethSent) = _sendRewards(_recipient, lusdAmount, ethAmount);
    }

    function _sendRewards(address _recipient, uint256 _lusdAmount, uint256 _ethAmount)
        internal
        returns (uint256 lusdSent, uint256 ethSent)
    {
        if (_lusdAmount > 0) lusd.transfer(_recipient, _lusdAmount);
        if (_ethAmount > 0) {
            (bool success,) = payable(_recipient).call{value: _ethAmount}("");
            require(success, "UserProxy: eth-fail");
        }

        return (_lusdAmount, _ethAmount);
    }

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

    receive() external payable {}
}

// 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;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

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

function sub(uint256 a, int256 b) pure returns (uint256) {
    if (b < 0) {
        return a + abs(b);
    }
    return a - uint256(b);
}

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

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

File 11 of 23 : 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(int256[] memory vals) pure {
    uint256 arrLength = vals.length;

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

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

contract MultiDelegateCall is IMultiDelegateCall {
    /// @inheritdoc IMultiDelegateCall
    function multiDelegateCall(bytes[] calldata inputs) external returns (bytes[] memory returnValues) {
        returnValues = new bytes[](inputs.length);

        for (uint256 i; i < inputs.length; ++i) {
            (bool success, bytes memory returnData) = address(this).delegatecall(inputs[i]);

            if (!success) {
                // Bubble up the revert
                assembly {
                    revert(
                        add(32, returnData), // offset (skip first 32 bytes, where the size of the array is stored)
                        mload(returnData) // size
                    )
                }
            }

            returnValues[i] = returnData;
        }
    }
}

File 13 of 23 : 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 23 : 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)
{
    /// 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);
}

// 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 23 : VotingPower.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

function _lqtyToVotes(uint256 _lqtyAmount, uint256 _timestamp, uint256 _offset) pure returns (uint256) {
    uint256 prod = _lqtyAmount * _timestamp;
    return prod > _offset ? prod - _offset : 0;
}

// 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);
}

// 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);
}

// 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();
        }
    }
}

// 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 {
    /// @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 lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
    /// @return lusdSent Amount of LUSD tokens sent to `_recipient` (may include previously received LUSD)
    /// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
    /// @return ethSent Amount of ETH sent to `_recipient` (may include previously received ETH)
    function stake(uint256 _amount, address _lqtyFrom, bool _doSendRewards, address _recipient)
        external
        returns (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent);

    /// @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 lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
    /// @return lusdSent Amount of LUSD tokens sent to `_recipient` (may include previously received LUSD)
    /// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
    /// @return ethSent Amount of ETH sent to `_recipient` (may include previously received ETH)
    function stakeViaPermit(
        uint256 _amount,
        address _lqtyFrom,
        PermitParams calldata _permitParams,
        bool _doSendRewards,
        address _recipient
    ) external returns (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent);

    /// @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 lqtyReceived Amount of LQTY tokens actually unstaked (may be lower than `_amount`)
    /// @return lqtySent Amount of LQTY tokens sent to `_recipient` (may include LQTY sent to the proxy from sources other than V1 staking)
    /// @return lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
    /// @return lusdSent Amount of LUSD tokens claimed (may include previously received LUSD)
    /// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
    /// @return ethSent Amount of ETH claimed (may include previously received ETH)
    function unstake(uint256 _amount, bool _doSendRewards, address _recipient)
        external
        returns (
            uint256 lqtyReceived,
            uint256 lqtySent,
            uint256 lusdReceived,
            uint256 lusdSent,
            uint256 ethReceived,
            uint256 ethSent
        );

    /// @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 (uint256);
}

// 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));
    }
}

// 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);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IMultiDelegateCall {
    /// @notice Call multiple functions of the contract while preserving `msg.sender`
    /// @param inputs Function calls to perform, encoded using `abi.encodeCall()` or equivalent
    /// @return returnValues Raw data returned by each call
    function multiDelegateCall(bytes[] calldata inputs) external returns (bytes[] memory returnValues);
}

Settings
{
  "remappings": [
    "openzeppelin/=lib/V2-gov/lib/openzeppelin-contracts/",
    "@chimera/=lib/V2-gov/lib/chimera/src/",
    "@openzeppelin/contracts/=lib/V2-gov/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-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "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 Security Audit

Contract ABI

API
[{"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":"uint256","name":"registrationFee","type":"uint256"},{"internalType":"uint256","name":"registrationThresholdFactor","type":"uint256"},{"internalType":"uint256","name":"unregistrationThresholdFactor","type":"uint256"},{"internalType":"uint256","name":"unregistrationAfterEpochs","type":"uint256"},{"internalType":"uint256","name":"votingThresholdFactor","type":"uint256"},{"internalType":"uint256","name":"minClaim","type":"uint256"},{"internalType":"uint256","name":"minAccrual","type":"uint256"},{"internalType":"uint256","name":"epochStart","type":"uint256"},{"internalType":"uint256","name":"epochDuration","type":"uint256"},{"internalType":"uint256","name":"epochVotingCutoff","type":"uint256"}],"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":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"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":"uint256","name":"atEpoch","type":"uint256"},{"indexed":false,"internalType":"enum IGovernance.HookStatus","name":"hookStatus","type":"uint8"}],"name":"AllocateLQTY","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint256","name":"bold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forEpoch","type":"uint256"},{"indexed":false,"internalType":"enum IGovernance.HookStatus","name":"hookStatus","type":"uint8"}],"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":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"rewardRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"lqtyAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lusdReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lusdSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethSent","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":"uint256","name":"atEpoch","type":"uint256"},{"indexed":false,"internalType":"enum IGovernance.HookStatus","name":"hookStatus","type":"uint8"}],"name":"RegisterInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"votes","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"boldAccrued","type":"uint256"}],"name":"SnapshotVotes","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint256","name":"votes","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vetos","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forEpoch","type":"uint256"}],"name":"SnapshotVotesForInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint256","name":"atEpoch","type":"uint256"},{"indexed":false,"internalType":"enum IGovernance.HookStatus","name":"hookStatus","type":"uint8"}],"name":"UnregisterInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"lqtyReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lqtySent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lusdReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lusdSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethSent","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":"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":"int256[]","name":"_absoluteLQTYVotes","type":"int256[]"},{"internalType":"int256[]","name":"_absoluteLQTYVetos","type":"int256[]"}],"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":"lusdSent","type":"uint256"},{"internalType":"uint256","name":"ethSent","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deployUserProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"}],"name":"depositLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"depositLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"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":"uint256","name":"_lqtyAmount","type":"uint256"},{"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":"address","name":"_user","type":"address"}],"name":"deriveUserProxyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"getInitiativeSnapshotAndState","outputs":[{"components":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"},{"internalType":"uint256","name":"lastCountedEpoch","type":"uint256"},{"internalType":"uint256","name":"vetos","type":"uint256"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"initiativeSnapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"voteLQTY","type":"uint256"},{"internalType":"uint256","name":"voteOffset","type":"uint256"},{"internalType":"uint256","name":"vetoLQTY","type":"uint256"},{"internalType":"uint256","name":"vetoOffset","type":"uint256"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"}],"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":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"}],"internalType":"struct IGovernance.VoteSnapshot","name":"_votesSnapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"},{"internalType":"uint256","name":"lastCountedEpoch","type":"uint256"},{"internalType":"uint256","name":"vetos","type":"uint256"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"_votesForInitiativeSnapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"voteLQTY","type":"uint256"},{"internalType":"uint256","name":"voteOffset","type":"uint256"},{"internalType":"uint256","name":"vetoLQTY","type":"uint256"},{"internalType":"uint256","name":"vetoOffset","type":"uint256"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"}],"internalType":"struct IGovernance.InitiativeState","name":"_initiativeState","type":"tuple"}],"name":"getInitiativeState","outputs":[{"internalType":"enum IGovernance.InitiativeStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"getInitiativeState","outputs":[{"internalType":"enum IGovernance.InitiativeStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"},{"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":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"}],"internalType":"struct IGovernance.VoteSnapshot","name":"snapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"countedVoteLQTY","type":"uint256"},{"internalType":"uint256","name":"countedVoteOffset","type":"uint256"}],"internalType":"struct IGovernance.GlobalState","name":"state","type":"tuple"},{"internalType":"bool","name":"shouldUpdate","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalState","outputs":[{"internalType":"uint256","name":"countedVoteLQTY","type":"uint256"},{"internalType":"uint256","name":"countedVoteOffset","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initiativeStates","outputs":[{"internalType":"uint256","name":"voteLQTY","type":"uint256"},{"internalType":"uint256","name":"voteOffset","type":"uint256"},{"internalType":"uint256","name":"vetoLQTY","type":"uint256"},{"internalType":"uint256","name":"vetoOffset","type":"uint256"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"}],"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":"uint256","name":"voteLQTY","type":"uint256"},{"internalType":"uint256","name":"voteOffset","type":"uint256"},{"internalType":"uint256","name":"vetoLQTY","type":"uint256"},{"internalType":"uint256","name":"vetoOffset","type":"uint256"},{"internalType":"uint256","name":"atEpoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"},{"internalType":"uint256","name":"_offset","type":"uint256"}],"name":"lqtyToVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"multiDelegateCall","outputs":[{"internalType":"bytes[]","name":"returnValues","type":"bytes[]"}],"stateMutability":"nonpayable","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":"uint256","name":"","type":"uint256"}],"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":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"snapshotVotesForInitiative","outputs":[{"components":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"}],"internalType":"struct IGovernance.VoteSnapshot","name":"voteSnapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"},{"internalType":"uint256","name":"lastCountedEpoch","type":"uint256"},{"internalType":"uint256","name":"vetos","type":"uint256"}],"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":"uint256","name":"unallocatedLQTY","type":"uint256"},{"internalType":"uint256","name":"unallocatedOffset","type":"uint256"},{"internalType":"uint256","name":"allocatedLQTY","type":"uint256"},{"internalType":"uint256","name":"allocatedOffset","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votesForInitiativeSnapshot","outputs":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"},{"internalType":"uint256","name":"lastCountedEpoch","type":"uint256"},{"internalType":"uint256","name":"vetos","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votesSnapshot","outputs":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"}],"name":"withdrawLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61024060405234801562000011575f80fd5b506040516200655538038062006555833981016040819052620000349162000756565b8187878782828260405162000049906200063f565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103905ff08015801562000083573d5f803e3d5ffd5b506001600160a01b0390811660805260015f81815581546001600160a01b03191692871692831790915560405191945092505f805160206200653583398151915291508290a3506001600160a01b0380861660a090815288821660c090815291861660e05290840151908401511115620001445760405162461bcd60e51b815260206004820152601d60248201527f476f763a206d696e2d636c61696d2d67742d6d696e2d6163637275616c00000060448201526064015b60405180910390fd5b82516101a0526020830151670de0b6b3a764000011620001a75760405162461bcd60e51b815260206004820152601860248201527f476f763a20726567697374726174696f6e2d636f6e666967000000000000000060448201526064016200013b565b60208301516101c0526040830151670de0b6b3a7640000106200020d5760405162461bcd60e51b815260206004820152601a60248201527f476f763a20756e726567697374726174696f6e2d636f6e66696700000000000060448201526064016200013b565b60408301516101e0526060830151610200526080830151670de0b6b3a764000011620002715760405162461bcd60e51b8152602060048201526012602482015271476f763a20766f74696e672d636f6e66696760701b60448201526064016200013b565b60808301516102205260a08301516101605260c08301516101805260e0830151421015620002e25760405162461bcd60e51b815260206004820152601b60248201527f476f763a2063616e6e6f742d73746172742d696e2d667574757265000000000060448201526064016200013b565b60e08301516101009081528301516200033e5760405162461bcd60e51b815260206004820152601860248201527f476f763a2065706f63682d6475726174696f6e2d7a65726f000000000000000060448201526064016200013b565b6101008301805161012090815290519084015110620003b35760405162461bcd60e51b815260206004820152602a60248201527f476f763a2065706f63682d766f74696e672d6375746f66662d67742d65706f636044820152693416b23ab930ba34b7b760b11b60648201526084016200013b565b61012083015161014052805115620003d057620003d081620003dd565b50505050505050620008e0565b620003f26001546001600160a01b0316331490565b620004405760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016200013b565b5f5b815181101562000578576001600b5f84848151811062000466576200046662000881565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055505f62000501838381518110620004ab57620004ab62000881565b6020026020010151620557305f6001604051602401620004cd91815260200190565b60408051601f198184030181529190526020810180516001600160e01b03908116632695d74d60e01b179091526200058616565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c83838151811062000539576200053962000881565b60200260200101513360018462000551575f62000554565b60015b60405162000566949392919062000895565b60405180910390a15060010162000442565b5062000583620005eb565b50565b5f62000595846103e862000622565b620005d65760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b60448201526064016200013b565b5f80835160208501868989f195945050505050565b6001546040515f916001600160a01b0316905f8051602062006535833981519152908390a3600180546001600160a01b0319169055565b5f80603f83619c4001026040850201603f5a021015949350505050565b610fc2806200557383390190565b80516001600160a01b038116811462000664575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b0381118282101715620006a357620006a362000669565b60405290565b5f82601f830112620006b9575f80fd5b815160206001600160401b0380831115620006d857620006d862000669565b8260051b604051601f19603f8301168101818110848211171562000700576200070062000669565b604052938452602081870181019490810192508785111562000720575f80fd5b6020870191505b848210156200074b576200073b826200064d565b8352918301919083019062000727565b979650505050505050565b5f805f805f805f8789036102008112156200076f575f80fd5b6200077a896200064d565b97506200078a60208a016200064d565b96506200079a60408a016200064d565b9550620007aa60608a016200064d565b945061014080607f1983011215620007c0575f80fd5b620007ca6200067d565b915060808a0151825260a08a0151602083015260c08a0151604083015260e08a01516060830152610100808b01516080840152610120808c015160a0850152828c015160c08501526101608c015160e08501526101808c0151828501526101a08c01518185015250505080935050620008476101c089016200064d565b6101e08901519092506001600160401b0381111562000864575f80fd5b620008728a828b01620006a9565b91505092959891949750929550565b634e487b7160e01b5f52603260045260245ffd5b6001600160a01b03858116825284166020820152604081018390526080810160038310620008d157634e487b7160e01b5f52602160045260245ffd5b82606083015295945050505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e0516102005161022051614b6162000a125f395f818161067d0152611ce201525f81816108f30152610f8501525f81816105a90152610fd701525f81816103ed01526112ce01525f8181610582015261128301525f818161092801526128cf01525f81816103c60152611ca001525f818161031d01526118a001525f81816107a901528181610ba201528181610e290152611e8001525f818161046601528181610be501528181610e4d0152611ea401525f81816105d0015281816112570152818161238e01528181612414015261285a01525f61060a01525f8181610975015281816113200152613ae801525f81816106ac01528181611049015261250a0152614b615ff3fe608060405234801561000f575f80fd5b50600436106102ff575f3560e01c80638a00b67a11610195578063adfcdb9d116100e4578063cf84a88c1161009e578063e772b76511610079578063e772b76514610923578063ecf22e321461094a578063ee1c71721461095d578063f556a79c14610970575f80fd5b8063cf84a88c14610876578063e4a61d95146108ee578063e76c01e414610915575f80fd5b8063adfcdb9d146107f6578063b1d8f27414610809578063b285a8701461082a578063bfd79cae1461083d578063c20fb59e14610850578063cc9baa1214610863575f80fd5b8063900cf0cf1161014f578063a5e8455d1161012a578063a5e8455d1461079b578063a70b9f0c146107a4578063a9952a0c146107cb578063ac85912e146107ee575f80fd5b8063900cf0cf1461073957806391050f4014610741578063972e6d6614610788575f80fd5b80638a00b67a146106785780638b7d38a11461069f5780638d4f0b6c146106a75780638da5cb5b146106ce5780638eb9399e146106df5780638f32d59b1461071b575f80fd5b8063524fcc8011610251578063727d0f351161020b5780638113630d116101e65780638113630d1461062c57806382a15be11461063f5780638777e0951461065257806388edf9be14610665575f80fd5b8063727d0f35146105cb57806378a1bdd4146105f25780637f6ec45514610605575f80fd5b8063524fcc80146104b257806352d420b91461051657806353905402146105295780635c79696c1461055d57806364b4f7511461057d5780636bca7c55146105a4575f80fd5b80632591003a116102bc578063415c2d9611610297578063415c2d961461044157806346d62a631461046157806350283275146104885780635245b1ea14610490575f80fd5b80632591003a146103e857806338f3e2851461040f578063394ff4061461042e575f80fd5b8063070f418a146103035780630df9049b146103185780630ecc535f146103525780630f2f6aa5146103a657806315e5a1e5146103b95780631cd2a05d146103c1575b5f80fd5b61031661031136600461400f565b610997565b005b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b61038661036036600461403c565b60086020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610349565b6103166103b436600461403c565b6109a6565b61033f610b9f565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f61041d36600461403c565b600b6020525f908152604090205481565b61031661043c366004614078565b610c0e565b61045461044f36600461410f565b610d19565b604051610349919061416f565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f610e26565b6104a361049e3660046142e2565b610e7c565b60405161034993929190614390565b6104ee6104c036600461403c565b60096020525f9081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a001610349565b61031661052436600461400f565b611037565b61038661053736600461403c565b60056020525f908152604090208054600182015460028301546003909301549192909184565b610565611042565b6040516001600160a01b039091168152602001610349565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b6105657f000000000000000000000000000000000000000000000000000000000000000081565b61031661060036600461403c565b6110ab565b6105657f000000000000000000000000000000000000000000000000000000000000000081565b61031661063a3660046143b8565b6114d1565b61031661064d36600461445f565b611627565b61033f61066036600461400f565b611c57565b610316610673366004614519565b611d1f565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f611daf565b6105657f000000000000000000000000000000000000000000000000000000000000000081565b6001546001600160a01b0316610565565b6106e7611dce565b604080518451815260209485015185820152835191810191909152929091015160608301521515608082015260a001610349565b6001546001600160a01b031633146040519015158152602001610349565b61033f611e7d565b6104ee61074f36600461456b565b600a60209081525f9283526040808420909152908252902080546001820154600283015460038401546004909401549293919290919085565b6104a361079636600461403c565b611ede565b61033f60025481565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b6003546004546107d9919082565b60408051928352602083019190915201610349565b61033f611f18565b61033f61080436600461459c565b611f26565b61081c61081736600461403c565b611f3c565b6040516103499291906145c5565b610316610838366004614605565b611fa3565b6107d961084b36600461403c565b6121a5565b61033f61085e36600461403c565b6122bc565b61056561087136600461403c565b612504565b61088961088436600461403c565b61253e565b60408051845181526020808601518183015285830151828401526060958601518683015284516080808401919091529085015160a08301529184015160c08201529383015160e085015290910151610100830152151561012082015261014001610349565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b6006546007546107d9919082565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b610316610958366004614605565b6126a9565b61031661096b366004614640565b6127be565b6105657f000000000000000000000000000000000000000000000000000000000000000081565b6109a381600133611fa3565b50565b6109ae6127ce565b5f806109b86127f6565b915091505f806109c78561294e565b915091505f6109d886868585610e7c565b50909150600590508160068111156109f2576109f261437c565b14610a555760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a2063616e6e6f742d756e72656769737465722d696e604482015267697469617469766560c01b60648201526084015b60405180910390fd5b5f610a5e611e7d565b9050610a6b600182614677565b836080015110610a7d57610a7d61468a565b825185511015610a8f57610a8f61468a565b826020015185602001511015610aa757610aa761468a565b825185518690610ab8908390614677565b9052506020808401519086018051610ad1908390614677565b90525084516006556020808601516007556001600160a01b0388165f908152600b90915260408082205f1990555160248101839052610b459089906205573090849060440160408051601f198184030181529190526020810180516001600160e01b031663955161cd60e01b179052612a18565b90507f4b2a06fb31ab5670eed8302066f58582b640ec5a17d06ddd383d30e3e11f1b64888383610b75575f610b78565b60015b604051610b87939291906146b2565b60405180910390a1505050505050506109a360015f55565b5f7f00000000000000000000000000000000000000000000000000000000000000006001610bcb611e7d565b610bd59190614677565b610bdf91906146d6565b610c09907f00000000000000000000000000000000000000000000000000000000000000006146ed565b905090565b610c166127ce565b5f610c2085612a79565b90505f805f80846001600160a01b031663eb876bf78a338b8b8b6040518663ffffffff1660e01b8152600401610c5a959493929190614700565b6080604051808303815f875af1158015610c76573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9a91906147a4565b604080516001600160a01b038c168152602081018f9052908101859052606081018490526080810183905260a081018290529397509195509350915033907f5cd37895bb9287e542a426d6abbc93d7daca0979bd43264d320d4fd224a5fc5e9060c00160405180910390a25050505050610d1360015f55565b50505050565b6060816001600160401b03811115610d3357610d336141e7565b604051908082528060200260200182016040528015610d6657816020015b6060815260200190600190039081610d515790505b5090505f5b82811015610e1f575f8030868685818110610d8857610d886147d7565b9050602002810190610d9a91906147eb565b604051610da892919061482d565b5f60405180830381855af49150503d805f8114610de0576040519150601f19603f3d011682016040523d82523d5f602084013e610de5565b606091505b509150915081610df757805181602001fd5b80848481518110610e0a57610e0a6147d7565b60209081029190910101525050600101610d6b565b5092915050565b5f7f0000000000000000000000000000000000000000000000000000000000000000610e727f000000000000000000000000000000000000000000000000000000000000000042614677565b610c099190614850565b6001600160a01b0384165f908152600b602052604081205481908190808203610eae575f805f9350935093505061102d565b5f610eb7611e7d565b9050808203610ed15760015f80945094509450505061102d565b6001600160a01b0389165f90815260096020526040902060040154935060018201610f055750600693505f915061102d9050565b610f10600182614677565b8410610f215760049450505061102d565b5f610f2e895f0151611c57565b905080885f0151118015610f46575060608801518851115b15610f7857885160025489515f9291610f5e916146d6565b610f689190614863565b60039750945061102d9350505050565b610f83600183614677565b7f00000000000000000000000000000000000000000000000000000000000000008860800151610fb391906146ed565b108061100d57508751606089015111801561100d5750670de0b6b3a7640000610ffc7f0000000000000000000000000000000000000000000000000000000000000000836146d6565b6110069190614863565b8860600151115b156110225750600594505f925061102d915050565b50600294505f925050505b9450945094915050565b6109a3815f336126a9565b5f8061106e7f000000000000000000000000000000000000000000000000000000000000000033612b4e565b6040519091506001600160a01b0382169033907fda66ba232f4fb8c122b7026f55eeff1d0b9cf2560b7873b2bba6eaab4c3d5989905f90a3919050565b6110b36127ce565b5f6110bc611e7d565b90506002811161111f5760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a20726567697374726174696f6e2d6e6f742d7965746044820152670b595b98589b195960c21b6064820152608401610a4c565b6001600160a01b0382166111755760405162461bcd60e51b815260206004820152601860248201527f476f7665726e616e63653a207a65726f2d6164647265737300000000000000006044820152606401610a4c565b5f61117f83611ede565b509091505f90508160068111156111985761119861437c565b146111f75760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e69746961746976652d616c72656164792d726044820152681959da5cdd195c995960ba1b6064820152608401610a4c565b5f61120133612504565b90505f61120c6127f6565b50335f8181526008602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460608201529192506112a7907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690307f0000000000000000000000000000000000000000000000000000000000000000612bb9565b8151602082015160608301515f916112be916146ed565b9050670de0b6b3a76400006112f37f0000000000000000000000000000000000000000000000000000000000000000846146d6565b6112fd9190614863565b6040516305a4d3f160e21b81526001600160a01b038781166004830152611399917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa158015611367573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061138b9190614876565b611393610b9f565b84611f26565b10156113e75760405162461bcd60e51b815260206004820152601d60248201527f476f7665726e616e63653a20696e73756666696369656e742d6c7174790000006044820152606401610a4c565b6001600160a01b0388165f908152600b6020526040902087905561140c600188614677565b6001600160a01b0389165f9081526009602052604080822060040192909255905160248101899052611474908a90620557309084906044015b60408051601f198184030181529190526020810180516001600160e01b0316632695d74d60e01b179052612a18565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c89338a846114a5575f6114a8565b60015b6040516114b8949392919061488d565b60405180910390a150505050505050506109a360015f55565b6001546001600160a01b0316331461152b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610a4c565b5f5b815181101561161e576001600b5f84848151811061154d5761154d6147d7565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055505f6115af83838151811061158e5761158e6147d7565b6020026020010151620557305f600160405160240161144591815260200190565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c8383815181106115e4576115e46147d7565b6020026020010151336001846115fa575f6115fd565b60015b60405161160d949392919061488d565b60405180910390a15060010161152d565b506109a3612c20565b61162f6127ce565b848314801561163d57508281145b6116935760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a2061727261792d6c656e6774682d6d69736d6174636044820152600d60fb1b6064820152608401610a4c565b61169d8888612c69565b6116a78686612c69565b6116e28484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d4692505050565b61171d8282808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d4692505050565b6117888484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f92019190915250612db892505050565b6117f38484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f92019190915250612e5692505050565b5f6117fe8989612ef4565b335f9081526008602090815260409182902082516080810184528154815260018201549281019290925260028101549282018390526003015460608201529192501561187e5760405162461bcd60e51b815260206004820152600f60248201526e1b5d5cdd0818994818481c995cd95d608a1b6044820152606401610a4c565b80515f0361189e5760405162461bcd60e51b8152600401610a4c906148b9565b7f00000000000000000000000000000000000000000000000000000000000000006118c7610e26565b1115611a3e575f5b87811015611a3c575f805b84518110156119c6578a8a848181106118f5576118f56147d7565b905060200201602081019061190a919061403c565b6001600160a01b0316858281518110611925576119256147d7565b60200260200101515f01516001600160a01b0316036119be5760019150848181518110611954576119546147d7565b602002602001015160200151898985818110611972576119726147d7565b9050602002013513156119b95760405162461bcd60e51b815260206004820152600f60248201526e43616e6e6f7420696e63726561736560881b6044820152606401610a4c565b6119c6565b6001016118da565b5080611a33578787838181106119de576119de6147d7565b905060200201355f14611a335760405162461bcd60e51b815260206004820181905260248201527f4d757374206265207a65726f20666f72206e657720696e6974696174697665736044820152606401610a4c565b506001016118cf565b505b5f876001600160401b03811115611a5757611a576141e7565b604051908082528060200260200182016040528015611a80578160200160208202803683370190505b5090505f886001600160401b03811115611a9c57611a9c6141e7565b604051908082528060200260200182016040528015611ac5578160200160208202803683370190505b5090505f5b89811015611ba057365f805f8c8c86818110611ae857611ae86147d7565b9050602002013513611afc57898986611b00565b8b8b875b9250925092505f838386818110611b1957611b196147d7565b9050602002013590505f885f0151828a60200151611b3791906146d6565b611b419190614863565b905081895f01818151611b549190614677565b905250602089018051829190611b6b908390614677565b90525082518190849088908110611b8457611b846147d7565b6020908102919091010152505060019093019250611aca915050565b50611c408a8a808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284375f9201919091525050604080516020808d0282810182019093528c82529093508c92508b9182918501908490808284375f9201919091525088925087915061336a9050565b50505050611c4d60015f55565b5050505050505050565b5f815f03611c6657505f919050565b5f8083670de0b6b3a7640000600254611c7f91906146d6565b611c899190614863565b90508015611cd15780611cc4670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006146d6565b611cce9190614863565b91505b611d17670de0b6b3a7640000611d077f0000000000000000000000000000000000000000000000000000000000000000876146d6565b611d119190614863565b83613bf7565b949350505050565b611d276127ce565b611d318383612c69565b611d3b8383612ef4565b508015611da157335f9081526008602052604090206002015415611da15760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206d757374206265206120726573657400000000006044820152606401610a4c565b611daa60015f55565b505050565b5f80611db96127f6565b509050611dc8815f0151611c57565b91505090565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f80611dfe611e7d565b60408051808201825260035481526004546020808301919091528251808401909352600654835260075490830152955093509050611e3d600182614677565b84602001511015611e7757825160019250611e6490611e5a610b9f565b8560200151611f26565b8452611e71600182614677565b60208501525b50909192565b5f7f0000000000000000000000000000000000000000000000000000000000000000611ec97f000000000000000000000000000000000000000000000000000000000000000042614677565b611ed39190614863565b610c099060016146ed565b5f805f80611eea6127f6565b5090505f80611ef88761294e565b91509150611f0887848484610e7c565b9550955095505050509193909250565b6003545f90611dc881611c57565b5f611f32848484613c0c565b90505b9392505050565b604080518082019091525f8082526020820152611f7660405180608001604052805f81526020015f81526020015f81526020015f81525090565b611f7e6127ce565b611f866127f6565b509150611f928361294e565b509050611f9e60015f55565b915091565b611fab6127ce565b335f81815260086020526040812091611fc390612504565b9050806001600160a01b03163b5f03611fee5760405162461bcd60e51b8152600401610a4c90614903565b81548511156120515760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e73756666696369656e742d756e616c6c6f63604482015268617465642d6c71747960b81b6064820152608401610a4c565b815485101561209957815460018301545f919061206e90886146d6565b6120789190614863565b905080836001015f82825461208d9190614677565b909155506120a0915050565b5f60018301555b84825f015f8282546120b29190614677565b9091555050604051633fdf42e360e01b81526004810186905284151560248201526001600160a01b0384811660448301525f9182918291829182918291881690633fdf42e39060640160c0604051808303815f875af1158015612117573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061213b9190614946565b955095509550955095509550336001600160a01b03167f2870fe177552976b0dbc3166ded2169d5d73275fc565df30894320fd3608f3398a88888888888860405161218c979695949392919061498c565b60405180910390a25050505050505050611daa60015f55565b5f805f6121b133612504565b9050806001600160a01b03163b5f036121dc5760405162461bcd60e51b8152600401610a4c90614903565b604051633fdf42e360e01b81525f60048201819052600160248301526001600160a01b0386811660448401529091829182918291861690633fdf42e39060640160c0604051808303815f875af1158015612238573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061225c9190614946565b604051929c509a50939750919550935090915033907f2870fe177552976b0dbc3166ded2169d5d73275fc565df30894320fd3608f339906122aa908b908890889088908e9089908f9061498c565b60405180910390a25050505050915091565b5f6122c56127ce565b5f6122ce6127f6565b5090505f806122dc8561294e565b915091505f806122ee87868686610e7c565b9193509091506003905082600681111561230a5761230a61437c565b1461231c575f955050505050506124f6565b6001612326611e7d565b6123309190614677565b8560200151146123425761234261468a565b600161234c611e7d565b6123569190614677565b6001600160a01b038881165f90815260096020526040808220600490810194909455516370a0823160e01b81523093810193909352917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa1580156123d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123f99190614876565b905080821115612407578091505b61243b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168984613c3a565b5f61249489620557305f8a6020015187604051602401612465929190918252602082015260400190565b60408051601f198184030181529190526020810180516001600160e01b0316630f0c6f0d60e11b179052612a18565b9050886001600160a01b03167f023acf63ebe0add874abd78d1a9b96f3384a3128bfdb97bf6c23e91f3d2e6e5a848960200151846124d2575f6124d5565b60015b6040516124e4939291906149c7565b60405180910390a25090955050505050505b6124ff60015f55565b919050565b5f6125387f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038416613c6b565b92915050565b61256560405180608001604052805f81526020015f81526020015f81526020015f81525090565b61256d613edd565b5f80612577611e7d565b905060055f866001600160a01b03166001600160a01b031681526020019081526020015f206040518060800160405290815f82015481526020016001820154815260200160028201548152602001600382015481525050935060095f866001600160a01b03166001600160a01b031681526020019081526020015f206040518060a00160405290815f8201548152602001600182015481526020016002820154815260200160038201548152602001600482015481525050925060018161263e9190614677565b846020015110156126a157600191505f612656610b9f565b90505f61266b855f0151838760200151611f26565b90505f6126818660400151848860600151611f26565b828852606088018190529050612698600185614677565b60208801525050505b509193909250565b6126b16127ce565b5f6126bb84612a79565b6040516338fb359960e01b81526004810186905233602482015284151560448201526001600160a01b0384811660648301529192505f91829182918291908616906338fb3599906084016080604051808303815f875af1158015612721573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061274591906147a4565b604080516001600160a01b038c168152602081018e9052908101859052606081018490526080810183905260a081018290529397509195509350915033907f5cd37895bb9287e542a426d6abbc93d7daca0979bd43264d320d4fd224a5fc5e9060c00160405180910390a25050505050611daa60015f55565b6127ca82825f33610c0e565b5050565b60025f54036127f057604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f612825611dce565b919450925090508015612949578251600355602083015160049081556040516370a0823160e01b815230918101919091525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156128a7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128cb9190614876565b90507f000000000000000000000000000000000000000000000000000000000000000081106128fa57806128fc565b5f5b6002819055845160208087015160408051938452918301528101919091527f977a95eb227039fce4f8fe452d2d625ab67187ac5363991816ed06f4e5eed5019060600160405180910390a1505b509091565b61297560405180608001604052805f81526020015f81526020015f81526020015f81525090565b61297d613edd565b5f6129878461253e565b919450925090508015612a12576001600160a01b0384165f81815260056020908152604091829020865180825587830151600183018190558885015160028401556060808a01516003909401849055855192835293820192909252928301527f27b60fbfc71b45e0696ef86e3c31ac7979d464c139f6de0709de61b6b78093c5910160405180910390a25b50915091565b5f612a25846103e8613cca565b612a645760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b6044820152606401610a4c565b5f80835160208501868989f195945050505050565b5f808211612ac95760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a207a65726f2d6c7174792d616d6f756e74000000006044820152606401610a4c565b5f612ad333612504565b9050806001600160a01b03163b5f03612af057612aee611042565b505b335f90815260086020526040812080548392869291612b109084906146ed565b90915550612b20905084426146d6565b335f9081526008602052604081206001018054909190612b419084906146ed565b9091555090949350505050565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760095ff590506001600160a01b038116612538576040516330be1a3d60e21b815260040160405180910390fd5b6040516001600160a01b038481166024830152838116604483015260648201839052610d139186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613ce7565b6001546040515f916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b805f819003612c7757505050565b5f5b612c84600183614677565b811015610d13575f612c978260016146ed565b90505b82811015612d3d57848482818110612cb457612cb46147d7565b9050602002016020810190612cc9919061403c565b6001600160a01b0316858584818110612ce457612ce46147d7565b9050602002016020810190612cf9919061403c565b6001600160a01b031603612d355760405162461bcd60e51b815260206004820152600360248201526206475760ec1b6044820152606401610a4c565b600101612c9a565b50600101612c79565b80515f5b81811015611daa575f838281518110612d6557612d656147d7565b60200260200101511215612db05760405162461bcd60e51b815260206004820152601260248201527143616e6e6f74206265206e6567617469766560701b6044820152606401610a4c565b600101612d4a565b5f5b8251811015611daa575f838281518110612dd657612dd66147d7565b60200260200101511380612e0257505f828281518110612df857612df86147d7565b6020026020010151135b612e4e5760405162461bcd60e51b815260206004820152601a60248201527f476f7665726e616e63653a20766f74696e67206e6f7468696e670000000000006044820152606401610a4c565b600101612dba565b5f5b8251811015611daa57828181518110612e7357612e736147d7565b60200260200101515f1480612ea05750818181518110612e9557612e956147d7565b60200260200101515f145b612eec5760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63653a20766f74652d616e642d7665746f000000000000006044820152606401610a4c565b600101612e58565b60605f826001600160401b03811115612f0f57612f0f6141e7565b604051908082528060200260200182016040528015612f7657816020015b612f636040518060a001604052805f6001600160a01b031681526020015f81526020015f81526020015f81526020015f81525090565b815260200190600190039081612f2d5790505b5090505f836001600160401b03811115612f9257612f926141e7565b604051908082528060200260200182016040528015612fbb578160200160208202803683370190505b5090505f846001600160401b03811115612fd757612fd76141e7565b604051908082528060200260200182016040528015613000578160200160208202803683370190505b5090505f856001600160401b0381111561301c5761301c6141e7565b604051908082528060200260200182016040528015613045578160200160208202803683370190505b5090505f866001600160401b03811115613061576130616141e7565b60405190808252806020026020018201604052801561308a578160200160208202803683370190505b5090505f5b8781101561331c57335f908152600a60205260408120818b8b858181106130b8576130b86147d7565b90506020020160208101906130cd919061403c565b6001600160a01b0316815260208082019290925260409081015f20815160a081018352815480825260018301549482019490945260028201549281019290925260038101546060830152600401546080820152915015158061313257505f8160400151115b61317e5760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a206e6f7468696e6720746f207265736574000000006044820152606401610a4c565b6040518060a001604052808b8b8581811061319b5761319b6147d7565b90506020020160208101906131b0919061403c565b6001600160a01b03168152602001825f01518152602001826040015181526020018260200151815260200182606001518152508783815181106131f5576131f56147d7565b6020026020010181905250868281518110613212576132126147d7565b602002602001015160200151613227906149e2565b868381518110613239576132396147d7565b602002602001018181525050868281518110613257576132576147d7565b60200260200101516040015161326c906149e2565b85838151811061327e5761327e6147d7565b60200260200101818152505086828151811061329c5761329c6147d7565b6020026020010151606001516132b1906149e2565b8483815181106132c3576132c36147d7565b6020026020010181815250508682815181106132e1576132e16147d7565b6020026020010151608001516132f6906149e2565b838381518110613308576133086147d7565b60209081029190910101525060010161308f565b5061335e8888808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508892508791508690508561336a565b50929695505050505050565b613372613f07565b61337a6127f6565b60208301528152613389611e7d565b60e0820152335f9081526008602090815260408083208151608081018352815481526001820154938101939093526002810154838301526003015460608301528301525b8651811015613ae5575f8782815181106133e9576133e96147d7565b60200260200101519050868281518110613405576134056147d7565b602002602001015183610100018181525050858281518110613429576134296147d7565b6020908102919091010151610120840152610100830151151580613451575061012083015115155b61345d5761345d61468a565b84828151811061346f5761346f6147d7565b602002602001015183610140018181525050838281518110613493576134936147d7565b6020026020010151836101600181815250506134ae8161294e565b608085018190526060850182905284515f926134cd9285929190610e7c565b505090505f84610100015113806134e857505f846101200151135b156135865760028160068111156135015761350161437c565b148061351e5750600381600681111561351c5761351c61437c565b145b8061353a575060048160068111156135385761353861437c565b145b6135865760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206163746976652d766f74652d66736d00000000006044820152606401610a4c565b600681600681111561359a5761359a61437c565b036135fc575f846101000151131580156135b957505f84610120015113155b6135fc5760405162461bcd60e51b8152602060048201526014602482015273135d5cdd0818994818481dda5d1a191c985dd85b60621b6044820152606401610a4c565b6040805160a080820183526080808801805151845280516020908101519085015280518501519484019490945283516060908101519084015283518101519083015286015251516101008501516136539190613d48565b6080850180519190915251604001516101208501516136729190613d48565b6080850180516040019190915251602001516101408501516136949190613d48565b6080850180516020019190915251606001516101608501516136b69190613d48565b60808086018051606090810193909352516001600160a01b0385165f90815260096020908152604091829020835181559083015160018201559082015160028201559281015160038401550151600490910155600681600681111561371d5761371d61437c565b146137b75760a084015151602085015151101561373c5761373c61468a565b60a08401515160208501518051613754908390614677565b90525060a0840151602090810151818601519091018051613776908390614677565b905250608084015151602085015180516137919083906146ed565b90525060808401516020908101518186015190910180516137b39083906146ed565b9052505b335f908152600a602090815260408083206001600160a01b0386168452825291829020825160a0810184528154815260018201549281019283526002820154938101939093526003810154606084015260040154608083015260c0860191909152516101408501516138299190613d48565b60c08501805160200191909152516060015161016085015161384b9190613d48565b60c08501805160600191909152515161010085015161386a9190613d48565b60c0850180519190915251604001516101208501516138899190613d48565b60c0850180516040019190915260e085015181516080015251602081015190516138b49042906146d6565b10156138c2576138c261468a565b60c084015160608101516040909101516138dd9042906146d6565b10156138eb576138eb61468a565b60c0840151335f908152600a602090815260408083206001600160a01b03871684528252918290208351815590830151600182015581830151600282015560608301516003820155608090920151600490920191909155840151516101208501516101008601516139659291613960916149fc565b613d75565b60408501805191909152516020015161016085015161014086015161398e9291613960916149fc565b6040808601805160200192909252905101516101208501516101008601516139bf92916139ba916149fc565b613d48565b846040015160400181815250506139ee8460400151606001518561016001518661014001516139ba91906149fc565b846040015160600181815250505f8460c00151604001515f03613a8057613a6c83620557305f8860e00151338a604001518b60c001518c60800151604051602401613a3d959493929190614a23565b60408051601f198184030181529190526020810180516001600160e01b0316633064cba960e21b179052612a18565b613a76575f613a79565b60015b9050613a84565b5060025b61010085015161012086015160e08701516040516001600160a01b0387169333937f361311c5372a81d4ab4bce0bf1b7dbfc61da467667c2097deb69b4b24bc12d2893613ad2938890614ad3565b60405180910390a35050506001016133cd565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166316934fc4613b1e33612504565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015613b60573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b849190614876565b8160400151604001511115613bab5760405162461bcd60e51b8152600401610a4c906148b9565b6020808201518051600655810151600755604091820151335f90815260088352839020815181559181015160018301559182015160028201556060909101516003909101555050505050565b5f818311613c055781611f35565b5090919050565b5f80613c1884866146d6565b9050828111613c27575f613c31565b613c318382614677565b95945050505050565b6040516001600160a01b03838116602483015260448201839052611daa91859182169063a9059cbb90606401612bee565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101205f90611f35565b5f80603f83619c4001026040850201603f5a021015949350505050565b5f613cfb6001600160a01b03841683613d9b565b905080515f14158015613d1f575080806020019051810190613d1d9190614af5565b155b15611daa57604051635274afe760e01b81526001600160a01b0384166004820152602401610a4c565b5f80821215613d6b57613d5a82613da8565b613d649084614677565b9050612538565b611f3582846146ed565b5f80821215613d9157613d8782613da8565b613d6490846146ed565b611f358284614677565b6060611f3583835f613dbf565b5f808212613db65781612538565b612538826149e2565b606081471015613de45760405163cd78605960e01b8152306004820152602401610a4c565b5f80856001600160a01b03168486604051613dff9190614b10565b5f6040518083038185875af1925050503d805f8114613e39576040519150601f19603f3d011682016040523d82523d5f602084013e613e3e565b606091505b5091509150613e4e868383613e58565b9695505050505050565b606082613e6d57613e6882613eb4565b611f35565b8151158015613e8457506001600160a01b0384163b155b15613ead57604051639996b31560e01b81526001600160a01b0385166004820152602401610a4c565b5080611f35565b805115613ec45780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b604080516101c081019091525f61018082018181526101a083019190915281908152602001613f4760405180604001604052805f81526020015f81525090565b8152602001613f7360405180608001604052805f81526020015f81526020015f81526020015f81525090565b8152602001613f9f60405180608001604052805f81526020015f81526020015f81526020015f81525090565b8152602001613fac613edd565b8152602001613fb9613edd565b8152602001613feb6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6020828403121561401f575f80fd5b5035919050565b80356001600160a01b03811681146124ff575f80fd5b5f6020828403121561404c575f80fd5b611f3582614026565b5f60e08284031215614065575f80fd5b50919050565b80151581146109a3575f80fd5b5f805f80610140858703121561408c575f80fd5b8435935061409d8660208701614055565b92506101008501356140ae8161406b565b91506140bd6101208601614026565b905092959194509250565b5f8083601f8401126140d8575f80fd5b5081356001600160401b038111156140ee575f80fd5b6020830191508360208260051b8501011115614108575f80fd5b9250929050565b5f8060208385031215614120575f80fd5b82356001600160401b03811115614135575f80fd5b614141858286016140c8565b90969095509350505050565b5f5b8381101561416757818101518382015260200161414f565b50505f910152565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b828110156141da57878503603f19018452815180518087526141bb818989018a850161414d565b601f01601f191695909501860194509285019290850190600101614194565b5092979650505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b038111828210171561421d5761421d6141e7565b60405290565b604051608081016001600160401b038111828210171561421d5761421d6141e7565b604051601f8201601f191681016001600160401b038111828210171561426d5761426d6141e7565b604052919050565b5f60a08284031215614285575f80fd5b60405160a081018181106001600160401b03821117156142a7576142a76141e7565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b5f805f808486036101808112156142f7575f80fd5b61430086614026565b94506040601f1982011215614313575f80fd5b61431b6141fb565b602087810135825260408801359082015293506080605f198201121561433f575f80fd5b50614348614223565b60608681013582526080870135602083015260a0870135604083015260c08701359082015291506140bd8660e08701614275565b634e487b7160e01b5f52602160045260245ffd5b60608101600785106143a4576143a461437c565b938152602081019290925260409091015290565b5f60208083850312156143c9575f80fd5b82356001600160401b03808211156143df575f80fd5b818501915085601f8301126143f2575f80fd5b813581811115614404576144046141e7565b8060051b9150614415848301614245565b818152918301840191848101908884111561442e575f80fd5b938501935b838510156144535761444485614026565b82529385019390850190614433565b98975050505050505050565b5f805f805f805f806080898b031215614476575f80fd5b88356001600160401b038082111561448c575f80fd5b6144988c838d016140c8565b909a50985060208b01359150808211156144b0575f80fd5b6144bc8c838d016140c8565b909850965060408b01359150808211156144d4575f80fd5b6144e08c838d016140c8565b909650945060608b01359150808211156144f8575f80fd5b506145058b828c016140c8565b999c989b5096995094979396929594505050565b5f805f6040848603121561452b575f80fd5b83356001600160401b03811115614540575f80fd5b61454c868287016140c8565b90945092505060208401356145608161406b565b809150509250925092565b5f806040838503121561457c575f80fd5b61458583614026565b915061459360208401614026565b90509250929050565b5f805f606084860312156145ae575f80fd5b505081359360208301359350604090920135919050565b825181526020808401519082015260c08101611f356040830184805182526020810151602083015260408101516040830152606081015160608301525050565b5f805f60608486031215614617575f80fd5b8335925060208401356146298161406b565b915061463760408501614026565b90509250925092565b5f806101008385031215614652575f80fd5b823591506145938460208501614055565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561253857612538614663565b634e487b7160e01b5f52600160045260245ffd5b600381106146ae576146ae61437c565b9052565b6001600160a01b03841681526020810183905260608101611d17604083018461469e565b808202811582820484141761253857612538614663565b8082018082111561253857612538614663565b8581526001600160a01b0385811660208301526101608201908061472387614026565b1660408401528061473660208801614026565b1660608401525060408501356080830152606085013560a0830152608085013560ff81168114614764575f80fd5b60ff1660c08381019190915260a086013560e084015294909401356101008201529115156101208301526001600160a01b03166101409091015292915050565b5f805f80608085870312156147b7575f80fd5b505082516020840151604085015160609095015191969095509092509050565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e19843603018112614800575f80fd5b8301803591506001600160401b03821115614819575f80fd5b602001915036819003821315614108575f80fd5b818382375f9101908152919050565b634e487b7160e01b5f52601260045260245ffd5b5f8261485e5761485e61483c565b500690565b5f826148715761487161483c565b500490565b5f60208284031215614886575f80fd5b5051919050565b6001600160a01b038581168252841660208201526040810183905260808101613c31606083018461469e565b6020808252602a908201527f476f7665726e616e63653a20696e73756666696369656e742d6f722d616c6c6f60408201526963617465642d6c71747960b01b606082015260800190565b60208082526023908201527f476f7665726e616e63653a20757365722d70726f78792d6e6f742d6465706c6f6040820152621e595960ea1b606082015260800190565b5f805f805f8060c0878903121561495b575f80fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6001600160a01b03979097168752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b8381526020810183905260608101611d17604083018461469e565b5f600160ff1b82016149f6576149f6614663565b505f0390565b8082018281125f831280158216821582161715614a1b57614a1b614663565b505092915050565b8581526001600160a01b03851660208201526102008101614a686040830186805182526020810151602083015260408101516040830152606081015160608301525050565b835160c0830152602084015160e08301526040840151610100830152606084015161012083015260808401516101408301528251610160830152602083015161018083015260408301516101a083015260608301516101c083015260808301516101e0830152613e4e565b848152602081018490526040810183905260808101613c31606083018461469e565b5f60208284031215614b05575f80fd5b8151611f358161406b565b5f8251614b2181846020870161414d565b919091019291505056fea264697066735822122068679bfa2c2eee9124643e597f74c2abf6b9406391adb4f8cd43a688cc2b27df64736f6c63430008180033610100604052348015610010575f80fd5b50604051610fc2380380610fc283398101604081905261002f9161006b565b6001600160a01b0392831660805290821660a0521660c0523360e0526100ab565b80516001600160a01b0381168114610066575f80fd5b919050565b5f805f6060848603121561007d575f80fd5b61008684610050565b925061009460208501610050565b91506100a260408501610050565b90509250925092565b60805160a05160c05160e051610e7461014e5f395f8181610195015281816102d801528181610591015261096201525f818161021a015281816102530152818161045801526106fc01525f81816101c801528181610335015281816104cd01528181610678015281816108030152610b4101525f818161014a015281816103d7015281816105e50152818161076e015281816108bf0152610a160152610e745ff3fe60806040526004361061007c575f3560e01c8063886117361161004c578063886117361461018457806399ad68a7146101b7578063eb876bf7146101ea578063f556a79c14610209575f80fd5b80630b76619b1461008757806338fb3599146100ae5780633fdf42e3146100ed5780637f6ec45514610139575f80fd5b3661008357005b5f80fd5b348015610092575f80fd5b5061009b61023c565b6040519081526020015b60405180910390f35b3480156100b9575f80fd5b506100cd6100c8366004610c7f565b6102c9565b6040805194855260208501939093529183015260608201526080016100a5565b3480156100f8575f80fd5b5061010c610107366004610ccb565b610580565b604080519687526020870195909552938501929092526060840152608083015260a082015260c0016100a5565b348015610144575f80fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100a5565b34801561018f575f80fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000081565b3480156101c2575f80fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000081565b3480156101f5575f80fd5b506100cd610204366004610d06565b610953565b348015610214575f80fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516305a4d3f160e21b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906316934fc490602401602060405180830381865afa1580156102a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102c49190610d70565b905090565b5f808080336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461031e5760405162461bcd60e51b815260040161031590610d87565b60405180910390fd5b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610382573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a69190610d70565b6040516323b872dd60e01b81526001600160a01b038a81166004830152306024830152604482018c905291925047917f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303815f875af115801561041d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104419190610dbe565b5060405163534a7e1d60e11b8152600481018b90527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a694fc3a906024015f604051808303815f87803b1580156104a1575f80fd5b505af11580156104b3573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201525f92507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691506370a0823190602401602060405180830381865afa15801561051b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061053f9190610d70565b90504761054c8483610de0565b97506105588382610de0565b955089156105715761056b898383610b13565b90975094505b50505050945094509450949050565b5f8080808080336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105ce5760405162461bcd60e51b815260040161031590610d87565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610632573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106569190610d70565b6040516370a0823160e01b81523060048201529091505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156106bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106e19190610d70565b6040516305c2fbcf60e31b8152600481018d905290915047907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e17de78906024015f604051808303815f87803b158015610745575f80fd5b505af1158015610757573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692506370a082319150602401602060405180830381865afa1580156107bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107e19190610d70565b6040516370a0823160e01b81523060048201529098505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610848573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061086c9190610d70565b905047610879858b610de0565b9a506108858483610de0565b98506108918382610de0565b9650891561092b5760405163a9059cbb60e01b81526001600160a01b038d81166004830152602482018c90527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303815f875af1158015610905573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109299190610dbe565b505b8c156109425761093c8c8383610b13565b90985095505b505050505093975093979195509350565b5f808080336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461099f5760405162461bcd60e51b815260040161031590610d87565b6109ac6020880188610e05565b6001600160a01b0316886001600160a01b031614610a0c5760405162461bcd60e51b815260206004820152601b60248201527f5573657250726f78793a206f776e65722d6e6f742d73656e64657200000000006044820152606401610315565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d505accf610a4860208a018a610e05565b610a5860408b0160208c01610e05565b60408b013560608c0135610a7260a08e0160808f01610e1e565b6040516001600160e01b031960e088901b1681526001600160a01b0395861660048201529490931660248501526044840191909152606483015260ff16608482015260a08a013560a482015260c08a013560c482015260e4015f604051808303815f87803b158015610ae2575f80fd5b505af1925050508015610af3575060015b50610b00898988886102c9565b929c919b50995090975095505050505050565b5f808315610bad5760405163a9059cbb60e01b81526001600160a01b038681166004830152602482018690527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303815f875af1158015610b87573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bab9190610dbe565b505b8215610c4a575f856001600160a01b0316846040515f6040518083038185875af1925050503d805f8114610bfc576040519150601f19603f3d011682016040523d82523d5f602084013e610c01565b606091505b5050905080610c485760405162461bcd60e51b8152602060048201526013602482015272155cd95c941c9bde1e4e88195d1a0b59985a5b606a1b6044820152606401610315565b505b5091939092509050565b80356001600160a01b0381168114610c6a575f80fd5b919050565b8015158114610c7c575f80fd5b50565b5f805f8060808587031215610c92575f80fd5b84359350610ca260208601610c54565b92506040850135610cb281610c6f565b9150610cc060608601610c54565b905092959194509250565b5f805f60608486031215610cdd575f80fd5b833592506020840135610cef81610c6f565b9150610cfd60408501610c54565b90509250925092565b5f805f805f858703610160811215610d1c575f80fd5b86359550610d2c60208801610c54565b945060e0603f1982011215610d3f575f80fd5b50604086019250610120860135610d5581610c6f565b9150610d646101408701610c54565b90509295509295909350565b5f60208284031215610d80575f80fd5b5051919050565b6020808252601f908201527f5573657250726f78793a2063616c6c65722d6e6f742d7374616b696e67563200604082015260600190565b5f60208284031215610dce575f80fd5b8151610dd981610c6f565b9392505050565b81810381811115610dff57634e487b7160e01b5f52601160045260245ffd5b92915050565b5f60208284031215610e15575f80fd5b610dd982610c54565b5f60208284031215610e2e575f80fd5b813560ff81168114610dd9575f80fdfea2646970667358221220d1fd88ecf95b01ee889170e70150b51cc382704fb757065b94b6a610e516a1e364736f6c634300081800338be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e00000000000000000000000006dea81c8171d0ba574754ef6f8b412f2ed88c54d0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab9800000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000005af3107a40000000000000000000000000000000000000000000000000000de0b6b3a7640001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067884c000000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000007e900000000000000000000000000bec25c5590e89596bde2dfcdc71579e66858772c00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106102ff575f3560e01c80638a00b67a11610195578063adfcdb9d116100e4578063cf84a88c1161009e578063e772b76511610079578063e772b76514610923578063ecf22e321461094a578063ee1c71721461095d578063f556a79c14610970575f80fd5b8063cf84a88c14610876578063e4a61d95146108ee578063e76c01e414610915575f80fd5b8063adfcdb9d146107f6578063b1d8f27414610809578063b285a8701461082a578063bfd79cae1461083d578063c20fb59e14610850578063cc9baa1214610863575f80fd5b8063900cf0cf1161014f578063a5e8455d1161012a578063a5e8455d1461079b578063a70b9f0c146107a4578063a9952a0c146107cb578063ac85912e146107ee575f80fd5b8063900cf0cf1461073957806391050f4014610741578063972e6d6614610788575f80fd5b80638a00b67a146106785780638b7d38a11461069f5780638d4f0b6c146106a75780638da5cb5b146106ce5780638eb9399e146106df5780638f32d59b1461071b575f80fd5b8063524fcc8011610251578063727d0f351161020b5780638113630d116101e65780638113630d1461062c57806382a15be11461063f5780638777e0951461065257806388edf9be14610665575f80fd5b8063727d0f35146105cb57806378a1bdd4146105f25780637f6ec45514610605575f80fd5b8063524fcc80146104b257806352d420b91461051657806353905402146105295780635c79696c1461055d57806364b4f7511461057d5780636bca7c55146105a4575f80fd5b80632591003a116102bc578063415c2d9611610297578063415c2d961461044157806346d62a631461046157806350283275146104885780635245b1ea14610490575f80fd5b80632591003a146103e857806338f3e2851461040f578063394ff4061461042e575f80fd5b8063070f418a146103035780630df9049b146103185780630ecc535f146103525780630f2f6aa5146103a657806315e5a1e5146103b95780631cd2a05d146103c1575b5f80fd5b61031661031136600461400f565b610997565b005b61033f7f000000000000000000000000000000000000000000000000000000000007e90081565b6040519081526020015b60405180910390f35b61038661036036600461403c565b60086020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610349565b6103166103b436600461403c565b6109a6565b61033f610b9f565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f7f00000000000000000000000000000000000000000000000000005af3107a400081565b61033f61041d36600461403c565b600b6020525f908152604090205481565b61031661043c366004614078565b610c0e565b61045461044f36600461410f565b610d19565b604051610349919061416f565b61033f7f0000000000000000000000000000000000000000000000000000000067884c0081565b61033f610e26565b6104a361049e3660046142e2565b610e7c565b60405161034993929190614390565b6104ee6104c036600461403c565b60096020525f9081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a001610349565b61031661052436600461400f565b611037565b61038661053736600461403c565b60056020525f908152604090208054600182015460028301546003909301549192909184565b610565611042565b6040516001600160a01b039091168152602001610349565b61033f7f00000000000000000000000000000000000000000000003635c9adc5dea0000081565b61033f7f0000000000000000000000000000000000000000000000000de0b6b3a764000181565b6105657f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab9881565b61031661060036600461403c565b6110ab565b6105657f0000000000000000000000006dea81c8171d0ba574754ef6f8b412f2ed88c54d81565b61031661063a3660046143b8565b6114d1565b61031661064d36600461445f565b611627565b61033f61066036600461400f565b611c57565b610316610673366004614519565b611d1f565b61033f7f00000000000000000000000000000000000000000000000000470de4df82000081565b61033f611daf565b6105657f0000000000000000000000005e17a49ec5efea6e54dab5468eb649b1d3bb572881565b6001546001600160a01b0316610565565b6106e7611dce565b604080518451815260209485015185820152835191810191909152929091015160608301521515608082015260a001610349565b6001546001600160a01b031633146040519015158152602001610349565b61033f611e7d565b6104ee61074f36600461456b565b600a60209081525f9283526040808420909152908252902080546001820154600283015460038401546004909401549293919290919085565b6104a361079636600461403c565b611ede565b61033f60025481565b61033f7f0000000000000000000000000000000000000000000000000000000000093a8081565b6003546004546107d9919082565b60408051928352602083019190915201610349565b61033f611f18565b61033f61080436600461459c565b611f26565b61081c61081736600461403c565b611f3c565b6040516103499291906145c5565b610316610838366004614605565b611fa3565b6107d961084b36600461403c565b6121a5565b61033f61085e36600461403c565b6122bc565b61056561087136600461403c565b612504565b61088961088436600461403c565b61253e565b60408051845181526020808601518183015285830151828401526060958601518683015284516080808401919091529085015160a08301529184015160c08201529383015160e085015290910151610100830152151561012082015261014001610349565b61033f7f000000000000000000000000000000000000000000000000000000000000000481565b6006546007546107d9919082565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b610316610958366004614605565b6126a9565b61031661096b366004614640565b6127be565b6105657f0000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d81565b6109a381600133611fa3565b50565b6109ae6127ce565b5f806109b86127f6565b915091505f806109c78561294e565b915091505f6109d886868585610e7c565b50909150600590508160068111156109f2576109f261437c565b14610a555760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a2063616e6e6f742d756e72656769737465722d696e604482015267697469617469766560c01b60648201526084015b60405180910390fd5b5f610a5e611e7d565b9050610a6b600182614677565b836080015110610a7d57610a7d61468a565b825185511015610a8f57610a8f61468a565b826020015185602001511015610aa757610aa761468a565b825185518690610ab8908390614677565b9052506020808401519086018051610ad1908390614677565b90525084516006556020808601516007556001600160a01b0388165f908152600b90915260408082205f1990555160248101839052610b459089906205573090849060440160408051601f198184030181529190526020810180516001600160e01b031663955161cd60e01b179052612a18565b90507f4b2a06fb31ab5670eed8302066f58582b640ec5a17d06ddd383d30e3e11f1b64888383610b75575f610b78565b60015b604051610b87939291906146b2565b60405180910390a1505050505050506109a360015f55565b5f7f0000000000000000000000000000000000000000000000000000000000093a806001610bcb611e7d565b610bd59190614677565b610bdf91906146d6565b610c09907f0000000000000000000000000000000000000000000000000000000067884c006146ed565b905090565b610c166127ce565b5f610c2085612a79565b90505f805f80846001600160a01b031663eb876bf78a338b8b8b6040518663ffffffff1660e01b8152600401610c5a959493929190614700565b6080604051808303815f875af1158015610c76573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9a91906147a4565b604080516001600160a01b038c168152602081018f9052908101859052606081018490526080810183905260a081018290529397509195509350915033907f5cd37895bb9287e542a426d6abbc93d7daca0979bd43264d320d4fd224a5fc5e9060c00160405180910390a25050505050610d1360015f55565b50505050565b6060816001600160401b03811115610d3357610d336141e7565b604051908082528060200260200182016040528015610d6657816020015b6060815260200190600190039081610d515790505b5090505f5b82811015610e1f575f8030868685818110610d8857610d886147d7565b9050602002810190610d9a91906147eb565b604051610da892919061482d565b5f60405180830381855af49150503d805f8114610de0576040519150601f19603f3d011682016040523d82523d5f602084013e610de5565b606091505b509150915081610df757805181602001fd5b80848481518110610e0a57610e0a6147d7565b60209081029190910101525050600101610d6b565b5092915050565b5f7f0000000000000000000000000000000000000000000000000000000000093a80610e727f0000000000000000000000000000000000000000000000000000000067884c0042614677565b610c099190614850565b6001600160a01b0384165f908152600b602052604081205481908190808203610eae575f805f9350935093505061102d565b5f610eb7611e7d565b9050808203610ed15760015f80945094509450505061102d565b6001600160a01b0389165f90815260096020526040902060040154935060018201610f055750600693505f915061102d9050565b610f10600182614677565b8410610f215760049450505061102d565b5f610f2e895f0151611c57565b905080885f0151118015610f46575060608801518851115b15610f7857885160025489515f9291610f5e916146d6565b610f689190614863565b60039750945061102d9350505050565b610f83600183614677565b7f00000000000000000000000000000000000000000000000000000000000000048860800151610fb391906146ed565b108061100d57508751606089015111801561100d5750670de0b6b3a7640000610ffc7f0000000000000000000000000000000000000000000000000de0b6b3a7640001836146d6565b6110069190614863565b8860600151115b156110225750600594505f925061102d915050565b50600294505f925050505b9450945094915050565b6109a3815f336126a9565b5f8061106e7f0000000000000000000000005e17a49ec5efea6e54dab5468eb649b1d3bb572833612b4e565b6040519091506001600160a01b0382169033907fda66ba232f4fb8c122b7026f55eeff1d0b9cf2560b7873b2bba6eaab4c3d5989905f90a3919050565b6110b36127ce565b5f6110bc611e7d565b90506002811161111f5760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a20726567697374726174696f6e2d6e6f742d7965746044820152670b595b98589b195960c21b6064820152608401610a4c565b6001600160a01b0382166111755760405162461bcd60e51b815260206004820152601860248201527f476f7665726e616e63653a207a65726f2d6164647265737300000000000000006044820152606401610a4c565b5f61117f83611ede565b509091505f90508160068111156111985761119861437c565b146111f75760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e69746961746976652d616c72656164792d726044820152681959da5cdd195c995960ba1b6064820152608401610a4c565b5f61120133612504565b90505f61120c6127f6565b50335f8181526008602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460608201529192506112a7907f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab986001600160a01b031690307f00000000000000000000000000000000000000000000003635c9adc5dea00000612bb9565b8151602082015160608301515f916112be916146ed565b9050670de0b6b3a76400006112f37f00000000000000000000000000000000000000000000000000005af3107a4000846146d6565b6112fd9190614863565b6040516305a4d3f160e21b81526001600160a01b038781166004830152611399917f0000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d909116906316934fc490602401602060405180830381865afa158015611367573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061138b9190614876565b611393610b9f565b84611f26565b10156113e75760405162461bcd60e51b815260206004820152601d60248201527f476f7665726e616e63653a20696e73756666696369656e742d6c7174790000006044820152606401610a4c565b6001600160a01b0388165f908152600b6020526040902087905561140c600188614677565b6001600160a01b0389165f9081526009602052604080822060040192909255905160248101899052611474908a90620557309084906044015b60408051601f198184030181529190526020810180516001600160e01b0316632695d74d60e01b179052612a18565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c89338a846114a5575f6114a8565b60015b6040516114b8949392919061488d565b60405180910390a150505050505050506109a360015f55565b6001546001600160a01b0316331461152b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610a4c565b5f5b815181101561161e576001600b5f84848151811061154d5761154d6147d7565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055505f6115af83838151811061158e5761158e6147d7565b6020026020010151620557305f600160405160240161144591815260200190565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c8383815181106115e4576115e46147d7565b6020026020010151336001846115fa575f6115fd565b60015b60405161160d949392919061488d565b60405180910390a15060010161152d565b506109a3612c20565b61162f6127ce565b848314801561163d57508281145b6116935760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a2061727261792d6c656e6774682d6d69736d6174636044820152600d60fb1b6064820152608401610a4c565b61169d8888612c69565b6116a78686612c69565b6116e28484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d4692505050565b61171d8282808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d4692505050565b6117888484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f92019190915250612db892505050565b6117f38484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f92019190915250612e5692505050565b5f6117fe8989612ef4565b335f9081526008602090815260409182902082516080810184528154815260018201549281019290925260028101549282018390526003015460608201529192501561187e5760405162461bcd60e51b815260206004820152600f60248201526e1b5d5cdd0818994818481c995cd95d608a1b6044820152606401610a4c565b80515f0361189e5760405162461bcd60e51b8152600401610a4c906148b9565b7f000000000000000000000000000000000000000000000000000000000007e9006118c7610e26565b1115611a3e575f5b87811015611a3c575f805b84518110156119c6578a8a848181106118f5576118f56147d7565b905060200201602081019061190a919061403c565b6001600160a01b0316858281518110611925576119256147d7565b60200260200101515f01516001600160a01b0316036119be5760019150848181518110611954576119546147d7565b602002602001015160200151898985818110611972576119726147d7565b9050602002013513156119b95760405162461bcd60e51b815260206004820152600f60248201526e43616e6e6f7420696e63726561736560881b6044820152606401610a4c565b6119c6565b6001016118da565b5080611a33578787838181106119de576119de6147d7565b905060200201355f14611a335760405162461bcd60e51b815260206004820181905260248201527f4d757374206265207a65726f20666f72206e657720696e6974696174697665736044820152606401610a4c565b506001016118cf565b505b5f876001600160401b03811115611a5757611a576141e7565b604051908082528060200260200182016040528015611a80578160200160208202803683370190505b5090505f886001600160401b03811115611a9c57611a9c6141e7565b604051908082528060200260200182016040528015611ac5578160200160208202803683370190505b5090505f5b89811015611ba057365f805f8c8c86818110611ae857611ae86147d7565b9050602002013513611afc57898986611b00565b8b8b875b9250925092505f838386818110611b1957611b196147d7565b9050602002013590505f885f0151828a60200151611b3791906146d6565b611b419190614863565b905081895f01818151611b549190614677565b905250602089018051829190611b6b908390614677565b90525082518190849088908110611b8457611b846147d7565b6020908102919091010152505060019093019250611aca915050565b50611c408a8a808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284375f9201919091525050604080516020808d0282810182019093528c82529093508c92508b9182918501908490808284375f9201919091525088925087915061336a9050565b50505050611c4d60015f55565b5050505050505050565b5f815f03611c6657505f919050565b5f8083670de0b6b3a7640000600254611c7f91906146d6565b611c899190614863565b90508015611cd15780611cc4670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006146d6565b611cce9190614863565b91505b611d17670de0b6b3a7640000611d077f00000000000000000000000000000000000000000000000000470de4df820000876146d6565b611d119190614863565b83613bf7565b949350505050565b611d276127ce565b611d318383612c69565b611d3b8383612ef4565b508015611da157335f9081526008602052604090206002015415611da15760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206d757374206265206120726573657400000000006044820152606401610a4c565b611daa60015f55565b505050565b5f80611db96127f6565b509050611dc8815f0151611c57565b91505090565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f80611dfe611e7d565b60408051808201825260035481526004546020808301919091528251808401909352600654835260075490830152955093509050611e3d600182614677565b84602001511015611e7757825160019250611e6490611e5a610b9f565b8560200151611f26565b8452611e71600182614677565b60208501525b50909192565b5f7f0000000000000000000000000000000000000000000000000000000000093a80611ec97f0000000000000000000000000000000000000000000000000000000067884c0042614677565b611ed39190614863565b610c099060016146ed565b5f805f80611eea6127f6565b5090505f80611ef88761294e565b91509150611f0887848484610e7c565b9550955095505050509193909250565b6003545f90611dc881611c57565b5f611f32848484613c0c565b90505b9392505050565b604080518082019091525f8082526020820152611f7660405180608001604052805f81526020015f81526020015f81526020015f81525090565b611f7e6127ce565b611f866127f6565b509150611f928361294e565b509050611f9e60015f55565b915091565b611fab6127ce565b335f81815260086020526040812091611fc390612504565b9050806001600160a01b03163b5f03611fee5760405162461bcd60e51b8152600401610a4c90614903565b81548511156120515760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e73756666696369656e742d756e616c6c6f63604482015268617465642d6c71747960b81b6064820152608401610a4c565b815485101561209957815460018301545f919061206e90886146d6565b6120789190614863565b905080836001015f82825461208d9190614677565b909155506120a0915050565b5f60018301555b84825f015f8282546120b29190614677565b9091555050604051633fdf42e360e01b81526004810186905284151560248201526001600160a01b0384811660448301525f9182918291829182918291881690633fdf42e39060640160c0604051808303815f875af1158015612117573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061213b9190614946565b955095509550955095509550336001600160a01b03167f2870fe177552976b0dbc3166ded2169d5d73275fc565df30894320fd3608f3398a88888888888860405161218c979695949392919061498c565b60405180910390a25050505050505050611daa60015f55565b5f805f6121b133612504565b9050806001600160a01b03163b5f036121dc5760405162461bcd60e51b8152600401610a4c90614903565b604051633fdf42e360e01b81525f60048201819052600160248301526001600160a01b0386811660448401529091829182918291861690633fdf42e39060640160c0604051808303815f875af1158015612238573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061225c9190614946565b604051929c509a50939750919550935090915033907f2870fe177552976b0dbc3166ded2169d5d73275fc565df30894320fd3608f339906122aa908b908890889088908e9089908f9061498c565b60405180910390a25050505050915091565b5f6122c56127ce565b5f6122ce6127f6565b5090505f806122dc8561294e565b915091505f806122ee87868686610e7c565b9193509091506003905082600681111561230a5761230a61437c565b1461231c575f955050505050506124f6565b6001612326611e7d565b6123309190614677565b8560200151146123425761234261468a565b600161234c611e7d565b6123569190614677565b6001600160a01b038881165f90815260096020526040808220600490810194909455516370a0823160e01b81523093810193909352917f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab98909116906370a0823190602401602060405180830381865afa1580156123d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123f99190614876565b905080821115612407578091505b61243b6001600160a01b037f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab98168984613c3a565b5f61249489620557305f8a6020015187604051602401612465929190918252602082015260400190565b60408051601f198184030181529190526020810180516001600160e01b0316630f0c6f0d60e11b179052612a18565b9050886001600160a01b03167f023acf63ebe0add874abd78d1a9b96f3384a3128bfdb97bf6c23e91f3d2e6e5a848960200151846124d2575f6124d5565b60015b6040516124e4939291906149c7565b60405180910390a25090955050505050505b6124ff60015f55565b919050565b5f6125387f0000000000000000000000005e17a49ec5efea6e54dab5468eb649b1d3bb57286001600160a01b038416613c6b565b92915050565b61256560405180608001604052805f81526020015f81526020015f81526020015f81525090565b61256d613edd565b5f80612577611e7d565b905060055f866001600160a01b03166001600160a01b031681526020019081526020015f206040518060800160405290815f82015481526020016001820154815260200160028201548152602001600382015481525050935060095f866001600160a01b03166001600160a01b031681526020019081526020015f206040518060a00160405290815f8201548152602001600182015481526020016002820154815260200160038201548152602001600482015481525050925060018161263e9190614677565b846020015110156126a157600191505f612656610b9f565b90505f61266b855f0151838760200151611f26565b90505f6126818660400151848860600151611f26565b828852606088018190529050612698600185614677565b60208801525050505b509193909250565b6126b16127ce565b5f6126bb84612a79565b6040516338fb359960e01b81526004810186905233602482015284151560448201526001600160a01b0384811660648301529192505f91829182918291908616906338fb3599906084016080604051808303815f875af1158015612721573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061274591906147a4565b604080516001600160a01b038c168152602081018e9052908101859052606081018490526080810183905260a081018290529397509195509350915033907f5cd37895bb9287e542a426d6abbc93d7daca0979bd43264d320d4fd224a5fc5e9060c00160405180910390a25050505050611daa60015f55565b6127ca82825f33610c0e565b5050565b60025f54036127f057604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f612825611dce565b919450925090508015612949578251600355602083015160049081556040516370a0823160e01b815230918101919091525f907f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab986001600160a01b0316906370a0823190602401602060405180830381865afa1580156128a7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128cb9190614876565b90507f000000000000000000000000000000000000000000000000000000000000000081106128fa57806128fc565b5f5b6002819055845160208087015160408051938452918301528101919091527f977a95eb227039fce4f8fe452d2d625ab67187ac5363991816ed06f4e5eed5019060600160405180910390a1505b509091565b61297560405180608001604052805f81526020015f81526020015f81526020015f81525090565b61297d613edd565b5f6129878461253e565b919450925090508015612a12576001600160a01b0384165f81815260056020908152604091829020865180825587830151600183018190558885015160028401556060808a01516003909401849055855192835293820192909252928301527f27b60fbfc71b45e0696ef86e3c31ac7979d464c139f6de0709de61b6b78093c5910160405180910390a25b50915091565b5f612a25846103e8613cca565b612a645760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b6044820152606401610a4c565b5f80835160208501868989f195945050505050565b5f808211612ac95760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a207a65726f2d6c7174792d616d6f756e74000000006044820152606401610a4c565b5f612ad333612504565b9050806001600160a01b03163b5f03612af057612aee611042565b505b335f90815260086020526040812080548392869291612b109084906146ed565b90915550612b20905084426146d6565b335f9081526008602052604081206001018054909190612b419084906146ed565b9091555090949350505050565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760095ff590506001600160a01b038116612538576040516330be1a3d60e21b815260040160405180910390fd5b6040516001600160a01b038481166024830152838116604483015260648201839052610d139186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613ce7565b6001546040515f916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b805f819003612c7757505050565b5f5b612c84600183614677565b811015610d13575f612c978260016146ed565b90505b82811015612d3d57848482818110612cb457612cb46147d7565b9050602002016020810190612cc9919061403c565b6001600160a01b0316858584818110612ce457612ce46147d7565b9050602002016020810190612cf9919061403c565b6001600160a01b031603612d355760405162461bcd60e51b815260206004820152600360248201526206475760ec1b6044820152606401610a4c565b600101612c9a565b50600101612c79565b80515f5b81811015611daa575f838281518110612d6557612d656147d7565b60200260200101511215612db05760405162461bcd60e51b815260206004820152601260248201527143616e6e6f74206265206e6567617469766560701b6044820152606401610a4c565b600101612d4a565b5f5b8251811015611daa575f838281518110612dd657612dd66147d7565b60200260200101511380612e0257505f828281518110612df857612df86147d7565b6020026020010151135b612e4e5760405162461bcd60e51b815260206004820152601a60248201527f476f7665726e616e63653a20766f74696e67206e6f7468696e670000000000006044820152606401610a4c565b600101612dba565b5f5b8251811015611daa57828181518110612e7357612e736147d7565b60200260200101515f1480612ea05750818181518110612e9557612e956147d7565b60200260200101515f145b612eec5760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63653a20766f74652d616e642d7665746f000000000000006044820152606401610a4c565b600101612e58565b60605f826001600160401b03811115612f0f57612f0f6141e7565b604051908082528060200260200182016040528015612f7657816020015b612f636040518060a001604052805f6001600160a01b031681526020015f81526020015f81526020015f81526020015f81525090565b815260200190600190039081612f2d5790505b5090505f836001600160401b03811115612f9257612f926141e7565b604051908082528060200260200182016040528015612fbb578160200160208202803683370190505b5090505f846001600160401b03811115612fd757612fd76141e7565b604051908082528060200260200182016040528015613000578160200160208202803683370190505b5090505f856001600160401b0381111561301c5761301c6141e7565b604051908082528060200260200182016040528015613045578160200160208202803683370190505b5090505f866001600160401b03811115613061576130616141e7565b60405190808252806020026020018201604052801561308a578160200160208202803683370190505b5090505f5b8781101561331c57335f908152600a60205260408120818b8b858181106130b8576130b86147d7565b90506020020160208101906130cd919061403c565b6001600160a01b0316815260208082019290925260409081015f20815160a081018352815480825260018301549482019490945260028201549281019290925260038101546060830152600401546080820152915015158061313257505f8160400151115b61317e5760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a206e6f7468696e6720746f207265736574000000006044820152606401610a4c565b6040518060a001604052808b8b8581811061319b5761319b6147d7565b90506020020160208101906131b0919061403c565b6001600160a01b03168152602001825f01518152602001826040015181526020018260200151815260200182606001518152508783815181106131f5576131f56147d7565b6020026020010181905250868281518110613212576132126147d7565b602002602001015160200151613227906149e2565b868381518110613239576132396147d7565b602002602001018181525050868281518110613257576132576147d7565b60200260200101516040015161326c906149e2565b85838151811061327e5761327e6147d7565b60200260200101818152505086828151811061329c5761329c6147d7565b6020026020010151606001516132b1906149e2565b8483815181106132c3576132c36147d7565b6020026020010181815250508682815181106132e1576132e16147d7565b6020026020010151608001516132f6906149e2565b838381518110613308576133086147d7565b60209081029190910101525060010161308f565b5061335e8888808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508892508791508690508561336a565b50929695505050505050565b613372613f07565b61337a6127f6565b60208301528152613389611e7d565b60e0820152335f9081526008602090815260408083208151608081018352815481526001820154938101939093526002810154838301526003015460608301528301525b8651811015613ae5575f8782815181106133e9576133e96147d7565b60200260200101519050868281518110613405576134056147d7565b602002602001015183610100018181525050858281518110613429576134296147d7565b6020908102919091010151610120840152610100830151151580613451575061012083015115155b61345d5761345d61468a565b84828151811061346f5761346f6147d7565b602002602001015183610140018181525050838281518110613493576134936147d7565b6020026020010151836101600181815250506134ae8161294e565b608085018190526060850182905284515f926134cd9285929190610e7c565b505090505f84610100015113806134e857505f846101200151135b156135865760028160068111156135015761350161437c565b148061351e5750600381600681111561351c5761351c61437c565b145b8061353a575060048160068111156135385761353861437c565b145b6135865760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206163746976652d766f74652d66736d00000000006044820152606401610a4c565b600681600681111561359a5761359a61437c565b036135fc575f846101000151131580156135b957505f84610120015113155b6135fc5760405162461bcd60e51b8152602060048201526014602482015273135d5cdd0818994818481dda5d1a191c985dd85b60621b6044820152606401610a4c565b6040805160a080820183526080808801805151845280516020908101519085015280518501519484019490945283516060908101519084015283518101519083015286015251516101008501516136539190613d48565b6080850180519190915251604001516101208501516136729190613d48565b6080850180516040019190915251602001516101408501516136949190613d48565b6080850180516020019190915251606001516101608501516136b69190613d48565b60808086018051606090810193909352516001600160a01b0385165f90815260096020908152604091829020835181559083015160018201559082015160028201559281015160038401550151600490910155600681600681111561371d5761371d61437c565b146137b75760a084015151602085015151101561373c5761373c61468a565b60a08401515160208501518051613754908390614677565b90525060a0840151602090810151818601519091018051613776908390614677565b905250608084015151602085015180516137919083906146ed565b90525060808401516020908101518186015190910180516137b39083906146ed565b9052505b335f908152600a602090815260408083206001600160a01b0386168452825291829020825160a0810184528154815260018201549281019283526002820154938101939093526003810154606084015260040154608083015260c0860191909152516101408501516138299190613d48565b60c08501805160200191909152516060015161016085015161384b9190613d48565b60c08501805160600191909152515161010085015161386a9190613d48565b60c0850180519190915251604001516101208501516138899190613d48565b60c0850180516040019190915260e085015181516080015251602081015190516138b49042906146d6565b10156138c2576138c261468a565b60c084015160608101516040909101516138dd9042906146d6565b10156138eb576138eb61468a565b60c0840151335f908152600a602090815260408083206001600160a01b03871684528252918290208351815590830151600182015581830151600282015560608301516003820155608090920151600490920191909155840151516101208501516101008601516139659291613960916149fc565b613d75565b60408501805191909152516020015161016085015161014086015161398e9291613960916149fc565b6040808601805160200192909252905101516101208501516101008601516139bf92916139ba916149fc565b613d48565b846040015160400181815250506139ee8460400151606001518561016001518661014001516139ba91906149fc565b846040015160600181815250505f8460c00151604001515f03613a8057613a6c83620557305f8860e00151338a604001518b60c001518c60800151604051602401613a3d959493929190614a23565b60408051601f198184030181529190526020810180516001600160e01b0316633064cba960e21b179052612a18565b613a76575f613a79565b60015b9050613a84565b5060025b61010085015161012086015160e08701516040516001600160a01b0387169333937f361311c5372a81d4ab4bce0bf1b7dbfc61da467667c2097deb69b4b24bc12d2893613ad2938890614ad3565b60405180910390a35050506001016133cd565b507f0000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d6001600160a01b03166316934fc4613b1e33612504565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015613b60573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b849190614876565b8160400151604001511115613bab5760405162461bcd60e51b8152600401610a4c906148b9565b6020808201518051600655810151600755604091820151335f90815260088352839020815181559181015160018301559182015160028201556060909101516003909101555050505050565b5f818311613c055781611f35565b5090919050565b5f80613c1884866146d6565b9050828111613c27575f613c31565b613c318382614677565b95945050505050565b6040516001600160a01b03838116602483015260448201839052611daa91859182169063a9059cbb90606401612bee565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101205f90611f35565b5f80603f83619c4001026040850201603f5a021015949350505050565b5f613cfb6001600160a01b03841683613d9b565b905080515f14158015613d1f575080806020019051810190613d1d9190614af5565b155b15611daa57604051635274afe760e01b81526001600160a01b0384166004820152602401610a4c565b5f80821215613d6b57613d5a82613da8565b613d649084614677565b9050612538565b611f3582846146ed565b5f80821215613d9157613d8782613da8565b613d6490846146ed565b611f358284614677565b6060611f3583835f613dbf565b5f808212613db65781612538565b612538826149e2565b606081471015613de45760405163cd78605960e01b8152306004820152602401610a4c565b5f80856001600160a01b03168486604051613dff9190614b10565b5f6040518083038185875af1925050503d805f8114613e39576040519150601f19603f3d011682016040523d82523d5f602084013e613e3e565b606091505b5091509150613e4e868383613e58565b9695505050505050565b606082613e6d57613e6882613eb4565b611f35565b8151158015613e8457506001600160a01b0384163b155b15613ead57604051639996b31560e01b81526001600160a01b0385166004820152602401610a4c565b5080611f35565b805115613ec45780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b604080516101c081019091525f61018082018181526101a083019190915281908152602001613f4760405180604001604052805f81526020015f81525090565b8152602001613f7360405180608001604052805f81526020015f81526020015f81526020015f81525090565b8152602001613f9f60405180608001604052805f81526020015f81526020015f81526020015f81525090565b8152602001613fac613edd565b8152602001613fb9613edd565b8152602001613feb6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6020828403121561401f575f80fd5b5035919050565b80356001600160a01b03811681146124ff575f80fd5b5f6020828403121561404c575f80fd5b611f3582614026565b5f60e08284031215614065575f80fd5b50919050565b80151581146109a3575f80fd5b5f805f80610140858703121561408c575f80fd5b8435935061409d8660208701614055565b92506101008501356140ae8161406b565b91506140bd6101208601614026565b905092959194509250565b5f8083601f8401126140d8575f80fd5b5081356001600160401b038111156140ee575f80fd5b6020830191508360208260051b8501011115614108575f80fd5b9250929050565b5f8060208385031215614120575f80fd5b82356001600160401b03811115614135575f80fd5b614141858286016140c8565b90969095509350505050565b5f5b8381101561416757818101518382015260200161414f565b50505f910152565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b828110156141da57878503603f19018452815180518087526141bb818989018a850161414d565b601f01601f191695909501860194509285019290850190600101614194565b5092979650505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b038111828210171561421d5761421d6141e7565b60405290565b604051608081016001600160401b038111828210171561421d5761421d6141e7565b604051601f8201601f191681016001600160401b038111828210171561426d5761426d6141e7565b604052919050565b5f60a08284031215614285575f80fd5b60405160a081018181106001600160401b03821117156142a7576142a76141e7565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b5f805f808486036101808112156142f7575f80fd5b61430086614026565b94506040601f1982011215614313575f80fd5b61431b6141fb565b602087810135825260408801359082015293506080605f198201121561433f575f80fd5b50614348614223565b60608681013582526080870135602083015260a0870135604083015260c08701359082015291506140bd8660e08701614275565b634e487b7160e01b5f52602160045260245ffd5b60608101600785106143a4576143a461437c565b938152602081019290925260409091015290565b5f60208083850312156143c9575f80fd5b82356001600160401b03808211156143df575f80fd5b818501915085601f8301126143f2575f80fd5b813581811115614404576144046141e7565b8060051b9150614415848301614245565b818152918301840191848101908884111561442e575f80fd5b938501935b838510156144535761444485614026565b82529385019390850190614433565b98975050505050505050565b5f805f805f805f806080898b031215614476575f80fd5b88356001600160401b038082111561448c575f80fd5b6144988c838d016140c8565b909a50985060208b01359150808211156144b0575f80fd5b6144bc8c838d016140c8565b909850965060408b01359150808211156144d4575f80fd5b6144e08c838d016140c8565b909650945060608b01359150808211156144f8575f80fd5b506145058b828c016140c8565b999c989b5096995094979396929594505050565b5f805f6040848603121561452b575f80fd5b83356001600160401b03811115614540575f80fd5b61454c868287016140c8565b90945092505060208401356145608161406b565b809150509250925092565b5f806040838503121561457c575f80fd5b61458583614026565b915061459360208401614026565b90509250929050565b5f805f606084860312156145ae575f80fd5b505081359360208301359350604090920135919050565b825181526020808401519082015260c08101611f356040830184805182526020810151602083015260408101516040830152606081015160608301525050565b5f805f60608486031215614617575f80fd5b8335925060208401356146298161406b565b915061463760408501614026565b90509250925092565b5f806101008385031215614652575f80fd5b823591506145938460208501614055565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561253857612538614663565b634e487b7160e01b5f52600160045260245ffd5b600381106146ae576146ae61437c565b9052565b6001600160a01b03841681526020810183905260608101611d17604083018461469e565b808202811582820484141761253857612538614663565b8082018082111561253857612538614663565b8581526001600160a01b0385811660208301526101608201908061472387614026565b1660408401528061473660208801614026565b1660608401525060408501356080830152606085013560a0830152608085013560ff81168114614764575f80fd5b60ff1660c08381019190915260a086013560e084015294909401356101008201529115156101208301526001600160a01b03166101409091015292915050565b5f805f80608085870312156147b7575f80fd5b505082516020840151604085015160609095015191969095509092509050565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e19843603018112614800575f80fd5b8301803591506001600160401b03821115614819575f80fd5b602001915036819003821315614108575f80fd5b818382375f9101908152919050565b634e487b7160e01b5f52601260045260245ffd5b5f8261485e5761485e61483c565b500690565b5f826148715761487161483c565b500490565b5f60208284031215614886575f80fd5b5051919050565b6001600160a01b038581168252841660208201526040810183905260808101613c31606083018461469e565b6020808252602a908201527f476f7665726e616e63653a20696e73756666696369656e742d6f722d616c6c6f60408201526963617465642d6c71747960b01b606082015260800190565b60208082526023908201527f476f7665726e616e63653a20757365722d70726f78792d6e6f742d6465706c6f6040820152621e595960ea1b606082015260800190565b5f805f805f8060c0878903121561495b575f80fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6001600160a01b03979097168752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b8381526020810183905260608101611d17604083018461469e565b5f600160ff1b82016149f6576149f6614663565b505f0390565b8082018281125f831280158216821582161715614a1b57614a1b614663565b505092915050565b8581526001600160a01b03851660208201526102008101614a686040830186805182526020810151602083015260408101516040830152606081015160608301525050565b835160c0830152602084015160e08301526040840151610100830152606084015161012083015260808401516101408301528251610160830152602083015161018083015260408301516101a083015260608301516101c083015260808301516101e0830152613e4e565b848152602081018490526040810183905260808101613c31606083018461469e565b5f60208284031215614b05575f80fd5b8151611f358161406b565b5f8251614b2181846020870161414d565b919091019291505056fea264697066735822122068679bfa2c2eee9124643e597f74c2abf6b9406391adb4f8cd43a688cc2b27df64736f6c63430008180033

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

0000000000000000000000006dea81c8171d0ba574754ef6f8b412f2ed88c54d0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab9800000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000005af3107a40000000000000000000000000000000000000000000000000000de0b6b3a7640001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067884c000000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000007e900000000000000000000000000bec25c5590e89596bde2dfcdc71579e66858772c00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _lqty (address): 0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D
Arg [1] : _lusd (address): 0x5f98805A4E8be255a32880FDeC7F6728C6568bA0
Arg [2] : _stakingV1 (address): 0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d
Arg [3] : _bold (address): 0xb01dd87B29d187F3E3a4Bf6cdAebfb97F3D9aB98
Arg [4] : _config (tuple):
Arg [1] : registrationFee (uint256): 1000000000000000000000
Arg [2] : registrationThresholdFactor (uint256): 100000000000000
Arg [3] : unregistrationThresholdFactor (uint256): 1000000000000000001
Arg [4] : unregistrationAfterEpochs (uint256): 4
Arg [5] : votingThresholdFactor (uint256): 20000000000000000
Arg [6] : minClaim (uint256): 0
Arg [7] : minAccrual (uint256): 0
Arg [8] : epochStart (uint256): 1736985600
Arg [9] : epochDuration (uint256): 604800
Arg [10] : epochVotingCutoff (uint256): 518400

Arg [5] : _owner (address): 0xbEC25C5590e89596BDE2DfCdc71579E66858772c
Arg [6] : _initiatives (address[]):

-----Encoded View---------------
17 Constructor Arguments found :
Arg [0] : 0000000000000000000000006dea81c8171d0ba574754ef6f8b412f2ed88c54d
Arg [1] : 0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0
Arg [2] : 0000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d
Arg [3] : 000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab98
Arg [4] : 00000000000000000000000000000000000000000000003635c9adc5dea00000
Arg [5] : 00000000000000000000000000000000000000000000000000005af3107a4000
Arg [6] : 0000000000000000000000000000000000000000000000000de0b6b3a7640001
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [8] : 00000000000000000000000000000000000000000000000000470de4df820000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000067884c00
Arg [12] : 0000000000000000000000000000000000000000000000000000000000093a80
Arg [13] : 000000000000000000000000000000000000000000000000000000000007e900
Arg [14] : 000000000000000000000000bec25c5590e89596bde2dfcdc71579e66858772c
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000000


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ 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.