Address Details
contract

0xF76329E2398B2D2707DD06Ef6EfA729cd52f73f2

Contract Name
DefaultStrategy
Creator
0x5bc1c4–68a788 at 0x70bc68–233c0d
Balance
0 CELO ( )
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
17756922
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
DefaultStrategy




Optimization enabled
false
Compiler version
v0.8.11+commit.d7f03943




EVM Version
istanbul




Verified at
2023-05-12T14:53:16.779092Z

contracts/DefaultStrategy.sol

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

import "./common/UUPSOwnableUpgradeable.sol";
import "./common/linkedlists/AddressSortedLinkedList.sol";
import "./interfaces/IAccount.sol";
import "./interfaces/IGroupHealth.sol";
import "./interfaces/IManager.sol";
import "./interfaces/ISpecificGroupStrategy.sol";
import "./Managed.sol";

/**
 * @title DefaultStrategy is responsible for handling any deposit/withdrawal
 * for accounts without any specific strategy.
 */
contract DefaultStrategy is UUPSOwnableUpgradeable, Managed {
    using EnumerableSet for EnumerableSet.AddressSet;
    using AddressSortedLinkedList for SortedLinkedList.List;

    /**
     * @notice Holds a group's address and votes.
     * @param group The address of the group.
     * @param votes The votes assigned to the group.
     */
    struct GroupWithVotes {
        address group;
        uint256 votes;
    }

    /**
     * @notice The set of currently active groups that will be voted for with
     * new deposits.
     */
    SortedLinkedList.List private activeGroups;

    /**
     * @notice An instance of the GroupHealth contract for the StakedCelo protocol.
     */
    IGroupHealth public groupHealth;

    /**
     * @notice An instance of the Account contract for the StakedCelo protocol.
     */
    IAccount public account;

    /**
     * @notice An instance of the SpecificGroupStrategy for the StakedCelo protocol.
     */
    ISpecificGroupStrategy public specificGroupStrategy;

    /**
     * @notice StCELO that was cast for default group strategy,
     * strategy => stCELO amount.
     */
    mapping(address => uint256) public stCeloInGroup;

    /**
     * @notice Maximum number of groups to distribute votes to.
     */
    uint256 public maxGroupsToDistributeTo;

    /**
     * @notice Maximum number of groups to withdraw from.
     */
    uint256 public maxGroupsToWithdrawFrom;

    /**
     * @notice Total stCELO that was voted with on default strategy.
     */
    uint256 public totalStCeloInStrategy;

    /**
     * @notice Loop limit while sorting active groups on chain.
     */
    uint256 private sortingLoopLimit;

    /**
     * @notice Whether or not active groups are sorted.
     * If active groups are not sorted it is neccessary to call updateActiveGroupOrder.
     */
    bool public sorted;

    /**
     * @notice Groups that need to be sorted.
     */
    EnumerableSet.AddressSet private unsortedGroups;

    /**
     * @notice Emitted when a group is deactivated.
     * @param group The group's address.
     */
    event GroupRemoved(address indexed group);

    /**
     * @notice Emitted when a new group is activated for voting.
     * @param group The group's address.
     */
    event GroupActivated(address indexed group);

    /**
     * Emmited when sorted status of active groups was changed.
     * @param update The new value.
     */
    event SortedFlagUpdated(bool update);

    /**
     * @notice Used when attempting to activate a group that is already active.
     * @param group The group's address.
     */
    error GroupAlreadyAdded(address group);

    /**
     * @notice Used when a group does not meet the validator group health requirements.
     * @param group The group's address.
     */
    error GroupNotEligible(address group);

    /**
     * @notice Used when attempting to deactivate a group that is not active.
     * @param group The group's address.
     */
    error GroupNotActive(address group);

    /**
     * @notice Used when attempting to deactivate a healthy group using deactivateUnhealthyGroup().
     * @param group The group's address.
     */
    error HealthyGroup(address group);

    /**
     * @notice Used when attempting to deposit when there are no active groups
     * to vote for.
     */
    error NoActiveGroups();

    /**
     * @notice Used when atempting to distribute votes but validator group limit is reached.
     */
    error NotAbleToDistributeVotes();

    /**
     * @notice Used when attempting sort active groups when there are no unsorted group.
     */
    error NotUnsortedGroup();

    /**
     * @notice Used when rebalancing to a non-active group.
     * @param group The group's address.
     */
    error InvalidToGroup(address group);

    /**
     * @notice Used when rebalancing from non-active group.
     * @param group The group's address.
     */
    error InvalidFromGroup(address group);

    /**
     * @notice Used when rebalancing and `fromGroup` doesn't have any extra stCELO.
     * @param group The group's address.
     * @param actualCelo The actual stCELO value.
     * @param expectedCelo The expected stCELO value.
     */
    error RebalanceNoExtraStCelo(address group, uint256 actualCelo, uint256 expectedCelo);

    /**
     * @notice Used when rebalancing and `toGroup` has enough stCELO.
     * @param group The group's address.
     * @param actualCelo The actual stCELO value.
     * @param expectedCelo The expected stCELO value.
     */
    error RebalanceEnoughStCelo(address group, uint256 actualCelo, uint256 expectedCelo);

    /**
     * @notice Used when attempting to pass in address zero where not allowed.
     */
    error AddressZeroNotAllowed();

    /**
     *  @notice Used when a `managerOrStrategy` function is called
     *  by a non-manager or non-strategy.
     *  @param caller `msg.sender` that called the function.
     */
    error CallerNotManagerNorStrategy(address caller);

    /**
     * @notice Checks that only the manager or strategy contract can execute a function.
     */
    modifier managerOrStrategy() {
        if (manager != msg.sender && address(specificGroupStrategy) != msg.sender) {
            revert CallerNotManagerNorStrategy(msg.sender);
        }
        _;
    }

    /**
     * @notice Empty constructor for proxy implementation, `initializer` modifer ensures the
     * implementation gets initialized.
     */
    // solhint-disable-next-line no-empty-blocks
    constructor() initializer {}

    /**
     * @notice Initialize the contract with registry and owner.
     * @param _owner The address of the contract owner.
     * @param _manager The address of the Manager contract.
     */
    function initialize(address _owner, address _manager) external initializer {
        _transferOwnership(_owner);
        __Managed_init(_manager);
        maxGroupsToDistributeTo = 8;
        maxGroupsToWithdrawFrom = 8;
        sortingLoopLimit = 10;
        sorted = true;
        emit SortedFlagUpdated(sorted);
    }

    /**
     * @notice Set this contract's dependencies in the StakedCelo system.
     * @param _account The address of the Account contract.
     * @param _groupHealth The address of the GroupHealth contract.
     * @param _specificGroupStrategy The address of the SpecificGroupStrategy contract.
     */
    function setDependencies(
        address _account,
        address _groupHealth,
        address _specificGroupStrategy
    ) external onlyOwner {
        if (
            _account == address(0) ||
            _groupHealth == address(0) ||
            _specificGroupStrategy == address(0)
        ) {
            revert AddressZeroNotAllowed();
        }

        groupHealth = IGroupHealth(_groupHealth);
        specificGroupStrategy = ISpecificGroupStrategy(_specificGroupStrategy);
        account = IAccount(_account);
    }

    /**
     * @notice Set distribution/withdrawal algorithm parameters.
     * @param distributeTo Maximum number of groups that can be distributed to.
     * @param withdrawFrom Maximum number of groups that can be withdrawn from.
     * @param loopLimit The sorting loop limit while sorting active groups on chain.
     */
    function setSortingParams(
        uint256 distributeTo,
        uint256 withdrawFrom,
        uint256 loopLimit
    ) external onlyOwner {
        maxGroupsToDistributeTo = distributeTo;
        maxGroupsToWithdrawFrom = withdrawFrom;
        sortingLoopLimit = loopLimit;
    }

    /**
     * @notice Distributes votes by computing the number of votes each active
     * group should receive.
     * @param celoAmount The amount of votes to distribute.
     * @param depositGroupToIgnore The group that will not be used for deposit.
     * @return finalGroups The groups that were chosen for distribution.
     * @return finalVotes The votes of chosen finalGroups.
     */
    function generateDepositVoteDistribution(uint256 celoAmount, address depositGroupToIgnore)
        external
        managerOrStrategy
        returns (address[] memory finalGroups, uint256[] memory finalVotes)
    {
        return _generateDepositVoteDistribution(celoAmount, depositGroupToIgnore);
    }

    /**
     * @notice Updates group order of unsorted group. When there are no more unsorted groups
     * it will mark active groups as sorted.
     * @param group The group address.
     * @param lesserKey The key of the group less than the group to update.
     * @param greaterKey The key of the group greater than the group to update.
     */
    function updateActiveGroupOrder(
        address group,
        address lesserKey,
        address greaterKey
    ) external {
        if (!unsortedGroups.contains(group)) {
            revert NotUnsortedGroup();
        }

        activeGroups.update(group, stCeloInGroup[group], lesserKey, greaterKey);
        unsortedGroups.remove(group);
        if (unsortedGroups.length() == 0) {
            sorted = true;
            emit SortedFlagUpdated(sorted);
        }
    }

    /**
     * @notice Marks a group as votable for default strategy.
     * It is necessary to call `updateGroupHealth` in GroupHealth smart contract first.
     * @param group The address of the group to add to the set of votable
     * groups.
     * @param lesser The group receiving fewer votes (in default strategy) than `group`,
     * or 0 if `group` has the fewest votes of any validator group.
     * @param greater The group receiving more votes (in default strategy) than `group`,
     *  or 0 if `group` has the most votes of any validator group.
     */
    function activateGroup(
        address group,
        address lesser,
        address greater
    ) external onlyOwner {
        if (!groupHealth.isGroupValid(group)) {
            revert GroupNotEligible(group);
        }

        if (activeGroups.contains(group)) {
            revert GroupAlreadyAdded(group);
        }

        // For migration purposes between V1 and V2. It can be removed once migrated to V2.
        uint256 currentStCelo = 0;
        uint256 stCeloForWholeGroup = IManager(manager).toStakedCelo(
            account.getCeloForGroup(group)
        );

        if (stCeloForWholeGroup != 0) {
            (uint256 specificGroupTotalStCelo, , ) = specificGroupStrategy.getStCeloInGroup(group);
            currentStCelo =
                stCeloForWholeGroup -
                Math.min(stCeloForWholeGroup, specificGroupTotalStCelo);
            updateGroupStCelo(group, currentStCelo, true);
        }

        activeGroups.insert(group, currentStCelo, lesser, greater);

        emit GroupActivated(group);
    }

    /**
     * @notice Rebalances CELO between groups that have an incorrect CELO-stCELO ratio.
     * `fromGroup` is required to have more CELO than it should and `toGroup` needs
     * to have less CELO than it should.
     * @param fromGroup The from group.
     * @param toGroup The to group.
     */
    function rebalance(address fromGroup, address toGroup) external {
        if (!activeGroups.contains(fromGroup)) {
            revert InvalidFromGroup(fromGroup);
        }

        if (!activeGroups.contains(toGroup)) {
            revert InvalidToGroup(toGroup);
        }

        (uint256 expectedFromStCelo, uint256 actualFromStCelo) = getExpectedAndActualStCeloForGroup(
            fromGroup
        );
        if (actualFromStCelo <= expectedFromStCelo) {
            // fromGroup needs to have more stCELO than it should
            revert RebalanceNoExtraStCelo(fromGroup, actualFromStCelo, expectedFromStCelo);
        }

        (uint256 expectedToStCelo, uint256 actualToStCelo) = getExpectedAndActualStCeloForGroup(
            toGroup
        );

        if (actualToStCelo >= expectedToStCelo) {
            // toGroup needs to have less stCELO than it should
            revert RebalanceEnoughStCelo(toGroup, actualToStCelo, expectedToStCelo);
        }

        uint256 toMove = Math.min(
            actualFromStCelo - expectedFromStCelo,
            expectedToStCelo - actualToStCelo
        );

        updateGroupStCelo(fromGroup, toMove, false);
        updateGroupStCelo(toGroup, toMove, true);

        trySort(fromGroup, stCeloInGroup[fromGroup], false);
        trySort(toGroup, stCeloInGroup[toGroup], true);
    }

    /**
     * @notice Distributes votes by computing the number of votes to be subtracted
     * from each active group.
     * @param celoAmount The amount of votes to subtract.
     * @return finalGroups The groups that were chosen for subtraction.
     * @return finalVotes The votes of chosen finalGroups.
     */
    function generateWithdrawalVoteDistribution(uint256 celoAmount)
        external
        managerOrStrategy
        returns (address[] memory finalGroups, uint256[] memory finalVotes)
    {
        if (activeGroups.getNumElements() == 0) {
            revert NoActiveGroups();
        }

        uint256 maxGroupCount = Math.min(maxGroupsToWithdrawFrom, activeGroups.getNumElements());

        address[] memory groups = new address[](maxGroupCount);
        uint256[] memory votes = new uint256[](maxGroupCount);

        address votedGroup = activeGroups.getHead();
        uint256 groupsIndex;

        while (groupsIndex < maxGroupCount && celoAmount != 0 && votedGroup != address(0)) {
            votes[groupsIndex] = Math.min(
                Math.min(
                    account.getCeloForGroup(votedGroup),
                    IManager(manager).toCelo(stCeloInGroup[votedGroup])
                ),
                celoAmount
            );

            groups[groupsIndex] = votedGroup;
            celoAmount -= votes[groupsIndex];
            updateGroupStCelo(
                votedGroup,
                IManager(manager).toStakedCelo(votes[groupsIndex]),
                false
            );
            trySort(votedGroup, stCeloInGroup[votedGroup], false);

            if (sorted) {
                votedGroup = activeGroups.getHead();
            } else {
                (, votedGroup, ) = activeGroups.get(votedGroup);
            }

            groupsIndex++;
        }

        if (celoAmount != 0) {
            revert NotAbleToDistributeVotes();
        }

        finalGroups = new address[](groupsIndex);
        finalVotes = new uint256[](groupsIndex);

        for (uint256 i = 0; i < groupsIndex; i++) {
            finalGroups[i] = groups[i];
            finalVotes[i] = votes[i];
        }
    }

    /**
     * @notice Deactivates group.
     * @param group The group to deactivated.
     */
    function deactivateGroup(address group) external onlyOwner {
        _deactivateGroup(group);
    }

    /**
     * @notice Deactivates an unhealthy group.
     * @param group The group to deactivate if unhealthy.
     */
    function deactivateUnhealthyGroup(address group) external {
        if (groupHealth.isGroupValid(group)) {
            revert HealthyGroup(group);
        }
        _deactivateGroup((group));
    }

    /**
     * @notice Returns the number of active groups.
     * @return The number of active groups.
     */
    function getNumberOfGroups() external view returns (uint256) {
        return activeGroups.getNumElements();
    }

    /**
     * @notice Returns previous and next address of key.
     * @param group The group address.
     * @return previousAddress The previous address.
     * @return nextAddress The next address.
     */
    function getGroupPreviousAndNext(address group)
        external
        view
        returns (address previousAddress, address nextAddress)
    {
        (, previousAddress, nextAddress) = activeGroups.get(group);
    }

    /**
     * @notice Returns head and previous address of head.
     * @return head The address of the sorted group with most votes.
     * @return previousAddress The previous address from head.
     */
    function getGroupsHead() external view returns (address head, address previousAddress) {
        head = activeGroups.getHead();
        (, previousAddress, ) = activeGroups.get(head);
    }

    /**
     * @notice Returns tail and next address of tail.
     * @return tail The address of the sorted group with least votes.
     * @return nextAddress The next address after tail.
     */
    function getGroupsTail() external view returns (address tail, address nextAddress) {
        tail = activeGroups.getTail();
        (, , nextAddress) = activeGroups.get(tail);
    }

    /**
     * @notice Returns whether active groups contain group.
     * @return Whether or not the given group is active.
     */
    function isActive(address group) external view returns (bool) {
        return activeGroups.contains(group);
    }

    /**
     * @notice Returns the number of unsorted groups.
     * @return The number of unsorted groups.
     */
    function getNumberOfUnsortedGroups() external view returns (uint256) {
        return unsortedGroups.length();
    }

    /**
     * @notice Returns the unsorted group at index.
     * @param index The index to look up.
     * @return The group.
     */
    function getUnsortedGroupAt(uint256 index) external view returns (address) {
        return unsortedGroups.at(index);
    }

    /**
     * @notice Returns the storage, major, minor, and patch version of the contract.
     * @return Storage version of the contract.
     * @return Major version of the contract.
     * @return Minor version of the contract.
     * @return Patch version of the contract.
     */
    function getVersionNumber()
        external
        pure
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        return (1, 1, 0, 0);
    }

    /**
     * @notice Returns expected stCELO and actual stCELO for group.
     * @param group The group.
     * @return expectedStCelo The amount of stCELO that group should have.
     * (The total amount of stCELO in the default strategy divided by the number of active groups.)
     * @return actualStCelo The amount of stCELO which is currently
     * assigned to group in the strategy.
     */
    function getExpectedAndActualStCeloForGroup(address group)
        public
        view
        returns (uint256 expectedStCelo, uint256 actualStCelo)
    {
        address head = activeGroups.getHead();
        uint256 numberOfActiveGroups = activeGroups.getNumElements();
        expectedStCelo = totalStCeloInStrategy / numberOfActiveGroups;
        if (group == head) {
            uint256 divisionResidue = totalStCeloInStrategy -
                (expectedStCelo * numberOfActiveGroups);
            expectedStCelo += divisionResidue;
        }

        actualStCelo = stCeloInGroup[group];
    }

    /**
     * @notice Adds/substracts value to totals of group and
     * total stCELO in default strategy.
     * @param group The validator group that we are updating.
     * @param stCeloAmount The amount of stCELO.
     * @param add Whether to add or substract.
     */
    function updateGroupStCelo(
        address group,
        uint256 stCeloAmount,
        bool add
    ) internal {
        if (add) {
            stCeloInGroup[group] += stCeloAmount;
            totalStCeloInStrategy += stCeloAmount;
        } else {
            stCeloInGroup[group] -= stCeloAmount;
            totalStCeloInStrategy -= stCeloAmount;
        }
    }

    /**
     * @notice Deactivates group.
     * @param group The group to deactivated.
     */
    function _deactivateGroup(address group) private {
        if (!activeGroups.contains(group)) {
            revert GroupNotActive(group);
        }
        activeGroups.remove(group);
        unsortedGroups.remove(group);

        uint256 groupTotalStCeloVotes = stCeloInGroup[group];

        if (groupTotalStCeloVotes > 0) {
            updateGroupStCelo(group, groupTotalStCeloVotes, false);
            address[] memory fromGroups = new address[](1);
            uint256[] memory fromVotes = new uint256[](1);
            fromGroups[0] = group;
            fromVotes[0] = IManager(manager).toCelo(groupTotalStCeloVotes);
            (
                address[] memory toGroups,
                uint256[] memory toVotes
            ) = _generateDepositVoteDistribution(
                    IManager(manager).toCelo(groupTotalStCeloVotes),
                    address(0)
                );
            IManager(manager).scheduleTransferWithinStrategy(
                fromGroups,
                toGroups,
                fromVotes,
                toVotes
            );
        }

        emit GroupRemoved(group);
    }

    /**
     * @notice Distributes votes by computing the number of votes each active
     * group should receive.
     * @param celoAmount The amount of votes to distribute.
     * @param depositGroupToIgnore The group that will not be used for deposit.
     * @return finalGroups The groups that were chosen for distribution.
     * @return finalVotes The votes of chosen finalGroups.
     */
    function _generateDepositVoteDistribution(uint256 celoAmount, address depositGroupToIgnore)
        private
        returns (address[] memory finalGroups, uint256[] memory finalVotes)
    {
        if (activeGroups.getNumElements() == 0) {
            revert NoActiveGroups();
        }

        uint256 maxGroupCount = Math.min(maxGroupsToDistributeTo, activeGroups.getNumElements());

        address[] memory groups = new address[](maxGroupCount);
        uint256[] memory votes = new uint256[](maxGroupCount);

        address votedGroup = activeGroups.getTail();
        uint256 groupsIndex;

        while (groupsIndex < maxGroupCount && celoAmount != 0 && votedGroup != address(0)) {
            uint256 receivableVotes = IManager(manager).getReceivableVotesForGroup(votedGroup);
            if (votedGroup == depositGroupToIgnore || receivableVotes == 0) {
                (, , votedGroup) = activeGroups.get(votedGroup);
                continue;
            }

            votes[groupsIndex] = Math.min(receivableVotes, celoAmount);
            groups[groupsIndex] = votedGroup;
            celoAmount -= votes[groupsIndex];
            updateGroupStCelo(votedGroup, IManager(manager).toStakedCelo(votes[groupsIndex]), true);
            trySort(votedGroup, stCeloInGroup[votedGroup], true);

            if (sorted) {
                votedGroup = activeGroups.getTail();
            } else {
                (, , votedGroup) = activeGroups.get(votedGroup);
            }
            groupsIndex++;
        }

        if (celoAmount != 0) {
            revert NotAbleToDistributeVotes();
        }

        finalGroups = new address[](groupsIndex);
        finalVotes = new uint256[](groupsIndex);

        for (uint256 i = 0; i < groupsIndex; i++) {
            finalGroups[i] = groups[i];
            finalVotes[i] = votes[i];
        }
    }

    /**
     * Try to sort group in active groups based on new value.
     * @param group The group address.
     * @param newValue The new value of group.
     * @param valueIncreased Whether value increased/decreased compared to original value.
     */
    function trySort(
        address group,
        uint256 newValue,
        bool valueIncreased
    ) private {
        if (unsortedGroups.contains(group)) {
            return;
        }

        (address lesserKey, address greaterKey) = valueIncreased
            ? activeGroups.getLesserAndGreaterOfAddressThatIncreasedValue(
                group,
                newValue,
                sortingLoopLimit
            )
            : activeGroups.getLesserAndGreaterOfAddressThatDecreasedValue(
                group,
                newValue,
                sortingLoopLimit
            );
        if (lesserKey != greaterKey || activeGroups.getNumElements() == 1) {
            activeGroups.update(group, newValue, lesserKey, greaterKey);
        } else {
            if (sorted) {
                sorted = false;
            }
            unsortedGroups.add(group);
        }
    }
}
        

/_openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeacon.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967Upgrade {
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallSecure(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        address oldImplementation = _getImplementation();

        // Initial upgrade and setup call
        _setImplementation(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }

        // Perform rollback test if not already in progress
        StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
        if (!rollbackTesting.value) {
            // Trigger rollback using upgradeTo from the new implementation
            rollbackTesting.value = true;
            Address.functionDelegateCall(
                newImplementation,
                abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
            );
            rollbackTesting.value = false;
            // Check rollback was effective
            require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
            // Finally reset to the new implementation and log the upgrade
            _upgradeTo(newImplementation);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            Address.isContract(IBeacon(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        }
    }
}
          

/_openzeppelin/contracts/proxy/beacon/IBeacon.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}
          

/_openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.0;

import "../ERC1967/ERC1967Upgrade.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 *
 * _Available since v4.1._
 */
abstract contract UUPSUpgradeable is ERC1967Upgrade {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
    address private immutable __self = address(this);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeTo(address newImplementation) external virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallSecure(newImplementation, new bytes(0), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallSecure(newImplementation, data, true);
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeTo} and {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal override onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;
}
          

/_openzeppelin/contracts/utils/Address.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @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, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * 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.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @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`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
          

/_openzeppelin/contracts/utils/StorageSlot.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly {
            r.slot := slot
        }
    }
}
          

/_openzeppelin/contracts/utils/math/Math.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }
}
          

/_openzeppelin/contracts/utils/structs/EnumerableSet.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly {
            result := store
        }

        return result;
    }
}
          

/_openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * 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.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
          

/_openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}
          

/_openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @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, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * 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.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @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`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
          

/_openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}
          

/contracts/Managed.sol

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/**
 * @title Used via inheritance to grant special access control to the Manager
 * contract.
 */
abstract contract Managed is Initializable, OwnableUpgradeable {
    address public manager;

    /**
     * @notice Emitted when the manager is initially set or later modified.
     * @param manager The new managing account address.
     */
    event ManagerSet(address indexed manager);

    /**
     *  @notice Used when an `onlyManager` function is called by a non-manager.
     *  @param caller `msg.sender` that called the function.
     */
    error CallerNotManager(address caller);

    /**
     * @notice Used when a passed address is address(0).
     */
    error NullAddress();

    /**
     * @dev Throws if called by any account other than the manager.
     */
    modifier onlyManager() {
        if (manager != msg.sender) {
            revert CallerNotManager(msg.sender);
        }
        _;
    }

    /**
     * @notice Sets the manager address.
     * @param _manager The new manager address.
     */
    function setManager(address _manager) external onlyOwner {
        _setManager(_manager);
    }

    /**
     * @dev Initializes the contract in an upgradable context.
     * @param _manager The initial managing address.
     */
    // solhint-disable-next-line func-name-mixedcase
    function __Managed_init(address _manager) internal onlyInitializing {
        _setManager(_manager);
    }

    /**
     * @notice Sets the manager address.
     * @param _manager The new manager address.
     */
    function _setManager(address _manager) internal {
        if (_manager == address(0)) {
            revert NullAddress();
        }
        manager = _manager;
        emit ManagerSet(_manager);
    }
}
          

/contracts/common/UUPSOwnableUpgradeable.sol

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

/**
 * @title A contract that links UUPSUUpgradeable with OwanbleUpgradeable to gate upgrades.
 */
abstract contract UUPSOwnableUpgradeable is UUPSUpgradeable, OwnableUpgradeable {
    /**
     * @notice Guard method for UUPS (Universal Upgradable Proxy Standard)
     * See: https://docs.openzeppelin.com/contracts/4.x/api/proxy#transparent-vs-uups
     * @dev This methods overrides the virtual one in UUPSUpgradeable and
     * adds the onlyOwner modifer.
     */
    // solhint-disable-next-line no-empty-blocks
    function _authorizeUpgrade(address) internal override onlyOwner {}
}
          

/contracts/common/linkedlists/AddressSortedLinkedList.sol

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;


import "@openzeppelin/contracts/utils/math/Math.sol";

import "./SortedLinkedList.sol";

/**
 * @title Maintains a sorted list of unsigned ints keyed by address.
 */
library AddressSortedLinkedList {
  using SortedLinkedList for SortedLinkedList.List;

  /**
   * @notice Inserts an element into a doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   * @param value The element value.
   * @param lesserKey The key of the element less than the element to insert.
   * @param greaterKey The key of the element greater than the element to insert.
   */
  function insert(
    SortedLinkedList.List storage list,
    address key,
    uint256 value,
    address lesserKey,
    address greaterKey
  ) public {
    list.insert(toBytes(key), value, toBytes(lesserKey), toBytes(greaterKey));
  }

  /**
   * @notice Removes an element from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to remove.
   */
  function remove(SortedLinkedList.List storage list, address key) public {
    list.remove(toBytes(key));
  }

  /**
   * @notice Updates an element in the list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @param value The element value.
   * @param lesserKey The key of the element will be just left of `key` after the update.
   * @param greaterKey The key of the element will be just right of `key` after the update.
   * @dev Note that only one of "lesserKey" or "greaterKey" needs to be correct to reduce friction.
   */
  function update(
    SortedLinkedList.List storage list,
    address key,
    uint256 value,
    address lesserKey,
    address greaterKey
  ) public {
    list.update(toBytes(key), value, toBytes(lesserKey), toBytes(greaterKey));
  }

  /**
   * @notice Returns whether or not a particular key is present in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return Whether or not the key is in the sorted list.
   */
  function contains(SortedLinkedList.List storage list, address key) public view returns (bool) {
    return list.contains(toBytes(key));
  }

  /**
   * @notice Returns the value for a particular key in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return The element value.
   */
  function getValue(SortedLinkedList.List storage list, address key) public view returns (uint256) {
    return list.getValue(toBytes(key));
  }

  /**
   * @notice Gets all elements from the doubly linked list.
   * @return Array of all keys in the list.
   * @return Values corresponding to keys, which will be ordered largest to smallest.
   */
  function getElements(SortedLinkedList.List storage list)
    public
    view
    returns (address[] memory, uint256[] memory)
  {
    bytes32[] memory byteKeys = list.getKeys();
    address[] memory keys = new address[](byteKeys.length);
    uint256[] memory values = new uint256[](byteKeys.length);
    for (uint256 i = 0; i < byteKeys.length; i = i + 1) {
      keys[i] = toAddress(byteKeys[i]);
      values[i] = list.values[byteKeys[i]];
    }
    return (keys, values);
  }

  /**
   * @notice Returns the minimum of `max` and the  number of elements in the list > threshold.
   * @param list A storage pointer to the underlying list.
   * @param threshold The number that the element must exceed to be included.
   * @param max The maximum number returned by this function.
   * @return The minimum of `max` and the  number of elements in the list > threshold.
   */
  function numElementsGreaterThan(
    SortedLinkedList.List storage list,
    uint256 threshold,
    uint256 max
  ) public view returns (uint256) {
    uint256 revisedMax = Math.min(max, list.list.numElements);
    bytes32 key = list.list.head;
    for (uint256 i = 0; i < revisedMax; i = i + 1) {
      if (list.getValue(key) < threshold) {
        return i;
      }
      key = list.list.elements[key].previousKey;
    }
    return revisedMax;
  }

  /**
   * @notice Returns the N greatest elements of the list.
   * @param list A storage pointer to the underlying list.
   * @param n The number of elements to return.
   * @return The keys of the greatest elements.
   */
  function headN(SortedLinkedList.List storage list, uint256 n)
    public
    view
    returns (address[] memory)
  {
    bytes32[] memory byteKeys = list.headN(n);
    address[] memory keys = new address[](n);
    for (uint256 i = 0; i < n; i = i + 1) {
      keys[i] = toAddress(byteKeys[i]);
    }
    return keys;
  }

  /**
   * @notice Gets all element keys from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return All element keys from head to tail.
   */
  function getKeys(SortedLinkedList.List storage list) public view returns (address[] memory) {
    return headN(list, list.list.numElements);
  }

  /**
   * @notice Returns the number of elements in the list.
   * @param list A storage pointer to the underlying list.
   * @return The number of elements in the list.
   */
  function getNumElements(SortedLinkedList.List storage list) public view returns (uint256) {
    return list.list.numElements;
  }

  /**
   * @notice Returns the key of the first element in the list.
   * @param list A storage pointer to the underlying list.
   * @return The key of the first element in the list.
   */
  function getHead(SortedLinkedList.List storage list) public view returns (address) {
    return toAddress(list.list.head);
  }

  /**
   * @notice Returns the key of the last element in the list.
   * @param list A storage pointer to the underlying list.
   * @return The key of the last element in the list.
   */
  function getTail(SortedLinkedList.List storage list) public view returns (address) {
    return toAddress(list.list.tail);
  }

  /**
   * @notice Gets lesser and greater for address that has increased it's value.
   * @param list A storage pointer to the underlying list.
   * @param group The original address.
   * @param newValue New value that has to be bigger or equal than the previous one.
   * @param loopLimit The max limit of loops that will be executed.
   */
  function getLesserAndGreaterOfAddressThatIncreasedValue(
    SortedLinkedList.List storage list,
    address group,
    uint256 newValue,
    uint256 loopLimit
  )
      public
      view
      returns (address previous, address next)
  {
      (, previous, next) = get(list, group);

      while (next != address(0) && loopLimit != 0 && newValue > getValue(list, next)) {
          previous = next;
          (, , next) = get(list, previous);
          loopLimit--;
      }

      if (loopLimit == 0) {
          return (address(0), address(0));
      }
  }

   /**
   * @notice Gets lesser and greater for address that has decreased it's value.
   * @param list A storage pointer to the underlying list.
   * @param group The original address.
   * @param newValue New value that has to be smaller or equal than the previous one.
   * @param loopLimit The max limit of loops that will be executed.
   */
  function getLesserAndGreaterOfAddressThatDecreasedValue(
    SortedLinkedList.List storage list,
    address group,
    uint256 newValue,
    uint256 loopLimit
  )
      public
      view
      returns (address previous, address next)
  {
      (, previous, next) = get(list, group);
      while (previous != address(0) && loopLimit != 0 && newValue < getValue(list, previous)) {
          next = previous;
          (, previous, ) = get(list, next);
          loopLimit--;
      }
      if (loopLimit == 0) {
          return (address(0), address(0));
      }
  }

  function toBytes(address a) public pure returns (bytes32) {
    return bytes32(uint256(uint160(a)) << 96);
  }

  function toAddress(bytes32 b) public pure returns (address) {
    return address(uint160(uint256(b) >> 96));
  }

  /**
   * @notice Returns Element based on key.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return exists Whether or not the key exists.
   * @return previousKey Previous key.
   * @return nextKey Next key.
   */
  function get(SortedLinkedList.List storage list, address key) 
    internal view returns (bool exists, address previousKey, address nextKey) {
    LinkedList.Element memory element = list.get(toBytes(key));
    exists = element.exists;
    if (element.exists) {
      previousKey = toAddress(element.previousKey);
      nextKey = toAddress(element.nextKey);
    }
  }
}
          

/contracts/common/linkedlists/LinkedList.sol

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

/**
 * @title Maintains a doubly linked list keyed by bytes32.
 * @dev Following the `next` pointers will lead you to the head, rather than the tail.
 */
library LinkedList {

  struct Element {
    bytes32 previousKey;
    bytes32 nextKey;
    bool exists;
  }

  struct List {
    bytes32 head;
    bytes32 tail;
    uint256 numElements;
    mapping(bytes32 => Element) elements;
  }

  /**
   * @notice Inserts an element into a doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   * @param previousKey The key of the element that comes before the element to insert.
   * @param nextKey The key of the element that comes after the element to insert.
   */
  function insert(List storage list, bytes32 key, bytes32 previousKey, bytes32 nextKey) internal {
    require(key != bytes32(0), "Key must be defined");
    require(!contains(list, key), "Can't insert an existing element");
    require(
      previousKey != key && nextKey != key,
      "Key cannot be the same as previousKey or nextKey"
    );

    Element storage element = list.elements[key];
    element.exists = true;

    if (list.numElements == 0) {
      list.tail = key;
      list.head = key;
    } else {
      require(
        previousKey != bytes32(0) || nextKey != bytes32(0),
        "Either previousKey or nextKey must be defined"
      );

      element.previousKey = previousKey;
      element.nextKey = nextKey;

      if (previousKey != bytes32(0)) {
        require(
          contains(list, previousKey),
          "If previousKey is defined, it must exist in the list"
        );
        Element storage previousElement = list.elements[previousKey];
        require(previousElement.nextKey == nextKey, "previousKey must be adjacent to nextKey");
        previousElement.nextKey = key;
      } else {
        list.tail = key;
      }

      if (nextKey != bytes32(0)) {
        require(contains(list, nextKey), "If nextKey is defined, it must exist in the list");
        Element storage nextElement = list.elements[nextKey];
        require(nextElement.previousKey == previousKey, "previousKey must be adjacent to nextKey");
        nextElement.previousKey = key;
      } else {
        list.head = key;
      }
    }

    list.numElements = list.numElements + 1;
  }

  /**
   * @notice Inserts an element at the tail of the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   */
  function push(List storage list, bytes32 key) internal {
    insert(list, key, bytes32(0), list.tail);
  }

  /**
   * @notice Removes an element from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to remove.
   */
  function remove(List storage list, bytes32 key) internal {
    Element storage element = list.elements[key];
    require(key != bytes32(0) && contains(list, key), "key not in list");
    if (element.previousKey != bytes32(0)) {
      Element storage previousElement = list.elements[element.previousKey];
      previousElement.nextKey = element.nextKey;
    } else {
      list.tail = element.nextKey;
    }

    if (element.nextKey != bytes32(0)) {
      Element storage nextElement = list.elements[element.nextKey];
      nextElement.previousKey = element.previousKey;
    } else {
      list.head = element.previousKey;
    }

    delete list.elements[key];
    list.numElements = list.numElements - 1;
  }

  /**
   * @notice Updates an element in the list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @param previousKey The key of the element that comes before the updated element.
   * @param nextKey The key of the element that comes after the updated element.
   */
  function update(List storage list, bytes32 key, bytes32 previousKey, bytes32 nextKey) internal {
    require(
      key != bytes32(0) && key != previousKey && key != nextKey && contains(list, key),
      "key on in list"
    );
    remove(list, key);
    insert(list, key, previousKey, nextKey);
  }

  /**
   * @notice Returns whether or not a particular key is present in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return Whether or not the key is in the sorted list.
   */
  function contains(List storage list, bytes32 key) internal view returns (bool) {
    return list.elements[key].exists;
  }

  /**
   * @notice Returns Element based on key.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return Whether or not the key is in the sorted list.
   */
  function get(List storage list, bytes32 key) internal view returns (Element memory) {
    return list.elements[key];
  }

  /**
   * @notice Returns the keys of the N elements at the head of the list.
   * @param list A storage pointer to the underlying list.
   * @param n The number of elements to return.
   * @return The keys of the N elements at the head of the list.
   * @dev Reverts if n is greater than the number of elements in the list.
   */
  function headN(List storage list, uint256 n) internal view returns (bytes32[] memory) {
    require(n <= list.numElements, "not enough elements");
    bytes32[] memory keys = new bytes32[](n);
    bytes32 key = list.head;
    for (uint256 i = 0; i < n; i = i + 1) {
      keys[i] = key;
      key = list.elements[key].previousKey;
    }
    return keys;
  }

  /**
   * @notice Gets all element keys from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return All element keys from head to tail.
   */
  function getKeys(List storage list) internal view returns (bytes32[] memory) {
    return headN(list, list.numElements);
  }
}
          

/contracts/common/linkedlists/SortedLinkedList.sol

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;


import "./LinkedList.sol";

/**
 * @title Maintains a sorted list of unsigned ints keyed by bytes32.
 */
library SortedLinkedList {
  using LinkedList for LinkedList.List;

  struct List {
    LinkedList.List list;
    mapping(bytes32 => uint256) values;
  }

  /**
   * @notice Inserts an element into a doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   * @param value The element value.
   * @param lesserKey The key of the element less than the element to insert.
   * @param greaterKey The key of the element greater than the element to insert.
   */
  function insert(
    List storage list,
    bytes32 key,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) internal {
    require(
      key != bytes32(0) && key != lesserKey && key != greaterKey && !contains(list, key),
      "invalid key"
    );
    require(
      (lesserKey != bytes32(0) || greaterKey != bytes32(0)) || list.list.numElements == 0,
      "greater and lesser key zero"
    );
    require(contains(list, lesserKey) || lesserKey == bytes32(0), "invalid lesser key");
    require(contains(list, greaterKey) || greaterKey == bytes32(0), "invalid greater key");
    (lesserKey, greaterKey) = getLesserAndGreater(list, value, lesserKey, greaterKey);
    list.list.insert(key, lesserKey, greaterKey);
    list.values[key] = value;
  }

  /**
   * @notice Removes an element from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to remove.
   */
  function remove(List storage list, bytes32 key) internal {
    list.list.remove(key);
    list.values[key] = 0;
  }

  /**
   * @notice Updates an element in the list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @param value The element value.
   * @param lesserKey The key of the element will be just left of `key` after the update.
   * @param greaterKey The key of the element will be just right of `key` after the update.
   * @dev Note that only one of "lesserKey" or "greaterKey" needs to be correct to reduce friction.
   */
  function update(
    List storage list,
    bytes32 key,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) internal {
    remove(list, key);
    insert(list, key, value, lesserKey, greaterKey);
  }

  /**
   * @notice Inserts an element at the tail of the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   */
  function push(List storage list, bytes32 key) internal {
    insert(list, key, 0, bytes32(0), list.list.tail);
  }

  /**
   * @notice Removes N elements from the head of the list and returns their keys.
   * @param list A storage pointer to the underlying list.
   * @param n The number of elements to pop.
   * @return The keys of the popped elements.
   */
  function popN(List storage list, uint256 n) internal returns (bytes32[] memory) {
    require(n <= list.list.numElements, "not enough elements");
    bytes32[] memory keys = new bytes32[](n);
    for (uint256 i = 0; i < n; i = i + 1) {
      bytes32 key = list.list.head;
      keys[i] = key;
      remove(list, key);
    }
    return keys;
  }

  /**
   * @notice Returns whether or not a particular key is present in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return Whether or not the key is in the sorted list.
   */
  function contains(List storage list, bytes32 key) internal view returns (bool) {
    return list.list.contains(key);
  }

  /**
   * @notice Returns Element based on key.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return Whether or not the key is in the sorted list.
   */
  function get(List storage list, bytes32 key) internal view returns (LinkedList.Element memory) {
    return list.list.get(key);
  }

  /**
   * @notice Returns the value for a particular key in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return The element value.
   */
  function getValue(List storage list, bytes32 key) internal view returns (uint256) {
    return list.values[key];
  }

  /**
   * @notice Gets all elements from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return Array of all keys in the list.
   * @return Values corresponding to keys, which will be ordered largest to smallest.
   */
  function getElements(List storage list)
    internal
    view
    returns (bytes32[] memory, uint256[] memory)
  {
    bytes32[] memory keys = getKeys(list);
    uint256[] memory values = new uint256[](keys.length);
    for (uint256 i = 0; i < keys.length; i = i + 1) {
      values[i] = list.values[keys[i]];
    }
    return (keys, values);
  }

  /**
   * @notice Gets all element keys from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return All element keys from head to tail.
   */
  function getKeys(List storage list) internal view returns (bytes32[] memory) {
    return list.list.getKeys();
  }

  /**
   * @notice Returns first N greatest elements of the list.
   * @param list A storage pointer to the underlying list.
   * @param n The number of elements to return.
   * @return The keys of the first n elements.
   * @dev Reverts if n is greater than the number of elements in the list.
   */
  function headN(List storage list, uint256 n) internal view returns (bytes32[] memory) {
    return list.list.headN(n);
  }

  /**
   * @notice Returns the keys of the elements greaterKey than and less than the provided value.
   * @param list A storage pointer to the underlying list.
   * @param value The element value.
   * @param lesserKey The key of the element which could be just left of the new value.
   * @param greaterKey The key of the element which could be just right of the new value.
   * @return The correct lesserKey keys.
   * @return The correct greaterKey keys.
   */
  function getLesserAndGreater(
    List storage list,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) private view returns (bytes32, bytes32) {
    // Check for one of the following conditions and fail if none are met:
    //   1. The value is less than the current lowest value
    //   2. The value is greater than the current greatest value
    //   3. The value is just greater than the value for `lesserKey`
    //   4. The value is just less than the value for `greaterKey`
    if (lesserKey == bytes32(0) && isValueBetween(list, value, lesserKey, list.list.tail)) {
      return (lesserKey, list.list.tail);
    } else if (
      greaterKey == bytes32(0) && isValueBetween(list, value, list.list.head, greaterKey)
    ) {
      return (list.list.head, greaterKey);
    } else if (
      lesserKey != bytes32(0) &&
      isValueBetween(list, value, lesserKey, list.list.elements[lesserKey].nextKey)
    ) {
      return (lesserKey, list.list.elements[lesserKey].nextKey);
    } else if (
      greaterKey != bytes32(0) &&
      isValueBetween(list, value, list.list.elements[greaterKey].previousKey, greaterKey)
    ) {
      return (list.list.elements[greaterKey].previousKey, greaterKey);
    } 

    require(false, "get lesser and greater failure");
    return (0, 0);
  }

  /**
   * @notice Returns whether or not a given element is between two other elements.
   * @param list A storage pointer to the underlying list.
   * @param value The element value.
   * @param lesserKey The key of the element whose value should be lesserKey.
   * @param greaterKey The key of the element whose value should be greaterKey.
   * @return True if the given element is between the two other elements.
   */
  function isValueBetween(List storage list, uint256 value, bytes32 lesserKey, bytes32 greaterKey)
    private
    view
    returns (bool)
  {
    bool isLesser = lesserKey == bytes32(0) || list.values[lesserKey] <= value;
    bool isGreater = greaterKey == bytes32(0) || list.values[greaterKey] >= value;
    return isLesser && isGreater;
  }
}
          

/contracts/interfaces/IAccount.sol

//SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

interface IAccount {
    function scheduleVotes(address[] calldata group, uint256[] calldata votes) external payable;

    function scheduleTransfer(
        address[] calldata fromGroups,
        uint256[] calldata fromVotes,
        address[] calldata toGroups,
        uint256[] calldata toVotess
    ) external;

    function scheduleWithdrawals(
        address beneficiary,
        address[] calldata group,
        uint256[] calldata withdrawals
    ) external;

    function votePartially(
        uint256 proposalId,
        uint256 index,
        uint256 yesVotes,
        uint256 noVotes,
        uint256 abstainVotes
    ) external;

    function getTotalCelo() external view returns (uint256);

    function getCeloForGroup(address) external view returns (uint256);

    function scheduledVotesForGroup(address group) external view returns (uint256);

    function scheduledRevokeForGroup(address group) external view returns (uint256);

    function scheduledWithdrawalsForGroup(address group) external view returns (uint256);
}
          

/contracts/interfaces/IGroupHealth.sol

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

interface IGroupHealth {
    function isGroupValid(address group) external view returns (bool);
}
          

/contracts/interfaces/IManager.sol

//SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

interface IManager {
    function updateHistoryAndReturnLockedStCeloInVoting(address beneficiary)
        external
        returns (uint256);

    function transfer(
        address from,
        address to,
        uint256 amount
    ) external;

    function scheduleTransferWithinStrategy(
        address[] calldata fromGroups,
        address[] calldata toGroups,
        uint256[] calldata fromVotes,
        uint256[] calldata toVotes
    ) external;

    function toCelo(uint256 stCeloAmount) external view returns (uint256);

    function toStakedCelo(uint256 celoAmount) external view returns (uint256);

    function getReceivableVotesForGroup(address group) external view returns (uint256);
}
          

/contracts/interfaces/ISpecificGroupStrategy.sol

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

interface ISpecificGroupStrategy {
    function generateDepositVoteDistribution(
        address group,
        uint256 votes,
        uint256 stCeloAmount
    ) external returns (address[] memory finalGroups, uint256[] memory finalVotes);

    function generateWithdrawalVoteDistribution(
        address group,
        uint256 celoWithdrawalAmount,
        uint256 stCeloWithdrawalAmount,
        bool isTransfer
    ) external returns (address[] memory groups, uint256[] memory votes);

    function isVotedGroup(address group) external view returns (bool);

    function isBlockedGroup(address group) external view returns (bool);

    function getStCeloInGroup(address group)
        external
        view
        returns (
            uint256 total,
            uint256 overflow,
            uint256 unhealthy
        );

    function totalStCeloLocked() external view returns (uint256);

    function totalStCeloOverflow() external view returns (uint256);

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

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"error","name":"AddressZeroNotAllowed","inputs":[]},{"type":"error","name":"CallerNotManager","inputs":[{"type":"address","name":"caller","internalType":"address"}]},{"type":"error","name":"CallerNotManagerNorStrategy","inputs":[{"type":"address","name":"caller","internalType":"address"}]},{"type":"error","name":"GroupAlreadyAdded","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"error","name":"GroupNotActive","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"error","name":"GroupNotEligible","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"error","name":"HealthyGroup","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"error","name":"InvalidFromGroup","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"error","name":"InvalidToGroup","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"error","name":"NoActiveGroups","inputs":[]},{"type":"error","name":"NotAbleToDistributeVotes","inputs":[]},{"type":"error","name":"NotUnsortedGroup","inputs":[]},{"type":"error","name":"NullAddress","inputs":[]},{"type":"error","name":"RebalanceEnoughStCelo","inputs":[{"type":"address","name":"group","internalType":"address"},{"type":"uint256","name":"actualCelo","internalType":"uint256"},{"type":"uint256","name":"expectedCelo","internalType":"uint256"}]},{"type":"error","name":"RebalanceNoExtraStCelo","inputs":[{"type":"address","name":"group","internalType":"address"},{"type":"uint256","name":"actualCelo","internalType":"uint256"},{"type":"uint256","name":"expectedCelo","internalType":"uint256"}]},{"type":"event","name":"AdminChanged","inputs":[{"type":"address","name":"previousAdmin","internalType":"address","indexed":false},{"type":"address","name":"newAdmin","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"BeaconUpgraded","inputs":[{"type":"address","name":"beacon","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"GroupActivated","inputs":[{"type":"address","name":"group","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"GroupRemoved","inputs":[{"type":"address","name":"group","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ManagerSet","inputs":[{"type":"address","name":"manager","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"SortedFlagUpdated","inputs":[{"type":"bool","name":"update","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"Upgraded","inputs":[{"type":"address","name":"implementation","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IAccount"}],"name":"account","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"activateGroup","inputs":[{"type":"address","name":"group","internalType":"address"},{"type":"address","name":"lesser","internalType":"address"},{"type":"address","name":"greater","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deactivateGroup","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deactivateUnhealthyGroup","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address[]","name":"finalGroups","internalType":"address[]"},{"type":"uint256[]","name":"finalVotes","internalType":"uint256[]"}],"name":"generateDepositVoteDistribution","inputs":[{"type":"uint256","name":"celoAmount","internalType":"uint256"},{"type":"address","name":"depositGroupToIgnore","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address[]","name":"finalGroups","internalType":"address[]"},{"type":"uint256[]","name":"finalVotes","internalType":"uint256[]"}],"name":"generateWithdrawalVoteDistribution","inputs":[{"type":"uint256","name":"celoAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"expectedStCelo","internalType":"uint256"},{"type":"uint256","name":"actualStCelo","internalType":"uint256"}],"name":"getExpectedAndActualStCeloForGroup","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"previousAddress","internalType":"address"},{"type":"address","name":"nextAddress","internalType":"address"}],"name":"getGroupPreviousAndNext","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"head","internalType":"address"},{"type":"address","name":"previousAddress","internalType":"address"}],"name":"getGroupsHead","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"tail","internalType":"address"},{"type":"address","name":"nextAddress","internalType":"address"}],"name":"getGroupsTail","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getNumberOfGroups","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getNumberOfUnsortedGroups","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getUnsortedGroupAt","inputs":[{"type":"uint256","name":"index","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getVersionNumber","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IGroupHealth"}],"name":"groupHealth","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_owner","internalType":"address"},{"type":"address","name":"_manager","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isActive","inputs":[{"type":"address","name":"group","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"manager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxGroupsToDistributeTo","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxGroupsToWithdrawFrom","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rebalance","inputs":[{"type":"address","name":"fromGroup","internalType":"address"},{"type":"address","name":"toGroup","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDependencies","inputs":[{"type":"address","name":"_account","internalType":"address"},{"type":"address","name":"_groupHealth","internalType":"address"},{"type":"address","name":"_specificGroupStrategy","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setManager","inputs":[{"type":"address","name":"_manager","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setSortingParams","inputs":[{"type":"uint256","name":"distributeTo","internalType":"uint256"},{"type":"uint256","name":"withdrawFrom","internalType":"uint256"},{"type":"uint256","name":"loopLimit","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"sorted","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ISpecificGroupStrategy"}],"name":"specificGroupStrategy","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stCeloInGroup","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalStCeloInStrategy","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateActiveGroupOrder","inputs":[{"type":"address","name":"group","internalType":"address"},{"type":"address","name":"lesserKey","internalType":"address"},{"type":"address","name":"greaterKey","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgradeTo","inputs":[{"type":"address","name":"newImplementation","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"upgradeToAndCall","inputs":[{"type":"address","name":"newImplementation","internalType":"address"},{"type":"bytes","name":"data","internalType":"bytes"}]}]
              

Contract Creation Code

0x60a06040523073ffffffffffffffffffffffffffffffffffffffff1660809073ffffffffffffffffffffffffffffffffffffffff168152503480156200004457600080fd5b50600060019054906101000a900460ff166200006f5760008054906101000a900460ff161562000080565b6200007f6200013c60201b60201c565b5b620000c2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000b99062000204565b60405180910390fd5b60008060019054906101000a900460ff16159050801562000113576001600060016101000a81548160ff02191690831515021790555060016000806101000a81548160ff0219169083151502179055505b8015620001355760008060016101000a81548160ff0219169083151502179055505b5062000226565b600062000154306200015a60201b62002b6e1760201c565b15905090565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600082825260208201905092915050565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b6000620001ec602e836200017d565b9150620001f9826200018e565b604082019050919050565b600060208201905081810360008301526200021f81620001dd565b9050919050565b608051615a2d6200025760003960008181610861015281816108f0015281816114a601526115350152615a2d6000f3fe6080604052600436106101f95760003560e01c8063885640db1161010d578063c1fa411a116100a0578063d2a2ce5d1161006f578063d2a2ce5d1461071c578063dcdab02a14610747578063e0cd8d2714610770578063eccacf7f146107ae578063f2fde38b146107d9576101f9565b8063c1fa411a14610671578063c5728c831461069c578063cf009f7a146106c8578063d0ebdbe7146106f3576101f9565b8063a3f16ef1116100dc578063a3f16ef1146105b6578063afc97578146105e1578063b52d326c1461060a578063be982c3114610633576101f9565b8063885640db146104fc5780638da5cb5b1461052557806393739a93146105505780639f8a13d714610579576101f9565b80634f1ef286116101905780635dab24201161015f5780635dab2420146104285780636fe958d814610453578063715018a61461047c578063724e61d01461049357806385a92cb7146104bf576101f9565b80634f1ef2861461038a5780634fc517eb146103a65780635145b0ad146103d157806354255be0146103fa576101f9565b80633659cfe6116101cc5780633659cfe6146102cf57806336691a41146102f8578063481c6a7514610336578063485cc95514610361576101f9565b80630c29aab2146101fe5780630f8f0fcc146102295780632863f68c146102665780632de2c5ac146102a4575b600080fd5b34801561020a57600080fd5b50610213610802565b6040516102209190614732565b60405180910390f35b34801561023557600080fd5b50610250600480360381019061024b919061478d565b610808565b60405161025d91906147fb565b60405180910390f35b34801561027257600080fd5b5061028d60048036038101906102889190614842565b610825565b60405161029b92919061486f565b60405180910390f35b3480156102b057600080fd5b506102b961084c565b6040516102c691906148b3565b60405180910390f35b3480156102db57600080fd5b506102f660048036038101906102f19190614842565b61085f565b005b34801561030457600080fd5b5061031f600480360381019061031a919061478d565b6109e8565b60405161032d929190614a4a565b60405180910390f35b34801561034257600080fd5b5061034b61130d565b60405161035891906147fb565b60405180910390f35b34801561036d57600080fd5b5061038860048036038101906103839190614a81565b611333565b005b6103a4600480360381019061039f9190614c07565b6114a4565b005b3480156103b257600080fd5b506103bb6115e1565b6040516103c89190614732565b60405180910390f35b3480156103dd57600080fd5b506103f860048036038101906103f39190614c63565b6115e7565b005b34801561040657600080fd5b5061040f611770565b60405161041f9493929190614cb6565b60405180910390f35b34801561043457600080fd5b5061043d61178a565b60405161044a9190614d5a565b60405180910390f35b34801561045f57600080fd5b5061047a60048036038101906104759190614a81565b6117b0565b005b34801561048857600080fd5b50610491611ab7565b005b34801561049f57600080fd5b506104a8611b3f565b6040516104b692919061486f565b60405180910390f35b3480156104cb57600080fd5b506104e660048036038101906104e19190614842565b611bdf565b6040516104f39190614732565b60405180910390f35b34801561050857600080fd5b50610523600480360381019061051e9190614d75565b611bf7565b005b34801561053157600080fd5b5061053a611c8d565b60405161054791906147fb565b60405180910390f35b34801561055c57600080fd5b5061057760048036038101906105729190614842565b611cb7565b005b34801561058557600080fd5b506105a0600480360381019061059b9190614842565b611d3f565b6040516105ad91906148b3565b60405180910390f35b3480156105c257600080fd5b506105cb611dc5565b6040516105d89190614de9565b60405180910390f35b3480156105ed57600080fd5b5061060860048036038101906106039190614c63565b611deb565b005b34801561061657600080fd5b50610631600480360381019061062c9190614c63565b6122ca565b005b34801561063f57600080fd5b5061065a60048036038101906106559190614842565b6124e3565b604051610668929190614e04565b60405180910390f35b34801561067d57600080fd5b5061068661269e565b6040516106939190614732565b60405180910390f35b3480156106a857600080fd5b506106b1612720565b6040516106bf92919061486f565b60405180910390f35b3480156106d457600080fd5b506106dd6127c1565b6040516106ea9190614e4e565b60405180910390f35b3480156106ff57600080fd5b5061071a60048036038101906107159190614842565b6127e7565b005b34801561072857600080fd5b5061073161286f565b60405161073e9190614732565b60405180910390f35b34801561075357600080fd5b5061076e60048036038101906107699190614842565b612880565b005b34801561077c57600080fd5b5061079760048036038101906107929190614e69565b61296a565b6040516107a5929190614a4a565b60405180910390f35b3480156107ba57600080fd5b506107c3612a70565b6040516107d09190614732565b60405180910390f35b3480156107e557600080fd5b5061080060048036038101906107fb9190614842565b612a76565b005b60715481565b600061081e826074612b9190919063ffffffff16565b9050919050565b60008061083c836066612bab90919063ffffffff16565b9091508092508193505050915091565b607360009054906101000a900460ff1681565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614156108ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108e590614f2c565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1661092d612c06565b73ffffffffffffffffffffffffffffffffffffffff1614610983576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161097a90614fbe565b60405180910390fd5b61098c81612c5d565b6109e581600067ffffffffffffffff8111156109ab576109aa614adc565b5b6040519080825280601f01601f1916602001820160405280156109dd5781602001600182028036833780820191505090505b506000612cdc565b50565b6060803373ffffffffffffffffffffffffffffffffffffffff16606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015610a9757503373ffffffffffffffffffffffffffffffffffffffff16606d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b15610ad957336040517f79eaeb2b000000000000000000000000000000000000000000000000000000008152600401610ad091906147fb565b60405180910390fd5b60006066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b8152600401610b159190614fe5565b602060405180830381865af4158015610b32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b569190615015565b1415610b8e576040517f7818a60e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610c166070546066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b8152600401610bd09190614fe5565b602060405180830381865af4158015610bed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c119190615015565b612ead565b905060008167ffffffffffffffff811115610c3457610c33614adc565b5b604051908082528060200260200182016040528015610c625781602001602082028036833780820191505090505b50905060008267ffffffffffffffff811115610c8157610c80614adc565b5b604051908082528060200260200182016040528015610caf5781602001602082028036833780820191505090505b50905060006066739a272d69f1b48acc648d454db78723cf8a220d9e630c8f298790916040518263ffffffff1660e01b8152600401610cee9190614fe5565b602060405180830381865af4158015610d0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2f9190615057565b905060005b8481108015610d44575060008814155b8015610d7d5750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b1561117257610f0a610f04606c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663acd201d0856040518263ffffffff1660e01b8152600401610de391906147fb565b602060405180830381865afa158015610e00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e249190615015565b606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630567847f606e60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518263ffffffff1660e01b8152600401610ebe9190614732565b602060405180830381865afa158015610edb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eff9190615015565b612ead565b89612ead565b838281518110610f1d57610f1c615084565b5b60200260200101818152505081848281518110610f3d57610f3c615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050828181518110610f8a57610f89615084565b5b602002602001015188610f9d91906150e2565b975061106082606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c494ec1e868581518110610ff457610ff3615084565b5b60200260200101516040518263ffffffff1660e01b81526004016110189190614732565b602060405180830381865afa158015611035573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110599190615015565b6000612ec6565b6110ab82606e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546000612fb5565b607360009054906101000a900460ff1615611142576066739a272d69f1b48acc648d454db78723cf8a220d9e630c8f298790916040518263ffffffff1660e01b81526004016110fa9190614fe5565b602060405180830381865af4158015611117573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113b9190615057565b915061115f565b611156826066612bab90919063ffffffff16565b90915050809250505b808061116a90615116565b915050610d34565b600088146111ac576040517f2e6c1bf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff8111156111c6576111c5614adc565b5b6040519080825280602002602001820160405280156111f45781602001602082028036833780820191505090505b5096508067ffffffffffffffff81111561121157611210614adc565b5b60405190808252806020026020018201604052801561123f5781602001602082028036833780820191505090505b50955060005b81811015611302578481815181106112605761125f615084565b5b602002602001015188828151811061127b5761127a615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508381815181106112c8576112c7615084565b5b60200260200101518782815181106112e3576112e2615084565b5b60200260200101818152505080806112fa90615116565b915050611245565b505050505050915091565b606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060019054906101000a900460ff1661135b5760008054906101000a900460ff1615611364565b611363613269565b5b6113a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161139a906151d1565b60405180910390fd5b60008060019054906101000a900460ff1615905080156113f3576001600060016101000a81548160ff02191690831515021790555060016000806101000a81548160ff0219169083151502179055505b6113fc8361327a565b61140582613340565b6008606f819055506008607081905550600a6072819055506001607360006101000a81548160ff0219169083151502179055507fb930683f0749189780c4016ec37d019eb0cbbf6550ce7374fac5cfbae93909a7607360009054906101000a900460ff1660405161147691906148b3565b60405180910390a1801561149f5760008060016101000a81548160ff0219169083151502179055505b505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415611533576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161152a90614f2c565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16611572612c06565b73ffffffffffffffffffffffffffffffffffffffff16146115c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115bf90614fbe565b60405180910390fd5b6115d182612c5d565b6115dd82826001612cdc565b5050565b606f5481565b6115fb83607461339b90919063ffffffff16565b611631576040517f0a1f2c0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6066739a272d69f1b48acc648d454db78723cf8a220d9e63cab455ae909185606e60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205486866040518663ffffffff1660e01b81526004016116b295949392919061520f565b60006040518083038186803b1580156116ca57600080fd5b505af41580156116de573d6000803e3d6000fd5b505050506116f68360746133cb90919063ffffffff16565b50600061170360746133fb565b141561176b576001607360006101000a81548160ff0219169083151502179055507fb930683f0749189780c4016ec37d019eb0cbbf6550ce7374fac5cfbae93909a7607360009054906101000a900460ff1660405161176291906148b3565b60405180910390a15b505050565b600080600080600180600080935093509350935090919293565b606c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091846040518363ffffffff1660e01b81526004016117ec929190615262565b602060405180830381865af4158015611809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182d91906152b7565b61186e57816040517fdc1b434d00000000000000000000000000000000000000000000000000000000815260040161186591906147fb565b60405180910390fd5b6066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091836040518363ffffffff1660e01b81526004016118aa929190615262565b602060405180830381865af41580156118c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118eb91906152b7565b61192c57806040517f5a08158400000000000000000000000000000000000000000000000000000000815260040161192391906147fb565b60405180910390fd5b600080611938846124e3565b91509150818111611984578381836040517f8b0123bf00000000000000000000000000000000000000000000000000000000815260040161197b939291906152e4565b60405180910390fd5b600080611990856124e3565b915091508181106119dc578481836040517f845286390000000000000000000000000000000000000000000000000000000081526004016119d3939291906152e4565b60405180910390fd5b60006119fe85856119ed91906150e2565b83856119f991906150e2565b612ead565b9050611a0c87826000612ec6565b611a1886826001612ec6565b611a6387606e60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546000612fb5565b611aae86606e60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001612fb5565b50505050505050565b611abf613410565b73ffffffffffffffffffffffffffffffffffffffff16611add611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614611b33576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b2a90615367565b60405180910390fd5b611b3d600061327a565b565b6000806066739a272d69f1b48acc648d454db78723cf8a220d9e630c8f298790916040518263ffffffff1660e01b8152600401611b7c9190614fe5565b602060405180830381865af4158015611b99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbd9190615057565b9150611bd3826066612bab90919063ffffffff16565b90915050809150509091565b606e6020528060005260406000206000915090505481565b611bff613410565b73ffffffffffffffffffffffffffffffffffffffff16611c1d611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614611c73576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c6a90615367565b60405180910390fd5b82606f819055508160708190555080607281905550505050565b6000603360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b611cbf613410565b73ffffffffffffffffffffffffffffffffffffffff16611cdd611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614611d33576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d2a90615367565b60405180910390fd5b611d3c81613418565b50565b60006066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091846040518363ffffffff1660e01b8152600401611d7d929190615262565b602060405180830381865af4158015611d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dbe91906152b7565b9050919050565b606b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611df3613410565b73ffffffffffffffffffffffffffffffffffffffff16611e11611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614611e67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e5e90615367565b60405180910390fd5b606b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ac8f4425846040518263ffffffff1660e01b8152600401611ec291906147fb565b602060405180830381865afa158015611edf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0391906152b7565b611f4457826040517f10a7bc6b000000000000000000000000000000000000000000000000000000008152600401611f3b91906147fb565b60405180910390fd5b6066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091856040518363ffffffff1660e01b8152600401611f80929190615262565b602060405180830381865af4158015611f9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc191906152b7565b1561200357826040517fbd133eee000000000000000000000000000000000000000000000000000000008152600401611ffa91906147fb565b60405180910390fd5b600080606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c494ec1e606c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663acd201d0886040518263ffffffff1660e01b815260040161209f91906147fb565b602060405180830381865afa1580156120bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120e09190615015565b6040518263ffffffff1660e01b81526004016120fc9190614732565b602060405180830381865afa158015612119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213d9190615015565b90506000811461220e576000606d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166356ab819f876040518263ffffffff1660e01b81526004016121a491906147fb565b606060405180830381865afa1580156121c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e59190615387565b505090506121f38282612ead565b826121fe91906150e2565b925061220c86846001612ec6565b505b6066739a272d69f1b48acc648d454db78723cf8a220d9e632dedbbf09091878588886040518663ffffffff1660e01b815260040161225095949392919061520f565b60006040518083038186803b15801561226857600080fd5b505af415801561227c573d6000803e3d6000fd5b505050508473ffffffffffffffffffffffffffffffffffffffff167f0503b4748a47435f2432d46ef300e0e2dc47baa0e5f04bb3bd8a355ee1e1dbe660405160405180910390a25050505050565b6122d2613410565b73ffffffffffffffffffffffffffffffffffffffff166122f0611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614612346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161233d90615367565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614806123ad5750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b806123e45750600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b1561241b576040517f0855380c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81606b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080606d60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082606c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050565b60008060006066739a272d69f1b48acc648d454db78723cf8a220d9e630c8f298790916040518263ffffffff1660e01b81526004016125229190614fe5565b602060405180830381865af415801561253f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125639190615057565b905060006066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b81526004016125a19190614fe5565b602060405180830381865af41580156125be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e29190615015565b9050806071546125f29190615409565b93508173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614156126555760008185612636919061543a565b60715461264391906150e2565b905080856126519190615494565b9450505b606e60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205492505050915091565b60006066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b81526004016126da9190614fe5565b602060405180830381865af41580156126f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061271b9190615015565b905090565b6000806066739a272d69f1b48acc648d454db78723cf8a220d9e6349923bff90916040518263ffffffff1660e01b815260040161275d9190614fe5565b602060405180830381865af415801561277a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061279e9190615057565b91506127b4826066612bab90919063ffffffff16565b9091509050809150509091565b606d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6127ef613410565b73ffffffffffffffffffffffffffffffffffffffff1661280d611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614612863576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161285a90615367565b60405180910390fd5b61286c816138e3565b50565b600061287b60746133fb565b905090565b606b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ac8f4425826040518263ffffffff1660e01b81526004016128db91906147fb565b602060405180830381865afa1580156128f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291c91906152b7565b1561295e57806040517fe3bd013b00000000000000000000000000000000000000000000000000000000815260040161295591906147fb565b60405180910390fd5b61296781613418565b50565b6060803373ffffffffffffffffffffffffffffffffffffffff16606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015612a1957503373ffffffffffffffffffffffffffffffffffffffff16606d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b15612a5b57336040517f79eaeb2b000000000000000000000000000000000000000000000000000000008152600401612a5291906147fb565b60405180910390fd5b612a6584846139d1565b915091509250929050565b60705481565b612a7e613410565b73ffffffffffffffffffffffffffffffffffffffff16612a9c611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614612af2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ae990615367565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612b62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b599061555c565b60405180910390fd5b612b6b8161327a565b50565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000612ba08360000183614190565b60001c905092915050565b600080600080612bcc612bbd866141bb565b876141e290919063ffffffff16565b905080604001519350806040015115612bfe57612bec8160000151614208565b9250612bfb8160200151614208565b91505b509250925092565b6000612c347f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b614219565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b612c65613410565b73ffffffffffffffffffffffffffffffffffffffff16612c83611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614612cd9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cd090615367565b60405180910390fd5b50565b6000612ce6612c06565b9050612cf184614223565b600083511180612cfe5750815b15612d0f57612d0d84846142dc565b505b6000612d3d7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b614309565b90508060000160009054906101000a900460ff16612ea65760018160000160006101000a81548160ff021916908315150217905550612e098583604051602401612d8791906147fb565b6040516020818303038152906040527f3659cfe6000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506142dc565b5060008160000160006101000a81548160ff021916908315150217905550612e2f612c06565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614612e9c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e93906155ee565b60405180910390fd5b612ea585614313565b5b5050505050565b6000818310612ebc5781612ebe565b825b905092915050565b8015612f405781606e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612f1b9190615494565b925050819055508160716000828254612f349190615494565b92505081905550612fb0565b81606e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612f8f91906150e2565b925050819055508160716000828254612fa891906150e2565b925050819055505b505050565b612fc983607461339b90919063ffffffff16565b15612fd357613264565b60008082613062576066739a272d69f1b48acc648d454db78723cf8a220d9e63a4aadfcc909187876072546040518563ffffffff1660e01b815260040161301d949392919061560e565b6040805180830381865af4158015613039573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061305d9190615653565b6130e5565b6066739a272d69f1b48acc648d454db78723cf8a220d9e6399f68a39909187876072546040518563ffffffff1660e01b81526004016130a4949392919061560e565b6040805180830381865af41580156130c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130e49190615653565b5b915091508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614158061319e575060016066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b815260040161315b9190614fe5565b602060405180830381865af4158015613178573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061319c9190615015565b145b1561321a576066739a272d69f1b48acc648d454db78723cf8a220d9e63cab455ae9091878786866040518663ffffffff1660e01b81526004016131e595949392919061520f565b60006040518083038186803b1580156131fd57600080fd5b505af4158015613211573d6000803e3d6000fd5b50505050613261565b607360009054906101000a900460ff161561324b576000607360006101000a81548160ff0219169083151502179055505b61325f85607461436290919063ffffffff16565b505b50505b505050565b600061327430612b6e565b15905090565b6000603360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081603360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600060019054906101000a900460ff1661338f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161338690615705565b60405180910390fd5b613398816138e3565b50565b60006133c3836000018373ffffffffffffffffffffffffffffffffffffffff1660001b614392565b905092915050565b60006133f3836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6143b5565b905092915050565b6000613409826000016144c9565b9050919050565b600033905090565b6066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091836040518363ffffffff1660e01b8152600401613454929190615262565b602060405180830381865af4158015613471573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349591906152b7565b6134d657806040517f1adbb1530000000000000000000000000000000000000000000000000000000081526004016134cd91906147fb565b60405180910390fd5b6066739a272d69f1b48acc648d454db78723cf8a220d9e63281359299091836040518363ffffffff1660e01b8152600401613512929190615262565b60006040518083038186803b15801561352a57600080fd5b505af415801561353e573d6000803e3d6000fd5b505050506135568160746133cb90919063ffffffff16565b506000606e60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600081111561389c576135b082826000612ec6565b6000600167ffffffffffffffff8111156135cd576135cc614adc565b5b6040519080825280602002602001820160405280156135fb5781602001602082028036833780820191505090505b5090506000600167ffffffffffffffff81111561361b5761361a614adc565b5b6040519080825280602002602001820160405280156136495781602001602082028036833780820191505090505b509050838260008151811061366157613660615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630567847f846040518263ffffffff1660e01b81526004016136f69190614732565b602060405180830381865afa158015613713573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137379190615015565b8160008151811061374b5761374a615084565b5b602002602001018181525050600080613800606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630567847f876040518263ffffffff1660e01b81526004016137b89190614732565b602060405180830381865afa1580156137d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f99190615015565b60006139d1565b91509150606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630c4d4e40858486856040518563ffffffff1660e01b81526004016138659493929190615725565b600060405180830381600087803b15801561387f57600080fd5b505af1158015613893573d6000803e3d6000fd5b50505050505050505b8173ffffffffffffffffffffffffffffffffffffffff167fa78a88a551e130ce9732be17784bdff12f72ab4a2c833fb2dcb3ef0818956b0360405160405180910390a25050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561394a576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80606560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa6960405160405180910390a250565b60608060006066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b8152600401613a109190614fe5565b602060405180830381865af4158015613a2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a519190615015565b1415613a89576040517f7818a60e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000613b11606f546066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b8152600401613acb9190614fe5565b602060405180830381865af4158015613ae8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b0c9190615015565b612ead565b905060008167ffffffffffffffff811115613b2f57613b2e614adc565b5b604051908082528060200260200182016040528015613b5d5781602001602082028036833780820191505090505b50905060008267ffffffffffffffff811115613b7c57613b7b614adc565b5b604051908082528060200260200182016040528015613baa5781602001602082028036833780820191505090505b50905060006066739a272d69f1b48acc648d454db78723cf8a220d9e6349923bff90916040518263ffffffff1660e01b8152600401613be99190614fe5565b602060405180830381865af4158015613c06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c2a9190615057565b905060005b8481108015613c3f575060008914155b8015613c785750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b15613ff3576000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632c431058846040518263ffffffff1660e01b8152600401613cda91906147fb565b602060405180830381865afa158015613cf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1b9190615015565b90508873ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161480613d575750600081145b15613d7f57613d70836066612bab90919063ffffffff16565b90915090508093505050613c2f565b613d89818b612ead565b848381518110613d9c57613d9b615084565b5b60200260200101818152505082858381518110613dbc57613dbb615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050838281518110613e0957613e08615084565b5b60200260200101518a613e1c91906150e2565b9950613edf83606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c494ec1e878681518110613e7357613e72615084565b5b60200260200101516040518263ffffffff1660e01b8152600401613e979190614732565b602060405180830381865afa158015613eb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ed89190615015565b6001612ec6565b613f2a83606e60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001612fb5565b607360009054906101000a900460ff1615613fc1576066739a272d69f1b48acc648d454db78723cf8a220d9e6349923bff90916040518263ffffffff1660e01b8152600401613f799190614fe5565b602060405180830381865af4158015613f96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fba9190615057565b9250613fdf565b613fd5836066612bab90919063ffffffff16565b9091509050809350505b8180613fea90615116565b92505050613c2f565b6000891461402d576040517f2e6c1bf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff81111561404757614046614adc565b5b6040519080825280602002602001820160405280156140755781602001602082028036833780820191505090505b5096508067ffffffffffffffff81111561409257614091614adc565b5b6040519080825280602002602001820160405280156140c05781602001602082028036833780820191505090505b50955060005b81811015614183578481815181106140e1576140e0615084565b5b60200260200101518882815181106140fc576140fb615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505083818151811061414957614148615084565b5b602002602001015187828151811061416457614163615084565b5b602002602001018181525050808061417b90615116565b9150506140c6565b5050505050509250929050565b60008260000182815481106141a8576141a7615084565b5b9060005260206000200154905092915050565b600060608273ffffffffffffffffffffffffffffffffffffffff16901b60001b9050919050565b6141ea6146f0565b61420082846000016144da90919063ffffffff16565b905092915050565b600060608260001c901c9050919050565b6000819050919050565b61422c81614539565b61426b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401614262906157f8565b60405180910390fd5b806142987f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b614219565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b606061430183836040518060600160405280602781526020016159d16027913961454c565b905092915050565b6000819050919050565b61431c81614223565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b600061438a836000018373ffffffffffffffffffffffffffffffffffffffff1660001b614619565b905092915050565b600080836001016000848152602001908152602001600020541415905092915050565b600080836001016000848152602001908152602001600020549050600081146144bd5760006001826143e791906150e2565b90506000600186600001805490506143ff91906150e2565b905081811461446e5760008660000182815481106144205761441f615084565b5b906000526020600020015490508087600001848154811061444457614443615084565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b8560000180548061448257614481615818565b5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506144c3565b60009150505b92915050565b600081600001805490509050919050565b6144e26146f0565b82600301600083815260200190815260200160002060405180606001604052908160008201548152602001600182015481526020016002820160009054906101000a900460ff161515151581525050905092915050565b600080823b905060008111915050919050565b606061455784614539565b614596576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161458d906158b9565b60405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff16856040516145be9190615953565b600060405180830381855af49150503d80600081146145f9576040519150601f19603f3d011682016040523d82523d6000602084013e6145fe565b606091505b509150915061460e828286614689565b925050509392505050565b60006146258383614392565b61467e578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050614683565b600090505b92915050565b60608315614699578290506146e9565b6000835111156146ac5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016146e091906159ae565b60405180910390fd5b9392505050565b604051806060016040528060008019168152602001600080191681526020016000151581525090565b6000819050919050565b61472c81614719565b82525050565b60006020820190506147476000830184614723565b92915050565b6000604051905090565b600080fd5b600080fd5b61476a81614719565b811461477557600080fd5b50565b60008135905061478781614761565b92915050565b6000602082840312156147a3576147a2614757565b5b60006147b184828501614778565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006147e5826147ba565b9050919050565b6147f5816147da565b82525050565b600060208201905061481060008301846147ec565b92915050565b61481f816147da565b811461482a57600080fd5b50565b60008135905061483c81614816565b92915050565b60006020828403121561485857614857614757565b5b60006148668482850161482d565b91505092915050565b600060408201905061488460008301856147ec565b61489160208301846147ec565b9392505050565b60008115159050919050565b6148ad81614898565b82525050565b60006020820190506148c860008301846148a4565b92915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b614903816147da565b82525050565b600061491583836148fa565b60208301905092915050565b6000602082019050919050565b6000614939826148ce565b61494381856148d9565b935061494e836148ea565b8060005b8381101561497f5781516149668882614909565b975061497183614921565b925050600181019050614952565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6149c181614719565b82525050565b60006149d383836149b8565b60208301905092915050565b6000602082019050919050565b60006149f78261498c565b614a018185614997565b9350614a0c836149a8565b8060005b83811015614a3d578151614a2488826149c7565b9750614a2f836149df565b925050600181019050614a10565b5085935050505092915050565b60006040820190508181036000830152614a64818561492e565b90508181036020830152614a7881846149ec565b90509392505050565b60008060408385031215614a9857614a97614757565b5b6000614aa68582860161482d565b9250506020614ab78582860161482d565b9150509250929050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b614b1482614acb565b810181811067ffffffffffffffff82111715614b3357614b32614adc565b5b80604052505050565b6000614b4661474d565b9050614b528282614b0b565b919050565b600067ffffffffffffffff821115614b7257614b71614adc565b5b614b7b82614acb565b9050602081019050919050565b82818337600083830152505050565b6000614baa614ba584614b57565b614b3c565b905082815260208101848484011115614bc657614bc5614ac6565b5b614bd1848285614b88565b509392505050565b600082601f830112614bee57614bed614ac1565b5b8135614bfe848260208601614b97565b91505092915050565b60008060408385031215614c1e57614c1d614757565b5b6000614c2c8582860161482d565b925050602083013567ffffffffffffffff811115614c4d57614c4c61475c565b5b614c5985828601614bd9565b9150509250929050565b600080600060608486031215614c7c57614c7b614757565b5b6000614c8a8682870161482d565b9350506020614c9b8682870161482d565b9250506040614cac8682870161482d565b9150509250925092565b6000608082019050614ccb6000830187614723565b614cd86020830186614723565b614ce56040830185614723565b614cf26060830184614723565b95945050505050565b6000819050919050565b6000614d20614d1b614d16846147ba565b614cfb565b6147ba565b9050919050565b6000614d3282614d05565b9050919050565b6000614d4482614d27565b9050919050565b614d5481614d39565b82525050565b6000602082019050614d6f6000830184614d4b565b92915050565b600080600060608486031215614d8e57614d8d614757565b5b6000614d9c86828701614778565b9350506020614dad86828701614778565b9250506040614dbe86828701614778565b9150509250925092565b6000614dd382614d27565b9050919050565b614de381614dc8565b82525050565b6000602082019050614dfe6000830184614dda565b92915050565b6000604082019050614e196000830185614723565b614e266020830184614723565b9392505050565b6000614e3882614d27565b9050919050565b614e4881614e2d565b82525050565b6000602082019050614e636000830184614e3f565b92915050565b60008060408385031215614e8057614e7f614757565b5b6000614e8e85828601614778565b9250506020614e9f8582860161482d565b9150509250929050565b600082825260208201905092915050565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f64656c656761746563616c6c0000000000000000000000000000000000000000602082015250565b6000614f16602c83614ea9565b9150614f2182614eba565b604082019050919050565b60006020820190508181036000830152614f4581614f09565b9050919050565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f6163746976652070726f78790000000000000000000000000000000000000000602082015250565b6000614fa8602c83614ea9565b9150614fb382614f4c565b604082019050919050565b60006020820190508181036000830152614fd781614f9b565b9050919050565b8082525050565b6000602082019050614ffa6000830184614fde565b92915050565b60008151905061500f81614761565b92915050565b60006020828403121561502b5761502a614757565b5b600061503984828501615000565b91505092915050565b60008151905061505181614816565b92915050565b60006020828403121561506d5761506c614757565b5b600061507b84828501615042565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006150ed82614719565b91506150f883614719565b92508282101561510b5761510a6150b3565b5b828203905092915050565b600061512182614719565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415615154576151536150b3565b5b600182019050919050565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b60006151bb602e83614ea9565b91506151c68261515f565b604082019050919050565b600060208201905081810360008301526151ea816151ae565b9050919050565b6151fa816147da565b82525050565b61520981614719565b82525050565b600060a0820190506152246000830188614fde565b61523160208301876151f1565b61523e6040830186615200565b61524b60608301856151f1565b61525860808301846151f1565b9695505050505050565b60006040820190506152776000830185614fde565b61528460208301846151f1565b9392505050565b61529481614898565b811461529f57600080fd5b50565b6000815190506152b18161528b565b92915050565b6000602082840312156152cd576152cc614757565b5b60006152db848285016152a2565b91505092915050565b60006060820190506152f960008301866147ec565b6153066020830185614723565b6153136040830184614723565b949350505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000615351602083614ea9565b915061535c8261531b565b602082019050919050565b6000602082019050818103600083015261538081615344565b9050919050565b6000806000606084860312156153a05761539f614757565b5b60006153ae86828701615000565b93505060206153bf86828701615000565b92505060406153d086828701615000565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061541482614719565b915061541f83614719565b92508261542f5761542e6153da565b5b828204905092915050565b600061544582614719565b915061545083614719565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615489576154886150b3565b5b828202905092915050565b600061549f82614719565b91506154aa83614719565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156154df576154de6150b3565b5b828201905092915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000615546602683614ea9565b9150615551826154ea565b604082019050919050565b6000602082019050818103600083015261557581615539565b9050919050565b7f45524331393637557067726164653a207570677261646520627265616b73206660008201527f7572746865722075706772616465730000000000000000000000000000000000602082015250565b60006155d8602f83614ea9565b91506155e38261557c565b604082019050919050565b60006020820190508181036000830152615607816155cb565b9050919050565b60006080820190506156236000830187614fde565b61563060208301866151f1565b61563d6040830185615200565b61564a6060830184615200565b95945050505050565b6000806040838503121561566a57615669614757565b5b600061567885828601615042565b925050602061568985828601615042565b9150509250929050565b7f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960008201527f6e697469616c697a696e67000000000000000000000000000000000000000000602082015250565b60006156ef602b83614ea9565b91506156fa82615693565b604082019050919050565b6000602082019050818103600083015261571e816156e2565b9050919050565b6000608082019050818103600083015261573f818761492e565b90508181036020830152615753818661492e565b9050818103604083015261576781856149ec565b9050818103606083015261577b81846149ec565b905095945050505050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b60006157e2602d83614ea9565b91506157ed82615786565b604082019050919050565b60006020820190508181036000830152615811816157d5565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b60006158a3602683614ea9565b91506158ae82615847565b604082019050919050565b600060208201905081810360008301526158d281615896565b9050919050565b600081519050919050565b600081905092915050565b60005b8381101561590d5780820151818401526020810190506158f2565b8381111561591c576000848401525b50505050565b600061592d826158d9565b61593781856158e4565b93506159478185602086016158ef565b80840191505092915050565b600061595f8284615922565b915081905092915050565b600081519050919050565b60006159808261596a565b61598a8185614ea9565b935061599a8185602086016158ef565b6159a381614acb565b840191505092915050565b600060208201905081810360008301526159c88184615975565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220a1703fc71954cbb1a1560d4385dc2ddecc730aa26a4d16d5f09db1ec2697648464736f6c634300080b0033

Deployed ByteCode

0x6080604052600436106101f95760003560e01c8063885640db1161010d578063c1fa411a116100a0578063d2a2ce5d1161006f578063d2a2ce5d1461071c578063dcdab02a14610747578063e0cd8d2714610770578063eccacf7f146107ae578063f2fde38b146107d9576101f9565b8063c1fa411a14610671578063c5728c831461069c578063cf009f7a146106c8578063d0ebdbe7146106f3576101f9565b8063a3f16ef1116100dc578063a3f16ef1146105b6578063afc97578146105e1578063b52d326c1461060a578063be982c3114610633576101f9565b8063885640db146104fc5780638da5cb5b1461052557806393739a93146105505780639f8a13d714610579576101f9565b80634f1ef286116101905780635dab24201161015f5780635dab2420146104285780636fe958d814610453578063715018a61461047c578063724e61d01461049357806385a92cb7146104bf576101f9565b80634f1ef2861461038a5780634fc517eb146103a65780635145b0ad146103d157806354255be0146103fa576101f9565b80633659cfe6116101cc5780633659cfe6146102cf57806336691a41146102f8578063481c6a7514610336578063485cc95514610361576101f9565b80630c29aab2146101fe5780630f8f0fcc146102295780632863f68c146102665780632de2c5ac146102a4575b600080fd5b34801561020a57600080fd5b50610213610802565b6040516102209190614732565b60405180910390f35b34801561023557600080fd5b50610250600480360381019061024b919061478d565b610808565b60405161025d91906147fb565b60405180910390f35b34801561027257600080fd5b5061028d60048036038101906102889190614842565b610825565b60405161029b92919061486f565b60405180910390f35b3480156102b057600080fd5b506102b961084c565b6040516102c691906148b3565b60405180910390f35b3480156102db57600080fd5b506102f660048036038101906102f19190614842565b61085f565b005b34801561030457600080fd5b5061031f600480360381019061031a919061478d565b6109e8565b60405161032d929190614a4a565b60405180910390f35b34801561034257600080fd5b5061034b61130d565b60405161035891906147fb565b60405180910390f35b34801561036d57600080fd5b5061038860048036038101906103839190614a81565b611333565b005b6103a4600480360381019061039f9190614c07565b6114a4565b005b3480156103b257600080fd5b506103bb6115e1565b6040516103c89190614732565b60405180910390f35b3480156103dd57600080fd5b506103f860048036038101906103f39190614c63565b6115e7565b005b34801561040657600080fd5b5061040f611770565b60405161041f9493929190614cb6565b60405180910390f35b34801561043457600080fd5b5061043d61178a565b60405161044a9190614d5a565b60405180910390f35b34801561045f57600080fd5b5061047a60048036038101906104759190614a81565b6117b0565b005b34801561048857600080fd5b50610491611ab7565b005b34801561049f57600080fd5b506104a8611b3f565b6040516104b692919061486f565b60405180910390f35b3480156104cb57600080fd5b506104e660048036038101906104e19190614842565b611bdf565b6040516104f39190614732565b60405180910390f35b34801561050857600080fd5b50610523600480360381019061051e9190614d75565b611bf7565b005b34801561053157600080fd5b5061053a611c8d565b60405161054791906147fb565b60405180910390f35b34801561055c57600080fd5b5061057760048036038101906105729190614842565b611cb7565b005b34801561058557600080fd5b506105a0600480360381019061059b9190614842565b611d3f565b6040516105ad91906148b3565b60405180910390f35b3480156105c257600080fd5b506105cb611dc5565b6040516105d89190614de9565b60405180910390f35b3480156105ed57600080fd5b5061060860048036038101906106039190614c63565b611deb565b005b34801561061657600080fd5b50610631600480360381019061062c9190614c63565b6122ca565b005b34801561063f57600080fd5b5061065a60048036038101906106559190614842565b6124e3565b604051610668929190614e04565b60405180910390f35b34801561067d57600080fd5b5061068661269e565b6040516106939190614732565b60405180910390f35b3480156106a857600080fd5b506106b1612720565b6040516106bf92919061486f565b60405180910390f35b3480156106d457600080fd5b506106dd6127c1565b6040516106ea9190614e4e565b60405180910390f35b3480156106ff57600080fd5b5061071a60048036038101906107159190614842565b6127e7565b005b34801561072857600080fd5b5061073161286f565b60405161073e9190614732565b60405180910390f35b34801561075357600080fd5b5061076e60048036038101906107699190614842565b612880565b005b34801561077c57600080fd5b5061079760048036038101906107929190614e69565b61296a565b6040516107a5929190614a4a565b60405180910390f35b3480156107ba57600080fd5b506107c3612a70565b6040516107d09190614732565b60405180910390f35b3480156107e557600080fd5b5061080060048036038101906107fb9190614842565b612a76565b005b60715481565b600061081e826074612b9190919063ffffffff16565b9050919050565b60008061083c836066612bab90919063ffffffff16565b9091508092508193505050915091565b607360009054906101000a900460ff1681565b7f000000000000000000000000f76329e2398b2d2707dd06ef6efa729cd52f73f273ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614156108ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108e590614f2c565b60405180910390fd5b7f000000000000000000000000f76329e2398b2d2707dd06ef6efa729cd52f73f273ffffffffffffffffffffffffffffffffffffffff1661092d612c06565b73ffffffffffffffffffffffffffffffffffffffff1614610983576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161097a90614fbe565b60405180910390fd5b61098c81612c5d565b6109e581600067ffffffffffffffff8111156109ab576109aa614adc565b5b6040519080825280601f01601f1916602001820160405280156109dd5781602001600182028036833780820191505090505b506000612cdc565b50565b6060803373ffffffffffffffffffffffffffffffffffffffff16606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015610a9757503373ffffffffffffffffffffffffffffffffffffffff16606d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b15610ad957336040517f79eaeb2b000000000000000000000000000000000000000000000000000000008152600401610ad091906147fb565b60405180910390fd5b60006066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b8152600401610b159190614fe5565b602060405180830381865af4158015610b32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b569190615015565b1415610b8e576040517f7818a60e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610c166070546066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b8152600401610bd09190614fe5565b602060405180830381865af4158015610bed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c119190615015565b612ead565b905060008167ffffffffffffffff811115610c3457610c33614adc565b5b604051908082528060200260200182016040528015610c625781602001602082028036833780820191505090505b50905060008267ffffffffffffffff811115610c8157610c80614adc565b5b604051908082528060200260200182016040528015610caf5781602001602082028036833780820191505090505b50905060006066739a272d69f1b48acc648d454db78723cf8a220d9e630c8f298790916040518263ffffffff1660e01b8152600401610cee9190614fe5565b602060405180830381865af4158015610d0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2f9190615057565b905060005b8481108015610d44575060008814155b8015610d7d5750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b1561117257610f0a610f04606c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663acd201d0856040518263ffffffff1660e01b8152600401610de391906147fb565b602060405180830381865afa158015610e00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e249190615015565b606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630567847f606e60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518263ffffffff1660e01b8152600401610ebe9190614732565b602060405180830381865afa158015610edb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eff9190615015565b612ead565b89612ead565b838281518110610f1d57610f1c615084565b5b60200260200101818152505081848281518110610f3d57610f3c615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050828181518110610f8a57610f89615084565b5b602002602001015188610f9d91906150e2565b975061106082606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c494ec1e868581518110610ff457610ff3615084565b5b60200260200101516040518263ffffffff1660e01b81526004016110189190614732565b602060405180830381865afa158015611035573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110599190615015565b6000612ec6565b6110ab82606e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546000612fb5565b607360009054906101000a900460ff1615611142576066739a272d69f1b48acc648d454db78723cf8a220d9e630c8f298790916040518263ffffffff1660e01b81526004016110fa9190614fe5565b602060405180830381865af4158015611117573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113b9190615057565b915061115f565b611156826066612bab90919063ffffffff16565b90915050809250505b808061116a90615116565b915050610d34565b600088146111ac576040517f2e6c1bf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff8111156111c6576111c5614adc565b5b6040519080825280602002602001820160405280156111f45781602001602082028036833780820191505090505b5096508067ffffffffffffffff81111561121157611210614adc565b5b60405190808252806020026020018201604052801561123f5781602001602082028036833780820191505090505b50955060005b81811015611302578481815181106112605761125f615084565b5b602002602001015188828151811061127b5761127a615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508381815181106112c8576112c7615084565b5b60200260200101518782815181106112e3576112e2615084565b5b60200260200101818152505080806112fa90615116565b915050611245565b505050505050915091565b606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060019054906101000a900460ff1661135b5760008054906101000a900460ff1615611364565b611363613269565b5b6113a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161139a906151d1565b60405180910390fd5b60008060019054906101000a900460ff1615905080156113f3576001600060016101000a81548160ff02191690831515021790555060016000806101000a81548160ff0219169083151502179055505b6113fc8361327a565b61140582613340565b6008606f819055506008607081905550600a6072819055506001607360006101000a81548160ff0219169083151502179055507fb930683f0749189780c4016ec37d019eb0cbbf6550ce7374fac5cfbae93909a7607360009054906101000a900460ff1660405161147691906148b3565b60405180910390a1801561149f5760008060016101000a81548160ff0219169083151502179055505b505050565b7f000000000000000000000000f76329e2398b2d2707dd06ef6efa729cd52f73f273ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415611533576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161152a90614f2c565b60405180910390fd5b7f000000000000000000000000f76329e2398b2d2707dd06ef6efa729cd52f73f273ffffffffffffffffffffffffffffffffffffffff16611572612c06565b73ffffffffffffffffffffffffffffffffffffffff16146115c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115bf90614fbe565b60405180910390fd5b6115d182612c5d565b6115dd82826001612cdc565b5050565b606f5481565b6115fb83607461339b90919063ffffffff16565b611631576040517f0a1f2c0e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6066739a272d69f1b48acc648d454db78723cf8a220d9e63cab455ae909185606e60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205486866040518663ffffffff1660e01b81526004016116b295949392919061520f565b60006040518083038186803b1580156116ca57600080fd5b505af41580156116de573d6000803e3d6000fd5b505050506116f68360746133cb90919063ffffffff16565b50600061170360746133fb565b141561176b576001607360006101000a81548160ff0219169083151502179055507fb930683f0749189780c4016ec37d019eb0cbbf6550ce7374fac5cfbae93909a7607360009054906101000a900460ff1660405161176291906148b3565b60405180910390a15b505050565b600080600080600180600080935093509350935090919293565b606c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091846040518363ffffffff1660e01b81526004016117ec929190615262565b602060405180830381865af4158015611809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182d91906152b7565b61186e57816040517fdc1b434d00000000000000000000000000000000000000000000000000000000815260040161186591906147fb565b60405180910390fd5b6066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091836040518363ffffffff1660e01b81526004016118aa929190615262565b602060405180830381865af41580156118c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118eb91906152b7565b61192c57806040517f5a08158400000000000000000000000000000000000000000000000000000000815260040161192391906147fb565b60405180910390fd5b600080611938846124e3565b91509150818111611984578381836040517f8b0123bf00000000000000000000000000000000000000000000000000000000815260040161197b939291906152e4565b60405180910390fd5b600080611990856124e3565b915091508181106119dc578481836040517f845286390000000000000000000000000000000000000000000000000000000081526004016119d3939291906152e4565b60405180910390fd5b60006119fe85856119ed91906150e2565b83856119f991906150e2565b612ead565b9050611a0c87826000612ec6565b611a1886826001612ec6565b611a6387606e60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546000612fb5565b611aae86606e60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001612fb5565b50505050505050565b611abf613410565b73ffffffffffffffffffffffffffffffffffffffff16611add611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614611b33576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b2a90615367565b60405180910390fd5b611b3d600061327a565b565b6000806066739a272d69f1b48acc648d454db78723cf8a220d9e630c8f298790916040518263ffffffff1660e01b8152600401611b7c9190614fe5565b602060405180830381865af4158015611b99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbd9190615057565b9150611bd3826066612bab90919063ffffffff16565b90915050809150509091565b606e6020528060005260406000206000915090505481565b611bff613410565b73ffffffffffffffffffffffffffffffffffffffff16611c1d611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614611c73576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c6a90615367565b60405180910390fd5b82606f819055508160708190555080607281905550505050565b6000603360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b611cbf613410565b73ffffffffffffffffffffffffffffffffffffffff16611cdd611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614611d33576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d2a90615367565b60405180910390fd5b611d3c81613418565b50565b60006066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091846040518363ffffffff1660e01b8152600401611d7d929190615262565b602060405180830381865af4158015611d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dbe91906152b7565b9050919050565b606b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b611df3613410565b73ffffffffffffffffffffffffffffffffffffffff16611e11611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614611e67576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e5e90615367565b60405180910390fd5b606b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ac8f4425846040518263ffffffff1660e01b8152600401611ec291906147fb565b602060405180830381865afa158015611edf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0391906152b7565b611f4457826040517f10a7bc6b000000000000000000000000000000000000000000000000000000008152600401611f3b91906147fb565b60405180910390fd5b6066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091856040518363ffffffff1660e01b8152600401611f80929190615262565b602060405180830381865af4158015611f9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc191906152b7565b1561200357826040517fbd133eee000000000000000000000000000000000000000000000000000000008152600401611ffa91906147fb565b60405180910390fd5b600080606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c494ec1e606c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663acd201d0886040518263ffffffff1660e01b815260040161209f91906147fb565b602060405180830381865afa1580156120bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120e09190615015565b6040518263ffffffff1660e01b81526004016120fc9190614732565b602060405180830381865afa158015612119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213d9190615015565b90506000811461220e576000606d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166356ab819f876040518263ffffffff1660e01b81526004016121a491906147fb565b606060405180830381865afa1580156121c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e59190615387565b505090506121f38282612ead565b826121fe91906150e2565b925061220c86846001612ec6565b505b6066739a272d69f1b48acc648d454db78723cf8a220d9e632dedbbf09091878588886040518663ffffffff1660e01b815260040161225095949392919061520f565b60006040518083038186803b15801561226857600080fd5b505af415801561227c573d6000803e3d6000fd5b505050508473ffffffffffffffffffffffffffffffffffffffff167f0503b4748a47435f2432d46ef300e0e2dc47baa0e5f04bb3bd8a355ee1e1dbe660405160405180910390a25050505050565b6122d2613410565b73ffffffffffffffffffffffffffffffffffffffff166122f0611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614612346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161233d90615367565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614806123ad5750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b806123e45750600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b1561241b576040517f0855380c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81606b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080606d60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082606c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050565b60008060006066739a272d69f1b48acc648d454db78723cf8a220d9e630c8f298790916040518263ffffffff1660e01b81526004016125229190614fe5565b602060405180830381865af415801561253f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125639190615057565b905060006066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b81526004016125a19190614fe5565b602060405180830381865af41580156125be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e29190615015565b9050806071546125f29190615409565b93508173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614156126555760008185612636919061543a565b60715461264391906150e2565b905080856126519190615494565b9450505b606e60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205492505050915091565b60006066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b81526004016126da9190614fe5565b602060405180830381865af41580156126f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061271b9190615015565b905090565b6000806066739a272d69f1b48acc648d454db78723cf8a220d9e6349923bff90916040518263ffffffff1660e01b815260040161275d9190614fe5565b602060405180830381865af415801561277a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061279e9190615057565b91506127b4826066612bab90919063ffffffff16565b9091509050809150509091565b606d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6127ef613410565b73ffffffffffffffffffffffffffffffffffffffff1661280d611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614612863576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161285a90615367565b60405180910390fd5b61286c816138e3565b50565b600061287b60746133fb565b905090565b606b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ac8f4425826040518263ffffffff1660e01b81526004016128db91906147fb565b602060405180830381865afa1580156128f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291c91906152b7565b1561295e57806040517fe3bd013b00000000000000000000000000000000000000000000000000000000815260040161295591906147fb565b60405180910390fd5b61296781613418565b50565b6060803373ffffffffffffffffffffffffffffffffffffffff16606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015612a1957503373ffffffffffffffffffffffffffffffffffffffff16606d60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b15612a5b57336040517f79eaeb2b000000000000000000000000000000000000000000000000000000008152600401612a5291906147fb565b60405180910390fd5b612a6584846139d1565b915091509250929050565b60705481565b612a7e613410565b73ffffffffffffffffffffffffffffffffffffffff16612a9c611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614612af2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ae990615367565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612b62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b599061555c565b60405180910390fd5b612b6b8161327a565b50565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000612ba08360000183614190565b60001c905092915050565b600080600080612bcc612bbd866141bb565b876141e290919063ffffffff16565b905080604001519350806040015115612bfe57612bec8160000151614208565b9250612bfb8160200151614208565b91505b509250925092565b6000612c347f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b614219565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b612c65613410565b73ffffffffffffffffffffffffffffffffffffffff16612c83611c8d565b73ffffffffffffffffffffffffffffffffffffffff1614612cd9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cd090615367565b60405180910390fd5b50565b6000612ce6612c06565b9050612cf184614223565b600083511180612cfe5750815b15612d0f57612d0d84846142dc565b505b6000612d3d7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b614309565b90508060000160009054906101000a900460ff16612ea65760018160000160006101000a81548160ff021916908315150217905550612e098583604051602401612d8791906147fb565b6040516020818303038152906040527f3659cfe6000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506142dc565b5060008160000160006101000a81548160ff021916908315150217905550612e2f612c06565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614612e9c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e93906155ee565b60405180910390fd5b612ea585614313565b5b5050505050565b6000818310612ebc5781612ebe565b825b905092915050565b8015612f405781606e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612f1b9190615494565b925050819055508160716000828254612f349190615494565b92505081905550612fb0565b81606e60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612f8f91906150e2565b925050819055508160716000828254612fa891906150e2565b925050819055505b505050565b612fc983607461339b90919063ffffffff16565b15612fd357613264565b60008082613062576066739a272d69f1b48acc648d454db78723cf8a220d9e63a4aadfcc909187876072546040518563ffffffff1660e01b815260040161301d949392919061560e565b6040805180830381865af4158015613039573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061305d9190615653565b6130e5565b6066739a272d69f1b48acc648d454db78723cf8a220d9e6399f68a39909187876072546040518563ffffffff1660e01b81526004016130a4949392919061560e565b6040805180830381865af41580156130c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130e49190615653565b5b915091508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614158061319e575060016066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b815260040161315b9190614fe5565b602060405180830381865af4158015613178573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061319c9190615015565b145b1561321a576066739a272d69f1b48acc648d454db78723cf8a220d9e63cab455ae9091878786866040518663ffffffff1660e01b81526004016131e595949392919061520f565b60006040518083038186803b1580156131fd57600080fd5b505af4158015613211573d6000803e3d6000fd5b50505050613261565b607360009054906101000a900460ff161561324b576000607360006101000a81548160ff0219169083151502179055505b61325f85607461436290919063ffffffff16565b505b50505b505050565b600061327430612b6e565b15905090565b6000603360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081603360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600060019054906101000a900460ff1661338f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161338690615705565b60405180910390fd5b613398816138e3565b50565b60006133c3836000018373ffffffffffffffffffffffffffffffffffffffff1660001b614392565b905092915050565b60006133f3836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6143b5565b905092915050565b6000613409826000016144c9565b9050919050565b600033905090565b6066739a272d69f1b48acc648d454db78723cf8a220d9e6302f130289091836040518363ffffffff1660e01b8152600401613454929190615262565b602060405180830381865af4158015613471573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349591906152b7565b6134d657806040517f1adbb1530000000000000000000000000000000000000000000000000000000081526004016134cd91906147fb565b60405180910390fd5b6066739a272d69f1b48acc648d454db78723cf8a220d9e63281359299091836040518363ffffffff1660e01b8152600401613512929190615262565b60006040518083038186803b15801561352a57600080fd5b505af415801561353e573d6000803e3d6000fd5b505050506135568160746133cb90919063ffffffff16565b506000606e60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600081111561389c576135b082826000612ec6565b6000600167ffffffffffffffff8111156135cd576135cc614adc565b5b6040519080825280602002602001820160405280156135fb5781602001602082028036833780820191505090505b5090506000600167ffffffffffffffff81111561361b5761361a614adc565b5b6040519080825280602002602001820160405280156136495781602001602082028036833780820191505090505b509050838260008151811061366157613660615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630567847f846040518263ffffffff1660e01b81526004016136f69190614732565b602060405180830381865afa158015613713573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137379190615015565b8160008151811061374b5761374a615084565b5b602002602001018181525050600080613800606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630567847f876040518263ffffffff1660e01b81526004016137b89190614732565b602060405180830381865afa1580156137d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f99190615015565b60006139d1565b91509150606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630c4d4e40858486856040518563ffffffff1660e01b81526004016138659493929190615725565b600060405180830381600087803b15801561387f57600080fd5b505af1158015613893573d6000803e3d6000fd5b50505050505050505b8173ffffffffffffffffffffffffffffffffffffffff167fa78a88a551e130ce9732be17784bdff12f72ab4a2c833fb2dcb3ef0818956b0360405160405180910390a25050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561394a576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80606560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa6960405160405180910390a250565b60608060006066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b8152600401613a109190614fe5565b602060405180830381865af4158015613a2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a519190615015565b1415613a89576040517f7818a60e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000613b11606f546066739a272d69f1b48acc648d454db78723cf8a220d9e6365da149690916040518263ffffffff1660e01b8152600401613acb9190614fe5565b602060405180830381865af4158015613ae8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b0c9190615015565b612ead565b905060008167ffffffffffffffff811115613b2f57613b2e614adc565b5b604051908082528060200260200182016040528015613b5d5781602001602082028036833780820191505090505b50905060008267ffffffffffffffff811115613b7c57613b7b614adc565b5b604051908082528060200260200182016040528015613baa5781602001602082028036833780820191505090505b50905060006066739a272d69f1b48acc648d454db78723cf8a220d9e6349923bff90916040518263ffffffff1660e01b8152600401613be99190614fe5565b602060405180830381865af4158015613c06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c2a9190615057565b905060005b8481108015613c3f575060008914155b8015613c785750600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b15613ff3576000606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632c431058846040518263ffffffff1660e01b8152600401613cda91906147fb565b602060405180830381865afa158015613cf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1b9190615015565b90508873ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161480613d575750600081145b15613d7f57613d70836066612bab90919063ffffffff16565b90915090508093505050613c2f565b613d89818b612ead565b848381518110613d9c57613d9b615084565b5b60200260200101818152505082858381518110613dbc57613dbb615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050838281518110613e0957613e08615084565b5b60200260200101518a613e1c91906150e2565b9950613edf83606560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c494ec1e878681518110613e7357613e72615084565b5b60200260200101516040518263ffffffff1660e01b8152600401613e979190614732565b602060405180830381865afa158015613eb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ed89190615015565b6001612ec6565b613f2a83606e60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001612fb5565b607360009054906101000a900460ff1615613fc1576066739a272d69f1b48acc648d454db78723cf8a220d9e6349923bff90916040518263ffffffff1660e01b8152600401613f799190614fe5565b602060405180830381865af4158015613f96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fba9190615057565b9250613fdf565b613fd5836066612bab90919063ffffffff16565b9091509050809350505b8180613fea90615116565b92505050613c2f565b6000891461402d576040517f2e6c1bf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff81111561404757614046614adc565b5b6040519080825280602002602001820160405280156140755781602001602082028036833780820191505090505b5096508067ffffffffffffffff81111561409257614091614adc565b5b6040519080825280602002602001820160405280156140c05781602001602082028036833780820191505090505b50955060005b81811015614183578481815181106140e1576140e0615084565b5b60200260200101518882815181106140fc576140fb615084565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505083818151811061414957614148615084565b5b602002602001015187828151811061416457614163615084565b5b602002602001018181525050808061417b90615116565b9150506140c6565b5050505050509250929050565b60008260000182815481106141a8576141a7615084565b5b9060005260206000200154905092915050565b600060608273ffffffffffffffffffffffffffffffffffffffff16901b60001b9050919050565b6141ea6146f0565b61420082846000016144da90919063ffffffff16565b905092915050565b600060608260001c901c9050919050565b6000819050919050565b61422c81614539565b61426b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401614262906157f8565b60405180910390fd5b806142987f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b614219565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b606061430183836040518060600160405280602781526020016159d16027913961454c565b905092915050565b6000819050919050565b61431c81614223565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b600061438a836000018373ffffffffffffffffffffffffffffffffffffffff1660001b614619565b905092915050565b600080836001016000848152602001908152602001600020541415905092915050565b600080836001016000848152602001908152602001600020549050600081146144bd5760006001826143e791906150e2565b90506000600186600001805490506143ff91906150e2565b905081811461446e5760008660000182815481106144205761441f615084565b5b906000526020600020015490508087600001848154811061444457614443615084565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b8560000180548061448257614481615818565b5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506144c3565b60009150505b92915050565b600081600001805490509050919050565b6144e26146f0565b82600301600083815260200190815260200160002060405180606001604052908160008201548152602001600182015481526020016002820160009054906101000a900460ff161515151581525050905092915050565b600080823b905060008111915050919050565b606061455784614539565b614596576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161458d906158b9565b60405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff16856040516145be9190615953565b600060405180830381855af49150503d80600081146145f9576040519150601f19603f3d011682016040523d82523d6000602084013e6145fe565b606091505b509150915061460e828286614689565b925050509392505050565b60006146258383614392565b61467e578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050614683565b600090505b92915050565b60608315614699578290506146e9565b6000835111156146ac5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016146e091906159ae565b60405180910390fd5b9392505050565b604051806060016040528060008019168152602001600080191681526020016000151581525090565b6000819050919050565b61472c81614719565b82525050565b60006020820190506147476000830184614723565b92915050565b6000604051905090565b600080fd5b600080fd5b61476a81614719565b811461477557600080fd5b50565b60008135905061478781614761565b92915050565b6000602082840312156147a3576147a2614757565b5b60006147b184828501614778565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006147e5826147ba565b9050919050565b6147f5816147da565b82525050565b600060208201905061481060008301846147ec565b92915050565b61481f816147da565b811461482a57600080fd5b50565b60008135905061483c81614816565b92915050565b60006020828403121561485857614857614757565b5b60006148668482850161482d565b91505092915050565b600060408201905061488460008301856147ec565b61489160208301846147ec565b9392505050565b60008115159050919050565b6148ad81614898565b82525050565b60006020820190506148c860008301846148a4565b92915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b614903816147da565b82525050565b600061491583836148fa565b60208301905092915050565b6000602082019050919050565b6000614939826148ce565b61494381856148d9565b935061494e836148ea565b8060005b8381101561497f5781516149668882614909565b975061497183614921565b925050600181019050614952565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6149c181614719565b82525050565b60006149d383836149b8565b60208301905092915050565b6000602082019050919050565b60006149f78261498c565b614a018185614997565b9350614a0c836149a8565b8060005b83811015614a3d578151614a2488826149c7565b9750614a2f836149df565b925050600181019050614a10565b5085935050505092915050565b60006040820190508181036000830152614a64818561492e565b90508181036020830152614a7881846149ec565b90509392505050565b60008060408385031215614a9857614a97614757565b5b6000614aa68582860161482d565b9250506020614ab78582860161482d565b9150509250929050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b614b1482614acb565b810181811067ffffffffffffffff82111715614b3357614b32614adc565b5b80604052505050565b6000614b4661474d565b9050614b528282614b0b565b919050565b600067ffffffffffffffff821115614b7257614b71614adc565b5b614b7b82614acb565b9050602081019050919050565b82818337600083830152505050565b6000614baa614ba584614b57565b614b3c565b905082815260208101848484011115614bc657614bc5614ac6565b5b614bd1848285614b88565b509392505050565b600082601f830112614bee57614bed614ac1565b5b8135614bfe848260208601614b97565b91505092915050565b60008060408385031215614c1e57614c1d614757565b5b6000614c2c8582860161482d565b925050602083013567ffffffffffffffff811115614c4d57614c4c61475c565b5b614c5985828601614bd9565b9150509250929050565b600080600060608486031215614c7c57614c7b614757565b5b6000614c8a8682870161482d565b9350506020614c9b8682870161482d565b9250506040614cac8682870161482d565b9150509250925092565b6000608082019050614ccb6000830187614723565b614cd86020830186614723565b614ce56040830185614723565b614cf26060830184614723565b95945050505050565b6000819050919050565b6000614d20614d1b614d16846147ba565b614cfb565b6147ba565b9050919050565b6000614d3282614d05565b9050919050565b6000614d4482614d27565b9050919050565b614d5481614d39565b82525050565b6000602082019050614d6f6000830184614d4b565b92915050565b600080600060608486031215614d8e57614d8d614757565b5b6000614d9c86828701614778565b9350506020614dad86828701614778565b9250506040614dbe86828701614778565b9150509250925092565b6000614dd382614d27565b9050919050565b614de381614dc8565b82525050565b6000602082019050614dfe6000830184614dda565b92915050565b6000604082019050614e196000830185614723565b614e266020830184614723565b9392505050565b6000614e3882614d27565b9050919050565b614e4881614e2d565b82525050565b6000602082019050614e636000830184614e3f565b92915050565b60008060408385031215614e8057614e7f614757565b5b6000614e8e85828601614778565b9250506020614e9f8582860161482d565b9150509250929050565b600082825260208201905092915050565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f64656c656761746563616c6c0000000000000000000000000000000000000000602082015250565b6000614f16602c83614ea9565b9150614f2182614eba565b604082019050919050565b60006020820190508181036000830152614f4581614f09565b9050919050565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f6163746976652070726f78790000000000000000000000000000000000000000602082015250565b6000614fa8602c83614ea9565b9150614fb382614f4c565b604082019050919050565b60006020820190508181036000830152614fd781614f9b565b9050919050565b8082525050565b6000602082019050614ffa6000830184614fde565b92915050565b60008151905061500f81614761565b92915050565b60006020828403121561502b5761502a614757565b5b600061503984828501615000565b91505092915050565b60008151905061505181614816565b92915050565b60006020828403121561506d5761506c614757565b5b600061507b84828501615042565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006150ed82614719565b91506150f883614719565b92508282101561510b5761510a6150b3565b5b828203905092915050565b600061512182614719565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415615154576151536150b3565b5b600182019050919050565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b60006151bb602e83614ea9565b91506151c68261515f565b604082019050919050565b600060208201905081810360008301526151ea816151ae565b9050919050565b6151fa816147da565b82525050565b61520981614719565b82525050565b600060a0820190506152246000830188614fde565b61523160208301876151f1565b61523e6040830186615200565b61524b60608301856151f1565b61525860808301846151f1565b9695505050505050565b60006040820190506152776000830185614fde565b61528460208301846151f1565b9392505050565b61529481614898565b811461529f57600080fd5b50565b6000815190506152b18161528b565b92915050565b6000602082840312156152cd576152cc614757565b5b60006152db848285016152a2565b91505092915050565b60006060820190506152f960008301866147ec565b6153066020830185614723565b6153136040830184614723565b949350505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000615351602083614ea9565b915061535c8261531b565b602082019050919050565b6000602082019050818103600083015261538081615344565b9050919050565b6000806000606084860312156153a05761539f614757565b5b60006153ae86828701615000565b93505060206153bf86828701615000565b92505060406153d086828701615000565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061541482614719565b915061541f83614719565b92508261542f5761542e6153da565b5b828204905092915050565b600061544582614719565b915061545083614719565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615489576154886150b3565b5b828202905092915050565b600061549f82614719565b91506154aa83614719565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156154df576154de6150b3565b5b828201905092915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000615546602683614ea9565b9150615551826154ea565b604082019050919050565b6000602082019050818103600083015261557581615539565b9050919050565b7f45524331393637557067726164653a207570677261646520627265616b73206660008201527f7572746865722075706772616465730000000000000000000000000000000000602082015250565b60006155d8602f83614ea9565b91506155e38261557c565b604082019050919050565b60006020820190508181036000830152615607816155cb565b9050919050565b60006080820190506156236000830187614fde565b61563060208301866151f1565b61563d6040830185615200565b61564a6060830184615200565b95945050505050565b6000806040838503121561566a57615669614757565b5b600061567885828601615042565b925050602061568985828601615042565b9150509250929050565b7f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960008201527f6e697469616c697a696e67000000000000000000000000000000000000000000602082015250565b60006156ef602b83614ea9565b91506156fa82615693565b604082019050919050565b6000602082019050818103600083015261571e816156e2565b9050919050565b6000608082019050818103600083015261573f818761492e565b90508181036020830152615753818661492e565b9050818103604083015261576781856149ec565b9050818103606083015261577b81846149ec565b905095945050505050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b60006157e2602d83614ea9565b91506157ed82615786565b604082019050919050565b60006020820190508181036000830152615811816157d5565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b60006158a3602683614ea9565b91506158ae82615847565b604082019050919050565b600060208201905081810360008301526158d281615896565b9050919050565b600081519050919050565b600081905092915050565b60005b8381101561590d5780820151818401526020810190506158f2565b8381111561591c576000848401525b50505050565b600061592d826158d9565b61593781856158e4565b93506159478185602086016158ef565b80840191505092915050565b600061595f8284615922565b915081905092915050565b600081519050919050565b60006159808261596a565b61598a8185614ea9565b935061599a8185602086016158ef565b6159a381614acb565b840191505092915050565b600060208201905081810360008301526159c88184615975565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220a1703fc71954cbb1a1560d4385dc2ddecc730aa26a4d16d5f09db1ec2697648464736f6c634300080b0033