Address Details
contract

0x861E032D88a2a64CD7fc4e1Dc8943740EDd5E7E9

Contract Name
MultiSig
Creator
0x5bc1c4–68a788 at 0x11c334–05628d
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
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
15095696
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
MultiSig




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




EVM Version
istanbul




Verified at
2022-09-30T11:14:16.413895Z

contracts/common/MultiSig.sol

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

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

import "../libraries/ExternalCall.sol";

/**
 * @title Multisignature wallet - Allows multiple parties to agree on proposals before
 * execution.
 * @author Stefan George - <stefan.george@consensys.net>
 * @dev NOTE: This contract has its limitations and is not viable for every
 * multi-signature setup. On a case by case basis, evaluate whether this is the
 * correct contract for your use case.
 * In particular, this contract doesn't have an atomic "add owners and increase
 * requirement" operation.
 * This can be tricky, for example, in a situation where a MultiSig starts out
 * owned by a single owner. Safely increasing the owner set and requirement at
 * the same time is not trivial. One way to work around this situation is to
 * first add a second address controlled by the original owner, increase the
 * requirement, and then replace the auxillary address with the intended second
 * owner.
 * Again, this is just one example, in general make sure to verify this contract
 * will support your intended usage. The goal of this contract is to offer a
 * simple, minimal multi-signature API that's easy to understand even for novice
 * Solidity users.
 * Forked from
 * github.com/celo-org/celo-monorepo/blob/master/packages/protocol/contracts/common/MultiSig.sol
 */
contract MultiSig is Initializable, UUPSUpgradeable {
    using EnumerableSet for EnumerableSet.AddressSet;

    /**
     * @notice The maximum number of multisig owners.
     */
    uint256 public constant MAX_OWNER_COUNT = 50;

    /**
     * @notice The minimum time in seconds that must elapse before a proposal is executable.
     */
    uint256 public immutable minDelay;

    /**
     * @notice The value used to mark a proposal as executed.
     */
    uint256 internal constant DONE_TIMESTAMP = uint256(1);

    /**
     * @notice Used to keep track of a proposal.
     * @param destinations The addresses at which the proposal is directed to.
     * @param values The amounts of CELO involved.
     * @param payloads The payloads of the proposal.
     * @param timestampExecutable The timestamp at which a proposal becomes executable.
     * @dev timestampExecutable is 0 if proposal is not yet scheduled or 1 if the proposal
     * is executed.
     * @param confirmations The list of confirmations. Keyed by the address that
     * confirmed the proposal, whether or not the proposal is confirmed.
     */
    struct Proposal {
        address[] destinations;
        uint256[] values;
        bytes[] payloads;
        uint256 timestampExecutable;
        mapping(address => bool) confirmations;
    }

    /**
     * @notice The delay that must elapse to be able to execute a proposal.
     */
    uint256 public delay;

    /**
     * @notice Keyed by proposal ID, the Proposal record.
     */
    mapping(uint256 => Proposal) public proposals;

    /**
     * @notice The set of addresses which are owners of the multisig.
     */
    EnumerableSet.AddressSet private owners;

    /**
     * @notice The amount of confirmations required
     * for a proposal to be fully confirmed.
     */
    uint256 public required;

    /**
     * @notice The total count of proposals.
     */
    uint256 public proposalCount;

    /**
     * @notice Used when a proposal is successfully confirmed.
     * @param sender The address of the sender.
     * @param proposalId The ID of the proposal.
     */
    event ProposalConfirmed(address indexed sender, uint256 indexed proposalId);

    /**
     * @notice Used when a confirmation is successfully revoked.
     * @param sender The address of the sender.
     * @param proposalId The ID of the proposal.
     */
    event ConfirmationRevoked(address indexed sender, uint256 indexed proposalId);

    /**
     * @notice Used when a proposal is successfully added.
     * @param proposalId The ID of the proposal that was added.
     */
    event ProposalAdded(uint256 indexed proposalId);

    /**
     * @notice Emitted when a confirmed proposal is successfully executed.
     * @param proposalId The ID of the proposal that was executed.
     * @param returnData The response that was recieved from the external call.
     */
    event ProposalExecuted(uint256 indexed proposalId, bytes returnData);

    /**
     * @notice Emitted when one of the transactions that make up a proposal is successfully
     * executed.
     * @param index The index of the transaction within the proposal.
     * @param proposalId The ID of the proposal.
     * @param returnData The response that was recieved from the external call.
     */
    event TransactionExecuted(uint256 index, uint256 indexed proposalId, bytes returnData);

    /**
     * @notice Emitted when CELO is sent to this contract.
     * @param sender The account which sent the CELO.
     * @param value The amount of CELO sent.
     */
    event CeloDeposited(address indexed sender, uint256 value);

    /**
     * @notice Emitted when an Owner is successfully added as part of the multisig.
     * @param owner The added owner.
     */
    event OwnerAdded(address indexed owner);

    /**
     * @notice Emitted when an Owner is successfully removed from the multisig.
     * @param owner The removed owner.
     */
    event OwnerRemoved(address indexed owner);

    /**
     * @notice Emitted when the minimum amount of required confirmations is
     * successfully changed.
     * @param required The new required amount.
     */
    event RequirementChanged(uint256 required);

    /**
     * @notice Emitted when a proposal is scheduled.
     * @param proposalId The ID of the proposal that is scheduled.
     */
    event ProposalScheduled(uint256 indexed proposalId);

    /**
     * @notice Used when `delay` is changed.
     * @param delay The current delay value.
     * @param newDelay The new delay value.
     */
    event DelayChanged(uint256 delay, uint256 newDelay);

    /**
     * @notice Used when sender is not this contract in an `onlyWallet` function.
     * @param account The sender which triggered the function.
     */
    error SenderMustBeMultisigWallet(address account);

    /**
     * @notice Used when attempting to add an already existing owner.
     * @param owner The address of the owner.
     */
    error OwnerAlreadyExists(address owner);

    /**
     * @notice Used when an owner does not exist.
     * @param owner The address of the owner.
     */
    error OwnerDoesNotExist(address owner);

    /**
     * @notice Used when a proposal does not exist.
     * @param proposalId The ID of the non-existent proposal.
     */
    error ProposalDoesNotExist(uint256 proposalId);

    /**
     * @notice Used when a proposal is not confirmed by a given owner.
     * @param proposalId The ID of the proposal that is not confirmed.
     * @param owner The address of the owner which did not confirm the proposal.
     */
    error ProposalNotConfirmed(uint256 proposalId, address owner);

    /**
     * @notice Used when a proposal is not fully confirmed.
     * @dev A proposal is fully confirmed when the `required` threshold
     * of confirmations has been met.
     * @param proposalId The ID of the proposal that is not fully confirmed.
     */
    error ProposalNotFullyConfirmed(uint256 proposalId);

    /**
     * @notice Used when a proposal is already confirmed by an owner.
     * @param proposalId The ID of the proposal that is already confirmed.
     * @param owner The address of the owner which confirmed the proposal.
     */
    error ProposalAlreadyConfirmed(uint256 proposalId, address owner);

    /**
     * @notice Used when a proposal has been executed.
     * @param proposalId The ID of the proposal that is already executed.
     */
    error ProposalAlreadyExecuted(uint256 proposalId);

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

    /**
     * @notice Used when the set threshold values for owner and minimum
     * required confirmations are not met.
     * @param ownerCount The count of owners.
     * @param required The number of required confirmations.
     */
    error InvalidRequirement(uint256 ownerCount, uint256 required);

    /**
     * @notice Used when attempting to remove the last owner.
     * @param owner The last owner.
     */
    error CannotRemoveLastOwner(address owner);

    /**
     * @notice Used when attempting to schedule an already scheduled proposal.
     * @param proposalId The ID of the proposal which is already scheduled.
     */
    error ProposalAlreadyScheduled(uint256 proposalId);

    /**
     * @notice Used when a proposal is not scheduled.
     * @param proposalId The ID of the proposal which is not scheduled.
     */
    error ProposalNotScheduled(uint256 proposalId);

    /**
     * @notice Used when a time lock delay is not reached.
     * @param proposalId The ID of the proposal whose time lock has not been reached yet.
     */
    error ProposalTimelockNotReached(uint256 proposalId);

    /**
     * @notice Used when a provided value is less than the minimum time lock delay.
     * @param delay The insufficient delay.
     */
    error InsufficientDelay(uint256 delay);

    /**
     * @notice Used when the sizes of the provided arrays params do not match
     * when submitting a proposal.
     */
    error ParamLengthsMismatch();

    /**
     * @notice Checks that only the multisig contract can execute a function.
     */
    modifier onlyWallet() {
        if (msg.sender != address(this)) {
            revert SenderMustBeMultisigWallet(msg.sender);
        }
        _;
    }

    /**
     * @notice Checks that an address is not a multisig owner.
     * @param owner The address to check.
     */
    modifier ownerDoesNotExist(address owner) {
        if (owners.contains(owner)) {
            revert OwnerAlreadyExists(owner);
        }
        _;
    }

    /**
     * @notice Checks that an address is a multisig owner.
     * @param owner The address to check.
     */
    modifier ownerExists(address owner) {
        if (!owners.contains(owner)) {
            revert OwnerDoesNotExist(owner);
        }
        _;
    }

    /**
     * @notice Checks that a proposal exists.
     * @param proposalId The proposal ID to check.
     */
    modifier proposalExists(uint256 proposalId) {
        if (proposals[proposalId].destinations.length == 0) {
            revert ProposalDoesNotExist(proposalId);
        }
        _;
    }

    /**
     * @notice Checks that a proposal has been confirmed by a multisig owner.
     * @param proposalId The proposal ID to check.
     * @param owner The owner to check.
     */
    modifier confirmed(uint256 proposalId, address owner) {
        if (!proposals[proposalId].confirmations[owner]) {
            revert ProposalNotConfirmed(proposalId, owner);
        }
        _;
    }

    /**
     * @notice Checks that a proposal has not been confirmed by a multisig owner.
     * @param proposalId The proposal ID to check.
     * @param owner The owner to check.
     */
    modifier notConfirmed(uint256 proposalId, address owner) {
        if (proposals[proposalId].confirmations[owner]) {
            revert ProposalAlreadyConfirmed(proposalId, owner);
        }
        _;
    }

    /**
     * @notice Checks that a proposal has not been executed.
     * @dev A proposal can only be executed after it is fully confirmed.
     * @param proposalId The proposal ID to check.
     */
    modifier notExecuted(uint256 proposalId) {
        if (proposals[proposalId].timestampExecutable == DONE_TIMESTAMP) {
            revert ProposalAlreadyExecuted(proposalId);
        }
        _;
    }

    /**
     * @notice Checks that an address is not address(0).
     * @param addr The address to check.
     */
    modifier notNull(address addr) {
        if (addr == address(0)) {
            revert NullAddress();
        }
        _;
    }

    /**
     * @notice Checks that each address in a batch of addresses are not address(0).
     * @param _addresses The addresses to check.
     */
    modifier notNullBatch(address[] memory _addresses) {
        for (uint256 i = 0; i < _addresses.length; i++) {
            if (_addresses[i] == address(0)) {
                revert NullAddress();
            }
        }
        _;
    }

    /**
     * @notice Checks that the values passed for number of multisig owners and required
     * confirmation are valid in comparison with the configured thresholds.
     * @param ownerCount The owners count to check.
     * @param requiredConfirmations The minimum number of confirmations required to consider
     * a proposal as fully confirmed.
     */
    modifier validRequirement(uint256 ownerCount, uint256 requiredConfirmations) {
        if (
            ownerCount > MAX_OWNER_COUNT ||
            requiredConfirmations > ownerCount ||
            requiredConfirmations == 0 ||
            ownerCount == 0
        ) {
            revert InvalidRequirement(ownerCount, requiredConfirmations);
        }
        _;
    }

    /**
     * @notice Checks that a proposal is scheduled.
     * @param proposalId The ID of the proposal to check.
     */
    modifier scheduled(uint256 proposalId) {
        if (!isScheduled(proposalId)) {
            revert ProposalNotScheduled(proposalId);
        }
        _;
    }

    /**
     * @notice Checks that a proposal is not scheduled.
     * @param proposalId The ID of the proposal to check.
     */
    modifier notScheduled(uint256 proposalId) {
        if (isScheduled(proposalId)) {
            revert ProposalAlreadyScheduled(proposalId);
        }
        _;
    }

    /**
     * @notice Checks that a proposal's time lock has elapsed.
     * @param proposalId The ID of the proposal to check.
     */
    modifier timeLockReached(uint256 proposalId) {
        if (!isProposalTimelockReached(proposalId)) {
            revert ProposalTimelockNotReached(proposalId);
        }
        _;
    }

    /**
     * @notice Checks that a proposal is fully confirmed.
     * @param proposalId The ID of the proposal to check.
     */
    modifier fullyConfirmed(uint256 proposalId) {
        if (!isFullyConfirmed(proposalId)) {
            revert ProposalNotFullyConfirmed(proposalId);
        }
        _;
    }

    /**
     * @notice Sets `initialized` to  true on implementation contracts.
     * @param _minDelay The minimum time in seconds that must elapse before a
     * proposal is executable.
     */
    // solhint-disable-next-line no-empty-blocks
    constructor(uint256 _minDelay) initializer {
        minDelay = _minDelay;
    }

    receive() external payable {
        if (msg.value > 0) {
            emit CeloDeposited(msg.sender, msg.value);
        }
    }

    /**
     * @notice Bootstraps this contract with initial data.
     * @dev This plays the role of a typical contract constructor. Sets initial owners and
     * required number of confirmations. The initializer modifier ensures that this function
     * is ONLY callable once.
     * @param initialOwners The list of initial owners.
     * @param requiredConfirmations The number of required confirmations for a proposal
     * to be fully confirmed.
     * @param _delay The delay that must elapse to be able to execute a proposal.
     */
    function initialize(
        address[] calldata initialOwners,
        uint256 requiredConfirmations,
        uint256 _delay
    ) external initializer validRequirement(initialOwners.length, requiredConfirmations) {
        for (uint256 i = 0; i < initialOwners.length; i++) {
            if (owners.contains(initialOwners[i])) {
                revert OwnerAlreadyExists(initialOwners[i]);
            }

            if (initialOwners[i] == address(0)) {
                revert NullAddress();
            }

            owners.add(initialOwners[i]);
            emit OwnerAdded(initialOwners[i]);
        }
        _changeRequirement(requiredConfirmations);
        _changeDelay(_delay);
    }

    /**
     * @notice Adds a new multisig owner.
     * @dev This call can only be made by this contract.
     * @param owner The owner to add.
     */
    function addOwner(address owner)
        external
        onlyWallet
        ownerDoesNotExist(owner)
        notNull(owner)
        validRequirement(owners.length() + 1, required)
    {
        owners.add(owner);
        emit OwnerAdded(owner);
    }

    /**
     * @notice Removes an existing owner.
     * @dev This call can only be made by this contract.
     * @param owner The owner to remove.
     */
    function removeOwner(address owner) external onlyWallet ownerExists(owner) {
        if (owners.length() == 1) {
            revert CannotRemoveLastOwner(owner);
        }

        owners.remove(owner);

        if (required > owners.length()) {
            // Readjust the required amount, since the list of total owners has reduced.
            changeRequirement(owners.length());
        }
        emit OwnerRemoved(owner);
    }

    /**
     * @notice Replaces an existing owner with a new owner.
     * @dev This call can only be made by this contract.
     * @param owner The owner to be replaced.
     */
    function replaceOwner(address owner, address newOwner)
        external
        onlyWallet
        ownerExists(owner)
        notNull(newOwner)
        ownerDoesNotExist(newOwner)
    {
        owners.remove(owner);
        owners.add(newOwner);
        emit OwnerRemoved(owner);
        emit OwnerAdded(newOwner);
    }

    /**
     * @notice Void a confirmation for a previously confirmed proposal.
     * @param proposalId The ID of the proposal to be revoked.
     */
    function revokeConfirmation(uint256 proposalId)
        external
        ownerExists(msg.sender)
        confirmed(proposalId, msg.sender)
        notExecuted(proposalId)
    {
        proposals[proposalId].confirmations[msg.sender] = false;
        emit ConfirmationRevoked(msg.sender, proposalId);
    }

    /**
     * @notice Creates a proposal and triggers the first confirmation on behalf of the
     * proposal creator.
     * @param destinations The addresses at which the proposal is target at.
     * @param values The CELO values involved in the proposal if any.
     * @param payloads The payloads of the proposal.
     * @return proposalId Returns the ID of the proposal that gets generated.
     */
    function submitProposal(
        address[] calldata destinations,
        uint256[] calldata values,
        bytes[] calldata payloads
    ) external returns (uint256 proposalId) {
        if (destinations.length != values.length) {
            revert ParamLengthsMismatch();
        }

        if (destinations.length != payloads.length) {
            revert ParamLengthsMismatch();
        }
        proposalId = addProposal(destinations, values, payloads);
        confirmProposal(proposalId);
    }

    /**
     * @notice Get the list of multisig owners.
     * @return The list of owner addresses.
     */
    function getOwners() external view returns (address[] memory) {
        return owners.values();
    }

    /**
     * @notice Gets the list of owners' addresses which have confirmed a given proposal.
     * @param proposalId The ID of the proposal.
     * @return The list of owner addresses.
     */
    function getConfirmations(uint256 proposalId) external view returns (address[] memory) {
        address[] memory confirmationsTemp = new address[](owners.length());
        uint256 count = 0;
        for (uint256 i = 0; i < owners.length(); i++) {
            if (proposals[proposalId].confirmations[owners.at(i)]) {
                confirmationsTemp[count] = owners.at(i);
                count++;
            }
        }
        address[] memory confirmingOwners = new address[](count);
        for (uint256 i = 0; i < count; i++) {
            confirmingOwners[i] = confirmationsTemp[i];
        }
        return confirmingOwners;
    }

    /**
     * @notice Gets the destinations, values and payloads of a proposal.
     * @param proposalId The ID of the proposal.
     * @param destinations The addresses at which the proposal is target at.
     * @param values The CELO values involved in the proposal if any.
     * @param payloads The payloads of the proposal.
     */
    function getProposal(uint256 proposalId)
        external
        view
        returns (
            address[] memory destinations,
            uint256[] memory values,
            bytes[] memory payloads
        )
    {
        Proposal storage proposal = proposals[proposalId];
        return (proposal.destinations, proposal.values, proposal.payloads);
    }

    /**
     * @notice Changes the number of confirmations required to consider a proposal
     * fully confirmed.
     * @dev Proposal has to be sent by wallet.
     * @param newRequired The new number of confirmations required.
     */
    function changeRequirement(uint256 newRequired)
        public
        onlyWallet
        validRequirement(owners.length(), newRequired)
    {
        _changeRequirement(newRequired);
    }

    /**
     * @notice Changes the value of the delay that must
     * elapse before a proposal can become executable.
     * @dev Proposal has to be sent by wallet.
     * @param newDelay The new delay value.
     */
    function changeDelay(uint256 newDelay) public onlyWallet {
        _changeDelay(newDelay);
    }

    /**
     * @notice Confirms a proposal. A proposal is executed if this confirmation
     * makes it fully confirmed.
     * @param proposalId The ID of the proposal to confirm.
     */
    function confirmProposal(uint256 proposalId)
        public
        ownerExists(msg.sender)
        proposalExists(proposalId)
        notConfirmed(proposalId, msg.sender)
    {
        proposals[proposalId].confirmations[msg.sender] = true;
        emit ProposalConfirmed(msg.sender, proposalId);
        if (isFullyConfirmed(proposalId)) {
            scheduleProposal(proposalId);
        }
    }

    /**
     * @notice Schedules a proposal with a time lock.
     * @param proposalId The ID of the proposal to confirm.
     */
    function scheduleProposal(uint256 proposalId)
        public
        ownerExists(msg.sender)
        notExecuted(proposalId)
    {
        schedule(proposalId);
        emit ProposalScheduled(proposalId);
    }

    /**
     * @notice Executes a proposal. A proposal is only executetable if it is fully confirmed,
     * scheduled and the set delay has elapsed.
     * @dev Any of the multisig owners can execute a given proposal, even though they may
     * not have participated in its confirmation process.
     */
    function executeProposal(uint256 proposalId)
        public
        ownerExists(msg.sender)
        scheduled(proposalId)
        notExecuted(proposalId)
        timeLockReached(proposalId)
    {
        Proposal storage proposal = proposals[proposalId];
        proposal.timestampExecutable = DONE_TIMESTAMP;

        for (uint256 i = 0; i < proposals[proposalId].destinations.length; i++) {
            bytes memory returnData = ExternalCall.execute(
                proposal.destinations[i],
                proposal.values[i],
                proposal.payloads[i]
            );
            emit TransactionExecuted(i, proposalId, returnData);
        }
    }

    /**
     * @notice Returns the timestamp at which a proposal becomes executable.
     * @param proposalId The ID of the proposal.
     * @return The timestamp at which the proposal becomes executable.
     */
    function getTimestamp(uint256 proposalId) public view returns (uint256) {
        return proposals[proposalId].timestampExecutable;
    }

    /**
     * @notice Returns whether a proposal is scheduled.
     * @param proposalId The ID of the proposal to check.
     * @return Whether or not the proposal is scheduled.
     */
    function isScheduled(uint256 proposalId) public view returns (bool) {
        return getTimestamp(proposalId) > DONE_TIMESTAMP;
    }

    /**
     * @notice Returns whether a proposal is executable or not.
     * A proposal is executable if it is scheduled, the delay has elapsed
     * and it is not yet executed.
     * @param proposalId The ID of the proposal to check.
     * @return Whether or not the time lock is reached.
     */
    function isProposalTimelockReached(uint256 proposalId) public view returns (bool) {
        uint256 timestamp = getTimestamp(proposalId);
        return
            timestamp <= block.timestamp &&
            proposals[proposalId].timestampExecutable > DONE_TIMESTAMP;
    }

    /**
     * @notice Checks that a proposal has been confirmed by at least the `required`
     * number of owners.
     * @param proposalId The ID of the proposal to check.
     * @return Whether or not the proposal is confirmed by the minimum set of owners.
     */
    function isFullyConfirmed(uint256 proposalId) public view returns (bool) {
        uint256 count = 0;
        for (uint256 i = 0; i < owners.length(); i++) {
            if (proposals[proposalId].confirmations[owners.at(i)]) {
                count++;
            }
            if (count == required) {
                return true;
            }
        }
        return false;
    }

    /**
     * @notice Checks that a proposal is confirmed by an owner.
     * @param proposalId The ID of the proposal to check.
     * @param owner The address to check.
     * @return Whether or not the proposal is confirmed by the given owner.
     */
    function isConfirmedBy(uint256 proposalId, address owner) public view returns (bool) {
        return proposals[proposalId].confirmations[owner];
    }

    /**
     * @notice Checks that an address is a multisig owner.
     * @param owner The address to check.
     * @return Whether or not the address is a multisig owner.
     */
    function isOwner(address owner) public view returns (bool) {
        return owners.contains(owner);
    }

    /**
     * @notice Adds a new proposal to the proposals list.
     * @param destinations The addresses at which the proposal is directed to.
     * @param values The CELO valuse involved in the proposal if any.
     * @param payloads The payloads of the proposal.
     * @return proposalId Returns the ID of the proposal that gets generated.
     */
    function addProposal(
        address[] memory destinations,
        uint256[] memory values,
        bytes[] memory payloads
    ) internal notNullBatch(destinations) returns (uint256 proposalId) {
        proposalId = proposalCount;
        Proposal storage proposal = proposals[proposalId];

        proposal.destinations = destinations;
        proposal.values = values;
        proposal.payloads = payloads;

        proposalCount++;
        emit ProposalAdded(proposalId);
    }

    /**
     * @notice Schedules a proposal with a time lock.
     * @param proposalId The ID of the proposal to schedule.
     */
    function schedule(uint256 proposalId)
        internal
        notScheduled(proposalId)
        fullyConfirmed(proposalId)
    {
        proposals[proposalId].timestampExecutable = block.timestamp + delay;
    }

    /**
     * @notice Changes the value of the delay that must
     * elapse before a proposal can become executable.
     * @param newDelay The new delay value.
     */
    function _changeDelay(uint256 newDelay) internal {
        if (newDelay < minDelay) {
            revert InsufficientDelay(newDelay);
        }

        delay = newDelay;
        emit DelayChanged(delay, newDelay);
    }

    /**
     * @notice Changes the number of confirmations required to consider a proposal
     * fully confirmed.
     * @dev This method does not do any validation, see `changeRequirement`
     * for how it is used with the requirement validation modifier.
     * @param newRequired The new number of confirmations required.
     */
    function _changeRequirement(uint256 newRequired) internal {
        required = newRequired;
        emit RequirementChanged(newRequired);
    }

    /**
     * @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 onlyWallet modifer.
     */
    // solhint-disable-next-line no-empty-blocks
    function _authorizeUpgrade(address) internal override onlyWallet {}
}
        

/_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/Initializable.sol

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

pragma solidity ^0.8.0;

import "../../utils/Address.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 a proxied contract can't have 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 !Address.isContract(address(this));
    }
}
          

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

/contracts/libraries/ExternalCall.sol

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

import "@openzeppelin/contracts/utils/Address.sol";

library ExternalCall {
    /**
     * @notice Used when destination is not a contract.
     * @param destination The invalid destination address.
     */
    error InvalidContractAddress(address destination);

    /**
     * @notice Used when an execution fails.
     */
    error ExecutionFailed();

    /**
     * @notice Executes external call.
     * @param destination The address to call.
     * @param value The CELO value to be sent.
     * @param data The data to be sent.
     * @return The call return value.
     */
    function execute(
        address destination,
        uint256 value,
        bytes memory data
    ) internal returns (bytes memory) {
        if (data.length > 0) {
            if (!Address.isContract(destination)) {
                revert InvalidContractAddress(destination);
            }
        }

        bool success;
        bytes memory returnData;
        // solhint-disable-next-line avoid-low-level-calls
        (success, returnData) = destination.call{value: value}(data);
        if (!success) {
            revert ExecutionFailed();
        }

        return returnData;
    }
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"uint256","name":"_minDelay","internalType":"uint256"}]},{"type":"error","name":"CannotRemoveLastOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"ExecutionFailed","inputs":[]},{"type":"error","name":"InsufficientDelay","inputs":[{"type":"uint256","name":"delay","internalType":"uint256"}]},{"type":"error","name":"InvalidContractAddress","inputs":[{"type":"address","name":"destination","internalType":"address"}]},{"type":"error","name":"InvalidRequirement","inputs":[{"type":"uint256","name":"ownerCount","internalType":"uint256"},{"type":"uint256","name":"required","internalType":"uint256"}]},{"type":"error","name":"NullAddress","inputs":[]},{"type":"error","name":"OwnerAlreadyExists","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"OwnerDoesNotExist","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"ParamLengthsMismatch","inputs":[]},{"type":"error","name":"ProposalAlreadyConfirmed","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"ProposalAlreadyExecuted","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"error","name":"ProposalAlreadyScheduled","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"error","name":"ProposalDoesNotExist","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"error","name":"ProposalNotConfirmed","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"ProposalNotFullyConfirmed","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"error","name":"ProposalNotScheduled","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"error","name":"ProposalTimelockNotReached","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"error","name":"SenderMustBeMultisigWallet","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"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":"CeloDeposited","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ConfirmationRevoked","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"DelayChanged","inputs":[{"type":"uint256","name":"delay","internalType":"uint256","indexed":false},{"type":"uint256","name":"newDelay","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnerAdded","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnerRemoved","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ProposalAdded","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"ProposalConfirmed","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"ProposalExecuted","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":true},{"type":"bytes","name":"returnData","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalScheduled","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"RequirementChanged","inputs":[{"type":"uint256","name":"required","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TransactionExecuted","inputs":[{"type":"uint256","name":"index","internalType":"uint256","indexed":false},{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":true},{"type":"bytes","name":"returnData","internalType":"bytes","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":"uint256","name":"","internalType":"uint256"}],"name":"MAX_OWNER_COUNT","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"changeDelay","inputs":[{"type":"uint256","name":"newDelay","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"changeRequirement","inputs":[{"type":"uint256","name":"newRequired","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"confirmProposal","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"delay","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"executeProposal","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getConfirmations","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getOwners","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"destinations","internalType":"address[]"},{"type":"uint256[]","name":"values","internalType":"uint256[]"},{"type":"bytes[]","name":"payloads","internalType":"bytes[]"}],"name":"getProposal","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTimestamp","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address[]","name":"initialOwners","internalType":"address[]"},{"type":"uint256","name":"requiredConfirmations","internalType":"uint256"},{"type":"uint256","name":"_delay","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isConfirmedBy","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isFullyConfirmed","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isProposalTimelockReached","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isScheduled","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minDelay","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"proposalCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"timestampExecutable","internalType":"uint256"}],"name":"proposals","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"replaceOwner","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"required","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeConfirmation","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"scheduleProposal","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"proposalId","internalType":"uint256"}],"name":"submitProposal","inputs":[{"type":"address[]","name":"destinations","internalType":"address[]"},{"type":"uint256[]","name":"values","internalType":"uint256[]"},{"type":"bytes[]","name":"payloads","internalType":"bytes[]"}]},{"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"}]},{"type":"receive","stateMutability":"payable"}]
              

Contract Creation Code

0x60c06040523073ffffffffffffffffffffffffffffffffffffffff1660809073ffffffffffffffffffffffffffffffffffffffff168152503480156200004457600080fd5b506040516200473c3803806200473c83398181016040528101906200006a9190620001db565b600060019054906101000a900460ff16620000945760008054906101000a900460ff1615620000a5565b620000a46200016a60201b60201c565b5b620000e7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000de9062000294565b60405180910390fd5b60008060019054906101000a900460ff16159050801562000138576001600060016101000a81548160ff02191690831515021790555060016000806101000a81548160ff0219169083151502179055505b8160a081815250508015620001625760008060016101000a81548160ff0219169083151502179055505b5050620002b6565b600062000182306200018860201b620024961760201c565b15905090565b600080823b905060008111915050919050565b600080fd5b6000819050919050565b620001b581620001a0565b8114620001c157600080fd5b50565b600081519050620001d581620001aa565b92915050565b600060208284031215620001f457620001f36200019b565b5b60006200020484828501620001c4565b91505092915050565b600082825260208201905092915050565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b60006200027c602e836200020d565b915062000289826200021e565b604082019050919050565b60006020820190508181036000830152620002af816200026d565b9050919050565b60805160a051614444620002f860003960008181611d2a01526128ca015260008181610e4401528181610ed301528181611009015261109801526144446000f3fe6080604052600436106101bb5760003560e01c80638a8e784c116100ec578063c7f758a81161008a578063dc8452cd11610064578063dc8452cd146106be578063df5aa6b1146106e9578063e20056e614610712578063f4a4f4d21461073b5761021a565b8063c7f758a814610629578063d74f8edd14610668578063da35c664146106935761021a565b8063b5dc40c3116100c6578063b5dc40c31461055b578063b633620c14610598578063ba51a6df146105d5578063c63c4e9b146105fe5761021a565b80638a8e784c146104b6578063a0e67e2b146104f3578063a42606e31461051e5761021a565b80634f1ef286116101595780635eae7959116101335780635eae7959146103fc5780636a42b8f8146104255780636e7afa34146104505780637065cb481461048d5761021a565b80634f1ef2861461037a5780635037ec621461039657806352745014146103bf5761021a565b806320ea8d861161019557806320ea8d86146102ae5780632f54bf6e146102d75780633659cfe61461031457806339a08f011461033d5761021a565b8063013cf08b1461021f5780630d61b5191461025c578063173825d9146102855761021a565b3661021a576000341115610218573373ffffffffffffffffffffffffffffffffffffffff167fb3bcfe4f408c657ba1ce2fc1c3235d37903cb674e54269bfe562874af3fc0f143460405161020f919061335f565b60405180910390a25b005b600080fd5b34801561022b57600080fd5b50610246600480360381019061024191906133ba565b610764565b604051610253919061335f565b60405180910390f35b34801561026857600080fd5b50610283600480360381019061027e91906133ba565b610782565b005b34801561029157600080fd5b506102ac60048036038101906102a79190613445565b610a7e565b005b3480156102ba57600080fd5b506102d560048036038101906102d091906133ba565b610c15565b005b3480156102e357600080fd5b506102fe60048036038101906102f99190613445565b610e25565b60405161030b919061348d565b60405180910390f35b34801561032057600080fd5b5061033b60048036038101906103369190613445565b610e42565b005b34801561034957600080fd5b50610364600480360381019061035f91906133ba565b610fcb565b604051610371919061348d565b60405180910390f35b610394600480360381019061038f91906135ee565b611007565b005b3480156103a257600080fd5b506103bd60048036038101906103b891906133ba565b611144565b005b3480156103cb57600080fd5b506103e660048036038101906103e19190613756565b6111c0565b6040516103f3919061335f565b60405180910390f35b34801561040857600080fd5b50610423600480360381019061041e919061380a565b6112ee565b005b34801561043157600080fd5b5061043a611651565b604051610447919061335f565b60405180910390f35b34801561045c57600080fd5b50610477600480360381019061047291906133ba565b611657565b604051610484919061348d565b60405180910390f35b34801561049957600080fd5b506104b460048036038101906104af9190613445565b61172e565b005b3480156104c257600080fd5b506104dd60048036038101906104d8919061387e565b61193d565b6040516104ea919061348d565b60405180910390f35b3480156104ff57600080fd5b506105086119a8565b604051610515919061397c565b60405180910390f35b34801561052a57600080fd5b50610545600480360381019061054091906133ba565b6119b9565b604051610552919061348d565b60405180910390f35b34801561056757600080fd5b50610582600480360381019061057d91906133ba565b6119ce565b60405161058f919061397c565b60405180910390f35b3480156105a457600080fd5b506105bf60048036038101906105ba91906133ba565b611c17565b6040516105cc919061335f565b60405180910390f35b3480156105e157600080fd5b506105fc60048036038101906105f791906133ba565b611c37565b005b34801561060a57600080fd5b50610613611d28565b604051610620919061335f565b60405180910390f35b34801561063557600080fd5b50610650600480360381019061064b91906133ba565b611d4c565b60405161065f93929190613ba6565b60405180910390f35b34801561067457600080fd5b5061067d611f2f565b60405161068a919061335f565b60405180910390f35b34801561069f57600080fd5b506106a8611f34565b6040516106b5919061335f565b60405180910390f35b3480156106ca57600080fd5b506106d3611f3a565b6040516106e0919061335f565b60405180910390f35b3480156106f557600080fd5b50610710600480360381019061070b91906133ba565b611f40565b005b34801561071e57600080fd5b5061073960048036038101906107349190613bf2565b61202e565b005b34801561074757600080fd5b50610762600480360381019061075d91906133ba565b61226a565b005b60026020528060005260406000206000915090508060030154905081565b336107978160036124a990919063ffffffff16565b6107d857806040517f531e21ce0000000000000000000000000000000000000000000000000000000081526004016107cf9190613c41565b60405180910390fd5b816107e2816119b9565b61082357806040517ffc8f7d5500000000000000000000000000000000000000000000000000000000815260040161081a919061335f565b60405180910390fd5b8260016002600083815260200190815260200160002060030154141561088057806040517f4eec80e6000000000000000000000000000000000000000000000000000000008152600401610877919061335f565b60405180910390fd5b8361088a81610fcb565b6108cb57806040517f676790790000000000000000000000000000000000000000000000000000000081526004016108c2919061335f565b60405180910390fd5b60006002600087815260200190815260200160002090506001816003018190555060005b6002600088815260200190815260200160002060000180549050811015610a75576000610a2583600001838154811061092b5761092a613c5c565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684600101848154811061096c5761096b613c5c565b5b906000526020600020015485600201858154811061098d5761098c613c5c565b5b9060005260206000200180546109a290613cba565b80601f01602080910402602001604051908101604052809291908181526020018280546109ce90613cba565b8015610a1b5780601f106109f057610100808354040283529160200191610a1b565b820191906000526020600020905b8154815290600101906020018083116109fe57829003601f168201915b50505050506124d9565b9050877f8c24081880749c0e4b467d98c335531022b2e7b74f6e264405138daf13d31e3c8383604051610a59929190613d36565b60405180910390a2508080610a6d90613d95565b9150506108ef565b50505050505050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610aee57336040517f03f2b1fd000000000000000000000000000000000000000000000000000000008152600401610ae59190613c41565b60405180910390fd5b80610b038160036124a990919063ffffffff16565b610b4457806040517f531e21ce000000000000000000000000000000000000000000000000000000008152600401610b3b9190613c41565b60405180910390fd5b6001610b5060036125e8565b1415610b9357816040517fb1722f41000000000000000000000000000000000000000000000000000000008152600401610b8a9190613c41565b60405180910390fd5b610ba78260036125fd90919063ffffffff16565b50610bb260036125e8565b6005541115610bce57610bcd610bc860036125e8565b611c37565b5b8173ffffffffffffffffffffffffffffffffffffffff167f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da60405160405180910390a25050565b33610c2a8160036124a990919063ffffffff16565b610c6b57806040517f531e21ce000000000000000000000000000000000000000000000000000000008152600401610c629190613c41565b60405180910390fd5b81336002600083815260200190815260200160002060040160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610d115781816040517f36308e83000000000000000000000000000000000000000000000000000000008152600401610d08929190613dde565b60405180910390fd5b83600160026000838152602001908152602001600020600301541415610d6e57806040517f4eec80e6000000000000000000000000000000000000000000000000000000008152600401610d65919061335f565b60405180910390fd5b60006002600087815260200190815260200160002060040160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f795394da21278ca39d59bb3ca00efeebdc0679acc420916c7385c2c5d942656f60405160405180910390a35050505050565b6000610e3b8260036124a990919063ffffffff16565b9050919050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415610ed1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ec890613e8a565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16610f1061262d565b73ffffffffffffffffffffffffffffffffffffffff1614610f66576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f5d90613f1c565b60405180910390fd5b610f6f81612684565b610fc881600067ffffffffffffffff811115610f8e57610f8d6134c3565b5b6040519080825280601f01601f191660200182016040528015610fc05781602001600182028036833780820191505090505b5060006126f7565b50565b600080610fd783611c17565b9050428111158015610fff575060016002600085815260200190815260200160002060030154115b915050919050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415611096576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108d90613e8a565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166110d561262d565b73ffffffffffffffffffffffffffffffffffffffff161461112b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161112290613f1c565b60405180910390fd5b61113482612684565b611140828260016126f7565b5050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146111b457336040517f03f2b1fd0000000000000000000000000000000000000000000000000000000081526004016111ab9190613c41565b60405180910390fd5b6111bd816128c8565b50565b6000848490508787905014611201576040517f98a8185a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828290508787905014611240576040517f98a8185a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112d9878780806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508585906112d49190613fef565b612972565b90506112e48161226a565b9695505050505050565b600060019054906101000a900460ff166113165760008054906101000a900460ff161561131f565b61131e612acb565b5b61135e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161135590614076565b60405180910390fd5b60008060019054906101000a900460ff1615905080156113ae576001600060016101000a81548160ff02191690831515021790555060016000806101000a81548160ff0219169083151502179055505b848490508360328211806113c157508181115b806113cc5750600081145b806113d75750600082145b1561141b5781816040517f35077188000000000000000000000000000000000000000000000000000000008152600401611412929190614096565b60405180910390fd5b60005b878790508110156116145761146488888381811061143f5761143e613c5c565b5b90506020020160208101906114549190613445565b60036124a990919063ffffffff16565b156114cd5787878281811061147c5761147b613c5c565b5b90506020020160208101906114919190613445565b6040517f5ba0cba40000000000000000000000000000000000000000000000000000000081526004016114c49190613c41565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168888838181106114f8576114f7613c5c565b5b905060200201602081019061150d9190613445565b73ffffffffffffffffffffffffffffffffffffffff16141561155b576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61159688888381811061157157611570613c5c565b5b90506020020160208101906115869190613445565b6003612adc90919063ffffffff16565b508787828181106115aa576115a9613c5c565b5b90506020020160208101906115bf9190613445565b73ffffffffffffffffffffffffffffffffffffffff167f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c360405160405180910390a2808061160c90613d95565b91505061141e565b5061161e85612b0c565b611627846128c8565b5050801561164a5760008060016101000a81548160ff0219169083151502179055505b5050505050565b60015481565b6000806000905060005b61166b60036125e8565b8110156117225760026000858152602001908152602001600020600401600061169e836003612b4d90919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156116fa5781806116f690613d95565b9250505b60055482141561170f57600192505050611729565b808061171a90613d95565b915050611661565b5060009150505b919050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461179e57336040517f03f2b1fd0000000000000000000000000000000000000000000000000000000081526004016117959190613c41565b60405180910390fd5b806117b38160036124a990919063ffffffff16565b156117f557806040517f5ba0cba40000000000000000000000000000000000000000000000000000000081526004016117ec9190613c41565b60405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561185d576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600161186960036125e8565b61187391906140bf565b600554603282118061188457508181115b8061188f5750600081145b8061189a5750600082145b156118de5781816040517f350771880000000000000000000000000000000000000000000000000000000081526004016118d5929190614096565b60405180910390fd5b6118f2856003612adc90919063ffffffff16565b508473ffffffffffffffffffffffffffffffffffffffff167f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c360405160405180910390a25050505050565b60006002600084815260200190815260200160002060040160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60606119b46003612b67565b905090565b600060016119c683611c17565b119050919050565b606060006119dc60036125e8565b67ffffffffffffffff8111156119f5576119f46134c3565b5b604051908082528060200260200182016040528015611a235781602001602082028036833780820191505090505b5090506000805b611a3460036125e8565b811015611b3757600260008681526020019081526020016000206004016000611a67836003612b4d90919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611b2457611ac8816003612b4d90919063ffffffff16565b838381518110611adb57611ada613c5c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508180611b2090613d95565b9250505b8080611b2f90613d95565b915050611a2a565b5060008167ffffffffffffffff811115611b5457611b536134c3565b5b604051908082528060200260200182016040528015611b825781602001602082028036833780820191505090505b50905060005b82811015611c0b57838181518110611ba357611ba2613c5c565b5b6020026020010151828281518110611bbe57611bbd613c5c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080611c0390613d95565b915050611b88565b50809350505050919050565b600060026000838152602001908152602001600020600301549050919050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ca757336040517f03f2b1fd000000000000000000000000000000000000000000000000000000008152600401611c9e9190613c41565b60405180910390fd5b611cb160036125e8565b816032821180611cc057508181115b80611ccb5750600081145b80611cd65750600082145b15611d1a5781816040517f35077188000000000000000000000000000000000000000000000000000000008152600401611d11929190614096565b60405180910390fd5b611d2383612b0c565b505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6060806060600060026000868152602001908152602001600020905080600001816001018260020182805480602002602001604051908101604052809291908181526020018280548015611df557602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611dab575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611e4757602002820191906000526020600020905b815481526020019060010190808311611e33575b5050505050915080805480602002602001604051908101604052809291908181526020016000905b82821015611f1b578382906000526020600020018054611e8e90613cba565b80601f0160208091040260200160405190810160405280929190818152602001828054611eba90613cba565b8015611f075780601f10611edc57610100808354040283529160200191611f07565b820191906000526020600020905b815481529060010190602001808311611eea57829003601f168201915b505050505081526020019060010190611e6f565b505050509050935093509350509193909250565b603281565b60065481565b60055481565b33611f558160036124a990919063ffffffff16565b611f9657806040517f531e21ce000000000000000000000000000000000000000000000000000000008152600401611f8d9190613c41565b60405180910390fd5b81600160026000838152602001908152602001600020600301541415611ff357806040517f4eec80e6000000000000000000000000000000000000000000000000000000008152600401611fea919061335f565b60405180910390fd5b611ffc83612b88565b827f8b9c2cfee0d20895490bae51f33d88197032bb221b15e360155508136257569a60405160405180910390a2505050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461209e57336040517f03f2b1fd0000000000000000000000000000000000000000000000000000000081526004016120959190613c41565b60405180910390fd5b816120b38160036124a990919063ffffffff16565b6120f457806040517f531e21ce0000000000000000000000000000000000000000000000000000000081526004016120eb9190613c41565b60405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561215c576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826121718160036124a990919063ffffffff16565b156121b357806040517f5ba0cba40000000000000000000000000000000000000000000000000000000081526004016121aa9190613c41565b60405180910390fd5b6121c78560036125fd90919063ffffffff16565b506121dc846003612adc90919063ffffffff16565b508473ffffffffffffffffffffffffffffffffffffffff167f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da60405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c360405160405180910390a25050505050565b3361227f8160036124a990919063ffffffff16565b6122c057806040517f531e21ce0000000000000000000000000000000000000000000000000000000081526004016122b79190613c41565b60405180910390fd5b8160006002600083815260200190815260200160002060000180549050141561232057806040517f4f017420000000000000000000000000000000000000000000000000000000008152600401612317919061335f565b60405180910390fd5b82336002600083815260200190815260200160002060040160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156123c75781816040517f3d5ba2050000000000000000000000000000000000000000000000000000000081526004016123be929190613dde565b60405180910390fd5b60016002600087815260200190815260200160002060040160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f03c76756bd7402ca4b074fa11dfd2a209452c67f6f414cff1d8e9795d291421760405160405180910390a361248085611657565b1561248f5761248e85611f40565b5b5050505050565b600080823b905060008111915050919050565b60006124d1836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612c4c565b905092915050565b6060600082511115612530576124ee84612496565b61252f57836040517f19bb40290000000000000000000000000000000000000000000000000000000081526004016125269190613c41565b60405180910390fd5b5b600060608573ffffffffffffffffffffffffffffffffffffffff16858560405161255a9190614151565b60006040518083038185875af1925050503d8060008114612597576040519150601f19603f3d011682016040523d82523d6000602084013e61259c565b606091505b508092508193505050816125dc576040517facfdb44400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80925050509392505050565b60006125f682600001612c6f565b9050919050565b6000612625836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612c80565b905092915050565b600061265b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b612d94565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146126f457336040517f03f2b1fd0000000000000000000000000000000000000000000000000000000081526004016126eb9190613c41565b60405180910390fd5b50565b600061270161262d565b905061270c84612d9e565b6000835111806127195750815b1561272a576127288484612e57565b505b60006127587f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b612e84565b90508060000160009054906101000a900460ff166128c15760018160000160006101000a81548160ff02191690831515021790555061282485836040516024016127a29190613c41565b6040516020818303038152906040527f3659cfe6000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612e57565b5060008160000160006101000a81548160ff02191690831515021790555061284a61262d565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146128b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016128ae906141da565b60405180910390fd5b6128c085612e8e565b5b5050505050565b7f000000000000000000000000000000000000000000000000000000000000000081101561292d57806040517f08462b5c000000000000000000000000000000000000000000000000000000008152600401612924919061335f565b60405180910390fd5b806001819055507fe238f342cc2d86b842f1511bd768de5dbea53639f6b5335c5d877543bc355c7160015482604051612967929190614096565b60405180910390a150565b60008360005b8151811015612a1557600073ffffffffffffffffffffffffffffffffffffffff168282815181106129ac576129ab613c5c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415612a02576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8080612a0d90613d95565b915050612978565b506006549150600060026000848152602001908152602001600020905085816000019080519060200190612a4a929190613108565b5084816001019080519060200190612a63929190613192565b5083816002019080519060200190612a7c9291906131df565b5060066000815480929190612a9090613d95565b9190505550827f3f802220982dbddc337f1811180e73513e775b18380401997927fd1454cfd0bd60405160405180910390a250509392505050565b6000612ad630612496565b15905090565b6000612b04836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612edd565b905092915050565b806005819055507facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da81604051612b42919061335f565b60405180910390a150565b6000612b5c8360000183612f4d565b60001c905092915050565b60606000612b7783600001612f78565b905060608190508092505050919050565b80612b92816119b9565b15612bd457806040517fd61102f5000000000000000000000000000000000000000000000000000000008152600401612bcb919061335f565b60405180910390fd5b81612bde81611657565b612c1f57806040517f4e59425a000000000000000000000000000000000000000000000000000000008152600401612c16919061335f565b60405180910390fd5b60015442612c2d91906140bf565b6002600085815260200190815260200160002060030181905550505050565b600080836001016000848152602001908152602001600020541415905092915050565b600081600001805490509050919050565b60008083600101600084815260200190815260200160002054905060008114612d88576000600182612cb291906141fa565b9050600060018660000180549050612cca91906141fa565b9050818114612d39576000866000018281548110612ceb57612cea613c5c565b5b9060005260206000200154905080876000018481548110612d0f57612d0e613c5c565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b85600001805480612d4d57612d4c61422e565b5b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612d8e565b60009150505b92915050565b6000819050919050565b612da781612496565b612de6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ddd906142cf565b60405180910390fd5b80612e137f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b612d94565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060612e7c83836040518060600160405280602781526020016143e860279139612fd4565b905092915050565b6000819050919050565b612e9781612d9e565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b6000612ee98383612c4c565b612f42578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050612f47565b600090505b92915050565b6000826000018281548110612f6557612f64613c5c565b5b9060005260206000200154905092915050565b606081600001805480602002602001604051908101604052809291908181526020018280548015612fc857602002820191906000526020600020905b815481526020019060010190808311612fb4575b50505050509050919050565b6060612fdf84612496565b61301e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161301590614361565b60405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff16856040516130469190614151565b600060405180830381855af49150503d8060008114613081576040519150601f19603f3d011682016040523d82523d6000602084013e613086565b606091505b50915091506130968282866130a1565b925050509392505050565b606083156130b157829050613101565b6000835111156130c45782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130f891906143c5565b60405180910390fd5b9392505050565b828054828255906000526020600020908101928215613181579160200282015b828111156131805782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190613128565b5b50905061318e919061323f565b5090565b8280548282559060005260206000209081019282156131ce579160200282015b828111156131cd5782518255916020019190600101906131b2565b5b5090506131db919061323f565b5090565b82805482825590600052602060002090810192821561322e579160200282015b8281111561322d57825182908051906020019061321d92919061325c565b50916020019190600101906131ff565b5b50905061323b91906132e2565b5090565b5b80821115613258576000816000905550600101613240565b5090565b82805461326890613cba565b90600052602060002090601f01602090048101928261328a57600085556132d1565b82601f106132a357805160ff19168380011785556132d1565b828001600101855582156132d1579182015b828111156132d05782518255916020019190600101906132b5565b5b5090506132de919061323f565b5090565b5b8082111561330257600081816132f99190613306565b506001016132e3565b5090565b50805461331290613cba565b6000825580601f106133245750613343565b601f016020900490600052602060002090810190613342919061323f565b5b50565b6000819050919050565b61335981613346565b82525050565b60006020820190506133746000830184613350565b92915050565b6000604051905090565b600080fd5b600080fd5b61339781613346565b81146133a257600080fd5b50565b6000813590506133b48161338e565b92915050565b6000602082840312156133d0576133cf613384565b5b60006133de848285016133a5565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613412826133e7565b9050919050565b61342281613407565b811461342d57600080fd5b50565b60008135905061343f81613419565b92915050565b60006020828403121561345b5761345a613384565b5b600061346984828501613430565b91505092915050565b60008115159050919050565b61348781613472565b82525050565b60006020820190506134a2600083018461347e565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6134fb826134b2565b810181811067ffffffffffffffff8211171561351a576135196134c3565b5b80604052505050565b600061352d61337a565b905061353982826134f2565b919050565b600067ffffffffffffffff821115613559576135586134c3565b5b613562826134b2565b9050602081019050919050565b82818337600083830152505050565b600061359161358c8461353e565b613523565b9050828152602081018484840111156135ad576135ac6134ad565b5b6135b884828561356f565b509392505050565b600082601f8301126135d5576135d46134a8565b5b81356135e584826020860161357e565b91505092915050565b6000806040838503121561360557613604613384565b5b600061361385828601613430565b925050602083013567ffffffffffffffff81111561363457613633613389565b5b613640858286016135c0565b9150509250929050565b600080fd5b600080fd5b60008083601f84011261366a576136696134a8565b5b8235905067ffffffffffffffff8111156136875761368661364a565b5b6020830191508360208202830111156136a3576136a261364f565b5b9250929050565b60008083601f8401126136c0576136bf6134a8565b5b8235905067ffffffffffffffff8111156136dd576136dc61364a565b5b6020830191508360208202830111156136f9576136f861364f565b5b9250929050565b60008083601f840112613716576137156134a8565b5b8235905067ffffffffffffffff8111156137335761373261364a565b5b60208301915083602082028301111561374f5761374e61364f565b5b9250929050565b6000806000806000806060878903121561377357613772613384565b5b600087013567ffffffffffffffff81111561379157613790613389565b5b61379d89828a01613654565b9650965050602087013567ffffffffffffffff8111156137c0576137bf613389565b5b6137cc89828a016136aa565b9450945050604087013567ffffffffffffffff8111156137ef576137ee613389565b5b6137fb89828a01613700565b92509250509295509295509295565b6000806000806060858703121561382457613823613384565b5b600085013567ffffffffffffffff81111561384257613841613389565b5b61384e87828801613654565b94509450506020613861878288016133a5565b9250506040613872878288016133a5565b91505092959194509250565b6000806040838503121561389557613894613384565b5b60006138a3858286016133a5565b92505060206138b485828601613430565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6138f381613407565b82525050565b600061390583836138ea565b60208301905092915050565b6000602082019050919050565b6000613929826138be565b61393381856138c9565b935061393e836138da565b8060005b8381101561396f57815161395688826138f9565b975061396183613911565b925050600181019050613942565b5085935050505092915050565b60006020820190508181036000830152613996818461391e565b905092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6139d381613346565b82525050565b60006139e583836139ca565b60208301905092915050565b6000602082019050919050565b6000613a098261399e565b613a1381856139a9565b9350613a1e836139ba565b8060005b83811015613a4f578151613a3688826139d9565b9750613a41836139f1565b925050600181019050613a22565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b83811015613ac2578082015181840152602081019050613aa7565b83811115613ad1576000848401525b50505050565b6000613ae282613a88565b613aec8185613a93565b9350613afc818560208601613aa4565b613b05816134b2565b840191505092915050565b6000613b1c8383613ad7565b905092915050565b6000602082019050919050565b6000613b3c82613a5c565b613b468185613a67565b935083602082028501613b5885613a78565b8060005b85811015613b945784840389528151613b758582613b10565b9450613b8083613b24565b925060208a01995050600181019050613b5c565b50829750879550505050505092915050565b60006060820190508181036000830152613bc0818661391e565b90508181036020830152613bd481856139fe565b90508181036040830152613be88184613b31565b9050949350505050565b60008060408385031215613c0957613c08613384565b5b6000613c1785828601613430565b9250506020613c2885828601613430565b9150509250929050565b613c3b81613407565b82525050565b6000602082019050613c566000830184613c32565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680613cd257607f821691505b60208210811415613ce657613ce5613c8b565b5b50919050565b600082825260208201905092915050565b6000613d0882613a88565b613d128185613cec565b9350613d22818560208601613aa4565b613d2b816134b2565b840191505092915050565b6000604082019050613d4b6000830185613350565b8181036020830152613d5d8184613cfd565b90509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000613da082613346565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415613dd357613dd2613d66565b5b600182019050919050565b6000604082019050613df36000830185613350565b613e006020830184613c32565b9392505050565b600082825260208201905092915050565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f64656c656761746563616c6c0000000000000000000000000000000000000000602082015250565b6000613e74602c83613e07565b9150613e7f82613e18565b604082019050919050565b60006020820190508181036000830152613ea381613e67565b9050919050565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f6163746976652070726f78790000000000000000000000000000000000000000602082015250565b6000613f06602c83613e07565b9150613f1182613eaa565b604082019050919050565b60006020820190508181036000830152613f3581613ef9565b9050919050565b600067ffffffffffffffff821115613f5757613f566134c3565b5b602082029050602081019050919050565b6000613f7b613f7684613f3c565b613523565b90508083825260208201905060208402830185811115613f9e57613f9d61364f565b5b835b81811015613fe557803567ffffffffffffffff811115613fc357613fc26134a8565b5b808601613fd089826135c0565b85526020850194505050602081019050613fa0565b5050509392505050565b6000613ffc368484613f68565b905092915050565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b6000614060602e83613e07565b915061406b82614004565b604082019050919050565b6000602082019050818103600083015261408f81614053565b9050919050565b60006040820190506140ab6000830185613350565b6140b86020830184613350565b9392505050565b60006140ca82613346565b91506140d583613346565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561410a57614109613d66565b5b828201905092915050565b600081905092915050565b600061412b82613a88565b6141358185614115565b9350614145818560208601613aa4565b80840191505092915050565b600061415d8284614120565b915081905092915050565b7f45524331393637557067726164653a207570677261646520627265616b73206660008201527f7572746865722075706772616465730000000000000000000000000000000000602082015250565b60006141c4602f83613e07565b91506141cf82614168565b604082019050919050565b600060208201905081810360008301526141f3816141b7565b9050919050565b600061420582613346565b915061421083613346565b92508282101561422357614222613d66565b5b828203905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b60006142b9602d83613e07565b91506142c48261425d565b604082019050919050565b600060208201905081810360008301526142e8816142ac565b9050919050565b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b600061434b602683613e07565b9150614356826142ef565b604082019050919050565b6000602082019050818103600083015261437a8161433e565b9050919050565b600081519050919050565b600061439782614381565b6143a18185613e07565b93506143b1818560208601613aa4565b6143ba816134b2565b840191505092915050565b600060208201905081810360008301526143df818461438c565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220303e04746ad337ff4cf7e183142493b03ba7ffb20e40343fd30e314c9a21f40264736f6c634300080b00330000000000000000000000000000000000000000000000000000000000000001

Deployed ByteCode

0x6080604052600436106101bb5760003560e01c80638a8e784c116100ec578063c7f758a81161008a578063dc8452cd11610064578063dc8452cd146106be578063df5aa6b1146106e9578063e20056e614610712578063f4a4f4d21461073b5761021a565b8063c7f758a814610629578063d74f8edd14610668578063da35c664146106935761021a565b8063b5dc40c3116100c6578063b5dc40c31461055b578063b633620c14610598578063ba51a6df146105d5578063c63c4e9b146105fe5761021a565b80638a8e784c146104b6578063a0e67e2b146104f3578063a42606e31461051e5761021a565b80634f1ef286116101595780635eae7959116101335780635eae7959146103fc5780636a42b8f8146104255780636e7afa34146104505780637065cb481461048d5761021a565b80634f1ef2861461037a5780635037ec621461039657806352745014146103bf5761021a565b806320ea8d861161019557806320ea8d86146102ae5780632f54bf6e146102d75780633659cfe61461031457806339a08f011461033d5761021a565b8063013cf08b1461021f5780630d61b5191461025c578063173825d9146102855761021a565b3661021a576000341115610218573373ffffffffffffffffffffffffffffffffffffffff167fb3bcfe4f408c657ba1ce2fc1c3235d37903cb674e54269bfe562874af3fc0f143460405161020f919061335f565b60405180910390a25b005b600080fd5b34801561022b57600080fd5b50610246600480360381019061024191906133ba565b610764565b604051610253919061335f565b60405180910390f35b34801561026857600080fd5b50610283600480360381019061027e91906133ba565b610782565b005b34801561029157600080fd5b506102ac60048036038101906102a79190613445565b610a7e565b005b3480156102ba57600080fd5b506102d560048036038101906102d091906133ba565b610c15565b005b3480156102e357600080fd5b506102fe60048036038101906102f99190613445565b610e25565b60405161030b919061348d565b60405180910390f35b34801561032057600080fd5b5061033b60048036038101906103369190613445565b610e42565b005b34801561034957600080fd5b50610364600480360381019061035f91906133ba565b610fcb565b604051610371919061348d565b60405180910390f35b610394600480360381019061038f91906135ee565b611007565b005b3480156103a257600080fd5b506103bd60048036038101906103b891906133ba565b611144565b005b3480156103cb57600080fd5b506103e660048036038101906103e19190613756565b6111c0565b6040516103f3919061335f565b60405180910390f35b34801561040857600080fd5b50610423600480360381019061041e919061380a565b6112ee565b005b34801561043157600080fd5b5061043a611651565b604051610447919061335f565b60405180910390f35b34801561045c57600080fd5b50610477600480360381019061047291906133ba565b611657565b604051610484919061348d565b60405180910390f35b34801561049957600080fd5b506104b460048036038101906104af9190613445565b61172e565b005b3480156104c257600080fd5b506104dd60048036038101906104d8919061387e565b61193d565b6040516104ea919061348d565b60405180910390f35b3480156104ff57600080fd5b506105086119a8565b604051610515919061397c565b60405180910390f35b34801561052a57600080fd5b50610545600480360381019061054091906133ba565b6119b9565b604051610552919061348d565b60405180910390f35b34801561056757600080fd5b50610582600480360381019061057d91906133ba565b6119ce565b60405161058f919061397c565b60405180910390f35b3480156105a457600080fd5b506105bf60048036038101906105ba91906133ba565b611c17565b6040516105cc919061335f565b60405180910390f35b3480156105e157600080fd5b506105fc60048036038101906105f791906133ba565b611c37565b005b34801561060a57600080fd5b50610613611d28565b604051610620919061335f565b60405180910390f35b34801561063557600080fd5b50610650600480360381019061064b91906133ba565b611d4c565b60405161065f93929190613ba6565b60405180910390f35b34801561067457600080fd5b5061067d611f2f565b60405161068a919061335f565b60405180910390f35b34801561069f57600080fd5b506106a8611f34565b6040516106b5919061335f565b60405180910390f35b3480156106ca57600080fd5b506106d3611f3a565b6040516106e0919061335f565b60405180910390f35b3480156106f557600080fd5b50610710600480360381019061070b91906133ba565b611f40565b005b34801561071e57600080fd5b5061073960048036038101906107349190613bf2565b61202e565b005b34801561074757600080fd5b50610762600480360381019061075d91906133ba565b61226a565b005b60026020528060005260406000206000915090508060030154905081565b336107978160036124a990919063ffffffff16565b6107d857806040517f531e21ce0000000000000000000000000000000000000000000000000000000081526004016107cf9190613c41565b60405180910390fd5b816107e2816119b9565b61082357806040517ffc8f7d5500000000000000000000000000000000000000000000000000000000815260040161081a919061335f565b60405180910390fd5b8260016002600083815260200190815260200160002060030154141561088057806040517f4eec80e6000000000000000000000000000000000000000000000000000000008152600401610877919061335f565b60405180910390fd5b8361088a81610fcb565b6108cb57806040517f676790790000000000000000000000000000000000000000000000000000000081526004016108c2919061335f565b60405180910390fd5b60006002600087815260200190815260200160002090506001816003018190555060005b6002600088815260200190815260200160002060000180549050811015610a75576000610a2583600001838154811061092b5761092a613c5c565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684600101848154811061096c5761096b613c5c565b5b906000526020600020015485600201858154811061098d5761098c613c5c565b5b9060005260206000200180546109a290613cba565b80601f01602080910402602001604051908101604052809291908181526020018280546109ce90613cba565b8015610a1b5780601f106109f057610100808354040283529160200191610a1b565b820191906000526020600020905b8154815290600101906020018083116109fe57829003601f168201915b50505050506124d9565b9050877f8c24081880749c0e4b467d98c335531022b2e7b74f6e264405138daf13d31e3c8383604051610a59929190613d36565b60405180910390a2508080610a6d90613d95565b9150506108ef565b50505050505050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610aee57336040517f03f2b1fd000000000000000000000000000000000000000000000000000000008152600401610ae59190613c41565b60405180910390fd5b80610b038160036124a990919063ffffffff16565b610b4457806040517f531e21ce000000000000000000000000000000000000000000000000000000008152600401610b3b9190613c41565b60405180910390fd5b6001610b5060036125e8565b1415610b9357816040517fb1722f41000000000000000000000000000000000000000000000000000000008152600401610b8a9190613c41565b60405180910390fd5b610ba78260036125fd90919063ffffffff16565b50610bb260036125e8565b6005541115610bce57610bcd610bc860036125e8565b611c37565b5b8173ffffffffffffffffffffffffffffffffffffffff167f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da60405160405180910390a25050565b33610c2a8160036124a990919063ffffffff16565b610c6b57806040517f531e21ce000000000000000000000000000000000000000000000000000000008152600401610c629190613c41565b60405180910390fd5b81336002600083815260200190815260200160002060040160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16610d115781816040517f36308e83000000000000000000000000000000000000000000000000000000008152600401610d08929190613dde565b60405180910390fd5b83600160026000838152602001908152602001600020600301541415610d6e57806040517f4eec80e6000000000000000000000000000000000000000000000000000000008152600401610d65919061335f565b60405180910390fd5b60006002600087815260200190815260200160002060040160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f795394da21278ca39d59bb3ca00efeebdc0679acc420916c7385c2c5d942656f60405160405180910390a35050505050565b6000610e3b8260036124a990919063ffffffff16565b9050919050565b7f000000000000000000000000861e032d88a2a64cd7fc4e1dc8943740edd5e7e973ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415610ed1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ec890613e8a565b60405180910390fd5b7f000000000000000000000000861e032d88a2a64cd7fc4e1dc8943740edd5e7e973ffffffffffffffffffffffffffffffffffffffff16610f1061262d565b73ffffffffffffffffffffffffffffffffffffffff1614610f66576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f5d90613f1c565b60405180910390fd5b610f6f81612684565b610fc881600067ffffffffffffffff811115610f8e57610f8d6134c3565b5b6040519080825280601f01601f191660200182016040528015610fc05781602001600182028036833780820191505090505b5060006126f7565b50565b600080610fd783611c17565b9050428111158015610fff575060016002600085815260200190815260200160002060030154115b915050919050565b7f000000000000000000000000861e032d88a2a64cd7fc4e1dc8943740edd5e7e973ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161415611096576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161108d90613e8a565b60405180910390fd5b7f000000000000000000000000861e032d88a2a64cd7fc4e1dc8943740edd5e7e973ffffffffffffffffffffffffffffffffffffffff166110d561262d565b73ffffffffffffffffffffffffffffffffffffffff161461112b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161112290613f1c565b60405180910390fd5b61113482612684565b611140828260016126f7565b5050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146111b457336040517f03f2b1fd0000000000000000000000000000000000000000000000000000000081526004016111ab9190613c41565b60405180910390fd5b6111bd816128c8565b50565b6000848490508787905014611201576040517f98a8185a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828290508787905014611240576040517f98a8185a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112d9878780806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050508585906112d49190613fef565b612972565b90506112e48161226a565b9695505050505050565b600060019054906101000a900460ff166113165760008054906101000a900460ff161561131f565b61131e612acb565b5b61135e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161135590614076565b60405180910390fd5b60008060019054906101000a900460ff1615905080156113ae576001600060016101000a81548160ff02191690831515021790555060016000806101000a81548160ff0219169083151502179055505b848490508360328211806113c157508181115b806113cc5750600081145b806113d75750600082145b1561141b5781816040517f35077188000000000000000000000000000000000000000000000000000000008152600401611412929190614096565b60405180910390fd5b60005b878790508110156116145761146488888381811061143f5761143e613c5c565b5b90506020020160208101906114549190613445565b60036124a990919063ffffffff16565b156114cd5787878281811061147c5761147b613c5c565b5b90506020020160208101906114919190613445565b6040517f5ba0cba40000000000000000000000000000000000000000000000000000000081526004016114c49190613c41565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168888838181106114f8576114f7613c5c565b5b905060200201602081019061150d9190613445565b73ffffffffffffffffffffffffffffffffffffffff16141561155b576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61159688888381811061157157611570613c5c565b5b90506020020160208101906115869190613445565b6003612adc90919063ffffffff16565b508787828181106115aa576115a9613c5c565b5b90506020020160208101906115bf9190613445565b73ffffffffffffffffffffffffffffffffffffffff167f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c360405160405180910390a2808061160c90613d95565b91505061141e565b5061161e85612b0c565b611627846128c8565b5050801561164a5760008060016101000a81548160ff0219169083151502179055505b5050505050565b60015481565b6000806000905060005b61166b60036125e8565b8110156117225760026000858152602001908152602001600020600401600061169e836003612b4d90919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156116fa5781806116f690613d95565b9250505b60055482141561170f57600192505050611729565b808061171a90613d95565b915050611661565b5060009150505b919050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461179e57336040517f03f2b1fd0000000000000000000000000000000000000000000000000000000081526004016117959190613c41565b60405180910390fd5b806117b38160036124a990919063ffffffff16565b156117f557806040517f5ba0cba40000000000000000000000000000000000000000000000000000000081526004016117ec9190613c41565b60405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561185d576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600161186960036125e8565b61187391906140bf565b600554603282118061188457508181115b8061188f5750600081145b8061189a5750600082145b156118de5781816040517f350771880000000000000000000000000000000000000000000000000000000081526004016118d5929190614096565b60405180910390fd5b6118f2856003612adc90919063ffffffff16565b508473ffffffffffffffffffffffffffffffffffffffff167f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c360405160405180910390a25050505050565b60006002600084815260200190815260200160002060040160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60606119b46003612b67565b905090565b600060016119c683611c17565b119050919050565b606060006119dc60036125e8565b67ffffffffffffffff8111156119f5576119f46134c3565b5b604051908082528060200260200182016040528015611a235781602001602082028036833780820191505090505b5090506000805b611a3460036125e8565b811015611b3757600260008681526020019081526020016000206004016000611a67836003612b4d90919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611b2457611ac8816003612b4d90919063ffffffff16565b838381518110611adb57611ada613c5c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508180611b2090613d95565b9250505b8080611b2f90613d95565b915050611a2a565b5060008167ffffffffffffffff811115611b5457611b536134c3565b5b604051908082528060200260200182016040528015611b825781602001602082028036833780820191505090505b50905060005b82811015611c0b57838181518110611ba357611ba2613c5c565b5b6020026020010151828281518110611bbe57611bbd613c5c565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080611c0390613d95565b915050611b88565b50809350505050919050565b600060026000838152602001908152602001600020600301549050919050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ca757336040517f03f2b1fd000000000000000000000000000000000000000000000000000000008152600401611c9e9190613c41565b60405180910390fd5b611cb160036125e8565b816032821180611cc057508181115b80611ccb5750600081145b80611cd65750600082145b15611d1a5781816040517f35077188000000000000000000000000000000000000000000000000000000008152600401611d11929190614096565b60405180910390fd5b611d2383612b0c565b505050565b7f000000000000000000000000000000000000000000000000000000000000000181565b6060806060600060026000868152602001908152602001600020905080600001816001018260020182805480602002602001604051908101604052809291908181526020018280548015611df557602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611dab575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611e4757602002820191906000526020600020905b815481526020019060010190808311611e33575b5050505050915080805480602002602001604051908101604052809291908181526020016000905b82821015611f1b578382906000526020600020018054611e8e90613cba565b80601f0160208091040260200160405190810160405280929190818152602001828054611eba90613cba565b8015611f075780601f10611edc57610100808354040283529160200191611f07565b820191906000526020600020905b815481529060010190602001808311611eea57829003601f168201915b505050505081526020019060010190611e6f565b505050509050935093509350509193909250565b603281565b60065481565b60055481565b33611f558160036124a990919063ffffffff16565b611f9657806040517f531e21ce000000000000000000000000000000000000000000000000000000008152600401611f8d9190613c41565b60405180910390fd5b81600160026000838152602001908152602001600020600301541415611ff357806040517f4eec80e6000000000000000000000000000000000000000000000000000000008152600401611fea919061335f565b60405180910390fd5b611ffc83612b88565b827f8b9c2cfee0d20895490bae51f33d88197032bb221b15e360155508136257569a60405160405180910390a2505050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461209e57336040517f03f2b1fd0000000000000000000000000000000000000000000000000000000081526004016120959190613c41565b60405180910390fd5b816120b38160036124a990919063ffffffff16565b6120f457806040517f531e21ce0000000000000000000000000000000000000000000000000000000081526004016120eb9190613c41565b60405180910390fd5b81600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561215c576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826121718160036124a990919063ffffffff16565b156121b357806040517f5ba0cba40000000000000000000000000000000000000000000000000000000081526004016121aa9190613c41565b60405180910390fd5b6121c78560036125fd90919063ffffffff16565b506121dc846003612adc90919063ffffffff16565b508473ffffffffffffffffffffffffffffffffffffffff167f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da60405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c360405160405180910390a25050505050565b3361227f8160036124a990919063ffffffff16565b6122c057806040517f531e21ce0000000000000000000000000000000000000000000000000000000081526004016122b79190613c41565b60405180910390fd5b8160006002600083815260200190815260200160002060000180549050141561232057806040517f4f017420000000000000000000000000000000000000000000000000000000008152600401612317919061335f565b60405180910390fd5b82336002600083815260200190815260200160002060040160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156123c75781816040517f3d5ba2050000000000000000000000000000000000000000000000000000000081526004016123be929190613dde565b60405180910390fd5b60016002600087815260200190815260200160002060040160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f03c76756bd7402ca4b074fa11dfd2a209452c67f6f414cff1d8e9795d291421760405160405180910390a361248085611657565b1561248f5761248e85611f40565b5b5050505050565b600080823b905060008111915050919050565b60006124d1836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612c4c565b905092915050565b6060600082511115612530576124ee84612496565b61252f57836040517f19bb40290000000000000000000000000000000000000000000000000000000081526004016125269190613c41565b60405180910390fd5b5b600060608573ffffffffffffffffffffffffffffffffffffffff16858560405161255a9190614151565b60006040518083038185875af1925050503d8060008114612597576040519150601f19603f3d011682016040523d82523d6000602084013e61259c565b606091505b508092508193505050816125dc576040517facfdb44400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80925050509392505050565b60006125f682600001612c6f565b9050919050565b6000612625836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612c80565b905092915050565b600061265b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b612d94565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146126f457336040517f03f2b1fd0000000000000000000000000000000000000000000000000000000081526004016126eb9190613c41565b60405180910390fd5b50565b600061270161262d565b905061270c84612d9e565b6000835111806127195750815b1561272a576127288484612e57565b505b60006127587f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b612e84565b90508060000160009054906101000a900460ff166128c15760018160000160006101000a81548160ff02191690831515021790555061282485836040516024016127a29190613c41565b6040516020818303038152906040527f3659cfe6000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612e57565b5060008160000160006101000a81548160ff02191690831515021790555061284a61262d565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146128b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016128ae906141da565b60405180910390fd5b6128c085612e8e565b5b5050505050565b7f000000000000000000000000000000000000000000000000000000000000000181101561292d57806040517f08462b5c000000000000000000000000000000000000000000000000000000008152600401612924919061335f565b60405180910390fd5b806001819055507fe238f342cc2d86b842f1511bd768de5dbea53639f6b5335c5d877543bc355c7160015482604051612967929190614096565b60405180910390a150565b60008360005b8151811015612a1557600073ffffffffffffffffffffffffffffffffffffffff168282815181106129ac576129ab613c5c565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415612a02576040517fe99d5ac500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8080612a0d90613d95565b915050612978565b506006549150600060026000848152602001908152602001600020905085816000019080519060200190612a4a929190613108565b5084816001019080519060200190612a63929190613192565b5083816002019080519060200190612a7c9291906131df565b5060066000815480929190612a9090613d95565b9190505550827f3f802220982dbddc337f1811180e73513e775b18380401997927fd1454cfd0bd60405160405180910390a250509392505050565b6000612ad630612496565b15905090565b6000612b04836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612edd565b905092915050565b806005819055507facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da81604051612b42919061335f565b60405180910390a150565b6000612b5c8360000183612f4d565b60001c905092915050565b60606000612b7783600001612f78565b905060608190508092505050919050565b80612b92816119b9565b15612bd457806040517fd61102f5000000000000000000000000000000000000000000000000000000008152600401612bcb919061335f565b60405180910390fd5b81612bde81611657565b612c1f57806040517f4e59425a000000000000000000000000000000000000000000000000000000008152600401612c16919061335f565b60405180910390fd5b60015442612c2d91906140bf565b6002600085815260200190815260200160002060030181905550505050565b600080836001016000848152602001908152602001600020541415905092915050565b600081600001805490509050919050565b60008083600101600084815260200190815260200160002054905060008114612d88576000600182612cb291906141fa565b9050600060018660000180549050612cca91906141fa565b9050818114612d39576000866000018281548110612ceb57612cea613c5c565b5b9060005260206000200154905080876000018481548110612d0f57612d0e613c5c565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b85600001805480612d4d57612d4c61422e565b5b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612d8e565b60009150505b92915050565b6000819050919050565b612da781612496565b612de6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ddd906142cf565b60405180910390fd5b80612e137f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b612d94565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060612e7c83836040518060600160405280602781526020016143e860279139612fd4565b905092915050565b6000819050919050565b612e9781612d9e565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b6000612ee98383612c4c565b612f42578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050612f47565b600090505b92915050565b6000826000018281548110612f6557612f64613c5c565b5b9060005260206000200154905092915050565b606081600001805480602002602001604051908101604052809291908181526020018280548015612fc857602002820191906000526020600020905b815481526020019060010190808311612fb4575b50505050509050919050565b6060612fdf84612496565b61301e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161301590614361565b60405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff16856040516130469190614151565b600060405180830381855af49150503d8060008114613081576040519150601f19603f3d011682016040523d82523d6000602084013e613086565b606091505b50915091506130968282866130a1565b925050509392505050565b606083156130b157829050613101565b6000835111156130c45782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016130f891906143c5565b60405180910390fd5b9392505050565b828054828255906000526020600020908101928215613181579160200282015b828111156131805782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190613128565b5b50905061318e919061323f565b5090565b8280548282559060005260206000209081019282156131ce579160200282015b828111156131cd5782518255916020019190600101906131b2565b5b5090506131db919061323f565b5090565b82805482825590600052602060002090810192821561322e579160200282015b8281111561322d57825182908051906020019061321d92919061325c565b50916020019190600101906131ff565b5b50905061323b91906132e2565b5090565b5b80821115613258576000816000905550600101613240565b5090565b82805461326890613cba565b90600052602060002090601f01602090048101928261328a57600085556132d1565b82601f106132a357805160ff19168380011785556132d1565b828001600101855582156132d1579182015b828111156132d05782518255916020019190600101906132b5565b5b5090506132de919061323f565b5090565b5b8082111561330257600081816132f99190613306565b506001016132e3565b5090565b50805461331290613cba565b6000825580601f106133245750613343565b601f016020900490600052602060002090810190613342919061323f565b5b50565b6000819050919050565b61335981613346565b82525050565b60006020820190506133746000830184613350565b92915050565b6000604051905090565b600080fd5b600080fd5b61339781613346565b81146133a257600080fd5b50565b6000813590506133b48161338e565b92915050565b6000602082840312156133d0576133cf613384565b5b60006133de848285016133a5565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613412826133e7565b9050919050565b61342281613407565b811461342d57600080fd5b50565b60008135905061343f81613419565b92915050565b60006020828403121561345b5761345a613384565b5b600061346984828501613430565b91505092915050565b60008115159050919050565b61348781613472565b82525050565b60006020820190506134a2600083018461347e565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6134fb826134b2565b810181811067ffffffffffffffff8211171561351a576135196134c3565b5b80604052505050565b600061352d61337a565b905061353982826134f2565b919050565b600067ffffffffffffffff821115613559576135586134c3565b5b613562826134b2565b9050602081019050919050565b82818337600083830152505050565b600061359161358c8461353e565b613523565b9050828152602081018484840111156135ad576135ac6134ad565b5b6135b884828561356f565b509392505050565b600082601f8301126135d5576135d46134a8565b5b81356135e584826020860161357e565b91505092915050565b6000806040838503121561360557613604613384565b5b600061361385828601613430565b925050602083013567ffffffffffffffff81111561363457613633613389565b5b613640858286016135c0565b9150509250929050565b600080fd5b600080fd5b60008083601f84011261366a576136696134a8565b5b8235905067ffffffffffffffff8111156136875761368661364a565b5b6020830191508360208202830111156136a3576136a261364f565b5b9250929050565b60008083601f8401126136c0576136bf6134a8565b5b8235905067ffffffffffffffff8111156136dd576136dc61364a565b5b6020830191508360208202830111156136f9576136f861364f565b5b9250929050565b60008083601f840112613716576137156134a8565b5b8235905067ffffffffffffffff8111156137335761373261364a565b5b60208301915083602082028301111561374f5761374e61364f565b5b9250929050565b6000806000806000806060878903121561377357613772613384565b5b600087013567ffffffffffffffff81111561379157613790613389565b5b61379d89828a01613654565b9650965050602087013567ffffffffffffffff8111156137c0576137bf613389565b5b6137cc89828a016136aa565b9450945050604087013567ffffffffffffffff8111156137ef576137ee613389565b5b6137fb89828a01613700565b92509250509295509295509295565b6000806000806060858703121561382457613823613384565b5b600085013567ffffffffffffffff81111561384257613841613389565b5b61384e87828801613654565b94509450506020613861878288016133a5565b9250506040613872878288016133a5565b91505092959194509250565b6000806040838503121561389557613894613384565b5b60006138a3858286016133a5565b92505060206138b485828601613430565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6138f381613407565b82525050565b600061390583836138ea565b60208301905092915050565b6000602082019050919050565b6000613929826138be565b61393381856138c9565b935061393e836138da565b8060005b8381101561396f57815161395688826138f9565b975061396183613911565b925050600181019050613942565b5085935050505092915050565b60006020820190508181036000830152613996818461391e565b905092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6139d381613346565b82525050565b60006139e583836139ca565b60208301905092915050565b6000602082019050919050565b6000613a098261399e565b613a1381856139a9565b9350613a1e836139ba565b8060005b83811015613a4f578151613a3688826139d9565b9750613a41836139f1565b925050600181019050613a22565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b83811015613ac2578082015181840152602081019050613aa7565b83811115613ad1576000848401525b50505050565b6000613ae282613a88565b613aec8185613a93565b9350613afc818560208601613aa4565b613b05816134b2565b840191505092915050565b6000613b1c8383613ad7565b905092915050565b6000602082019050919050565b6000613b3c82613a5c565b613b468185613a67565b935083602082028501613b5885613a78565b8060005b85811015613b945784840389528151613b758582613b10565b9450613b8083613b24565b925060208a01995050600181019050613b5c565b50829750879550505050505092915050565b60006060820190508181036000830152613bc0818661391e565b90508181036020830152613bd481856139fe565b90508181036040830152613be88184613b31565b9050949350505050565b60008060408385031215613c0957613c08613384565b5b6000613c1785828601613430565b9250506020613c2885828601613430565b9150509250929050565b613c3b81613407565b82525050565b6000602082019050613c566000830184613c32565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680613cd257607f821691505b60208210811415613ce657613ce5613c8b565b5b50919050565b600082825260208201905092915050565b6000613d0882613a88565b613d128185613cec565b9350613d22818560208601613aa4565b613d2b816134b2565b840191505092915050565b6000604082019050613d4b6000830185613350565b8181036020830152613d5d8184613cfd565b90509392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000613da082613346565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415613dd357613dd2613d66565b5b600182019050919050565b6000604082019050613df36000830185613350565b613e006020830184613c32565b9392505050565b600082825260208201905092915050565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f64656c656761746563616c6c0000000000000000000000000000000000000000602082015250565b6000613e74602c83613e07565b9150613e7f82613e18565b604082019050919050565b60006020820190508181036000830152613ea381613e67565b9050919050565b7f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060008201527f6163746976652070726f78790000000000000000000000000000000000000000602082015250565b6000613f06602c83613e07565b9150613f1182613eaa565b604082019050919050565b60006020820190508181036000830152613f3581613ef9565b9050919050565b600067ffffffffffffffff821115613f5757613f566134c3565b5b602082029050602081019050919050565b6000613f7b613f7684613f3c565b613523565b90508083825260208201905060208402830185811115613f9e57613f9d61364f565b5b835b81811015613fe557803567ffffffffffffffff811115613fc357613fc26134a8565b5b808601613fd089826135c0565b85526020850194505050602081019050613fa0565b5050509392505050565b6000613ffc368484613f68565b905092915050565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b6000614060602e83613e07565b915061406b82614004565b604082019050919050565b6000602082019050818103600083015261408f81614053565b9050919050565b60006040820190506140ab6000830185613350565b6140b86020830184613350565b9392505050565b60006140ca82613346565b91506140d583613346565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561410a57614109613d66565b5b828201905092915050565b600081905092915050565b600061412b82613a88565b6141358185614115565b9350614145818560208601613aa4565b80840191505092915050565b600061415d8284614120565b915081905092915050565b7f45524331393637557067726164653a207570677261646520627265616b73206660008201527f7572746865722075706772616465730000000000000000000000000000000000602082015250565b60006141c4602f83613e07565b91506141cf82614168565b604082019050919050565b600060208201905081810360008301526141f3816141b7565b9050919050565b600061420582613346565b915061421083613346565b92508282101561422357614222613d66565b5b828203905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b60006142b9602d83613e07565b91506142c48261425d565b604082019050919050565b600060208201905081810360008301526142e8816142ac565b9050919050565b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b600061434b602683613e07565b9150614356826142ef565b604082019050919050565b6000602082019050818103600083015261437a8161433e565b9050919050565b600081519050919050565b600061439782614381565b6143a18185613e07565b93506143b1818560208601613aa4565b6143ba816134b2565b840191505092915050565b600060208201905081810360008301526143df818461438c565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220303e04746ad337ff4cf7e183142493b03ba7ffb20e40343fd30e314c9a21f40264736f6c634300080b0033