ETH Price: $2,575.02 (-4.66%)
Gas: 1.2 Gwei

Contract

0x42182b2a508E08FA23154A26aE63E77F9A583720
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Renounce Operato...166429572023-02-16 18:03:23722 days ago1676570603IN
0x42182b2a...F9A583720
0 ETH0.001241649.13952716
Update Claim Gen...166429392023-02-16 17:59:47722 days ago1676570387IN
0x42182b2a...F9A583720
0 ETH0.0013510343.67339018
Update Coordinat...166429382023-02-16 17:59:35722 days ago1676570375IN
0x42182b2a...F9A583720
0 ETH0.0012982242.02733257
Add Operator166429362023-02-16 17:59:11722 days ago1676570351IN
0x42182b2a...F9A583720
0 ETH0.002091640.08435618
Stake164193182023-01-16 12:22:23753 days ago1673871743IN
0x42182b2a...F9A583720
0 ETH0.0009899317.34956168
Stake164193182023-01-16 12:22:23753 days ago1673871743IN
0x42182b2a...F9A583720
0 ETH0.0009899317.34956168
Stake164193182023-01-16 12:22:23753 days ago1673871743IN
0x42182b2a...F9A583720
0 ETH0.0009899317.34956168
Stake164192932023-01-16 12:17:23753 days ago1673871443IN
0x42182b2a...F9A583720
0 ETH0.0014262919.23317928
Stake164192932023-01-16 12:17:23753 days ago1673871443IN
0x42182b2a...F9A583720
0 ETH0.0014262919.23317928
Stake164192932023-01-16 12:17:23753 days ago1673871443IN
0x42182b2a...F9A583720
0 ETH0.0014262919.23317928
Stake164188782023-01-16 10:54:11753 days ago1673866451IN
0x42182b2a...F9A583720
0 ETH0.0009065415.88809458
Stake164188782023-01-16 10:54:11753 days ago1673866451IN
0x42182b2a...F9A583720
0 ETH0.0009065415.88809458
Stake164188562023-01-16 10:49:47753 days ago1673866187IN
0x42182b2a...F9A583720
0 ETH0.0007869713.79246293
Stake164188452023-01-16 10:47:35753 days ago1673866055IN
0x42182b2a...F9A583720
0 ETH0.0011192415.09276667
Stake164187122023-01-16 10:20:47753 days ago1673864447IN
0x42182b2a...F9A583720
0 ETH0.0008854915.51913392
Stake164186312023-01-16 10:04:35753 days ago1673863475IN
0x42182b2a...F9A583720
0 ETH0.0011139315.02104393
Stake164186312023-01-16 10:04:35753 days ago1673863475IN
0x42182b2a...F9A583720
0 ETH0.0008570715.02104393
Stake164185262023-01-16 9:43:11753 days ago1673862191IN
0x42182b2a...F9A583720
0 ETH0.0009610616.84363814
Stake164185262023-01-16 9:43:11753 days ago1673862191IN
0x42182b2a...F9A583720
0 ETH0.0009610616.84363814
Stake164184162023-01-16 9:21:11753 days ago1673860871IN
0x42182b2a...F9A583720
0 ETH0.000976317.11079591
Stake164184102023-01-16 9:19:47753 days ago1673860787IN
0x42182b2a...F9A583720
0 ETH0.0011784415.8909478
Claim159998992022-11-18 22:31:35812 days ago1668810695IN
0x42182b2a...F9A583720
0 ETH0.0007531312.69798696
Settle Spent Sta...159998992022-11-18 22:31:35812 days ago1668810695IN
0x42182b2a...F9A583720
0 ETH0.0008245812.69798696
Claim And Unwrap159998922022-11-18 22:30:11812 days ago1668810611IN
0x42182b2a...F9A583720
0 ETH0.000829212.53177872
Settle Spent Sta...159998742022-11-18 22:26:35812 days ago1668810395IN
0x42182b2a...F9A583720
0 ETH0.000822312.66290551
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
159998922022-11-18 22:30:11812 days ago1668810611
0x42182b2a...F9A583720
0.00229959 ETH
159998922022-11-18 22:30:11812 days ago1668810611
0x42182b2a...F9A583720
0.00229959 ETH
159998592022-11-18 22:23:35812 days ago1668810215
0x42182b2a...F9A583720
0.0017934 ETH
159998592022-11-18 22:23:35812 days ago1668810215
0x42182b2a...F9A583720
0.0017934 ETH
159996812022-11-18 21:47:59812 days ago1668808079
0x42182b2a...F9A583720
0.00194339 ETH
159996812022-11-18 21:47:59812 days ago1668808079
0x42182b2a...F9A583720
0.00194339 ETH
159996492022-11-18 21:41:35812 days ago1668807695
0x42182b2a...F9A583720
0.00218473 ETH
159996492022-11-18 21:41:35812 days ago1668807695
0x42182b2a...F9A583720
0.00218473 ETH
159996402022-11-18 21:39:47812 days ago1668807587
0x42182b2a...F9A583720
0.00163325 ETH
159996402022-11-18 21:39:47812 days ago1668807587
0x42182b2a...F9A583720
0.00163325 ETH
159995882022-11-18 21:29:23812 days ago1668806963
0x42182b2a...F9A583720
0.00211925 ETH
159995882022-11-18 21:29:23812 days ago1668806963
0x42182b2a...F9A583720
0.00211925 ETH
159995052022-11-18 21:12:23812 days ago1668805943
0x42182b2a...F9A583720
0.00164547 ETH
159995052022-11-18 21:12:23812 days ago1668805943
0x42182b2a...F9A583720
0.00164547 ETH
159992382022-11-18 20:18:59812 days ago1668802739
0x42182b2a...F9A583720
0.00130927 ETH
159992382022-11-18 20:18:59812 days ago1668802739
0x42182b2a...F9A583720
0.00130927 ETH
159990332022-11-18 19:37:47812 days ago1668800267
0x42182b2a...F9A583720
0.0063698 ETH
159990332022-11-18 19:37:47812 days ago1668800267
0x42182b2a...F9A583720
0.0063698 ETH
159985992022-11-18 18:10:11812 days ago1668795011
0x42182b2a...F9A583720
0.00294794 ETH
159985992022-11-18 18:10:11812 days ago1668795011
0x42182b2a...F9A583720
0.00294794 ETH
159984432022-11-18 17:38:59812 days ago1668793139
0x42182b2a...F9A583720
0.0040452 ETH
159984432022-11-18 17:38:59812 days ago1668793139
0x42182b2a...F9A583720
0.0040452 ETH
159984102022-11-18 17:32:23812 days ago1668792743
0x42182b2a...F9A583720
0.00205798 ETH
159984102022-11-18 17:32:23812 days ago1668792743
0x42182b2a...F9A583720
0.00205798 ETH
159939692022-11-18 2:39:35812 days ago1668739175
0x42182b2a...F9A583720
0.01421031 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CoordinationPaymentChannels

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 12 : CoordinationPaymentChannels.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./CanReclaimTokens.sol";

interface IWETH {
    function withdraw(uint wad) external;
}

contract CoordinationPaymentChannels is CanReclaimTokens {
    using SafeERC20 for IERC20;

    IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    bytes constant public INSTANT_WITHDRAWAL_COMMITMENT_DATA = bytes("INSTANT");

    // This keypair is responsible for signing stake commitments
    address public coordinator;
    // This keypair is responsible for signing user claim commitments
    address public claimGenerator;

    /* coordinator <-> keeper payment channels
     * The Coordinator and Keepers transact off-chain using these payment channels in a series of auctions
     * hosted by the Coordinator. The values stored in these variables reflect the latest on-chain state for a
     * payment channel, but the actual state of the payment channels is likely to be more recent than what is on
     * chain. The Coordinator and keeepers will pass signed commitments back and forth dictating what the current state
     * of the payment channel is.
     * A payment channel is keyed by a Keeper's stake address.
     * Note a keeper may have multiple payment channels / stake addresses.
     */
    // The amount of WETH a keeper's stake address has staked. This determines their maximum spending power.
    mapping (address => uint256) public stakedAmount;
    // This nonce is used to give an ordering to the off-chain stake commitments and to prevent replays on-chain.
    mapping (address => uint256) public stakeNonce;
    // The total amount of WETH a keeper has currently spent. Cannot be greater than stakedAmount.
    mapping (address => uint256) public stakeSpent;
    // Used to prevent channel reuse edge cases if a stake address closes their channel and opens another.
    mapping (address => uint256) public channelNonce;
    // Used to track the expiration of withdrawal timelocks.
    mapping (bytes32 => uint256) public withdrawalTimelockTimestamp;

    /* claim generator -> user payment channels
     * The Claim Generator will generate claims using the Keeper bids from the Coordinator auctions. Claim amounts are
     * calculated as a percentage of the bid amounts determined by the DAO. Given that bids are signed by Keepers and
     * the Claim Generator, and they are available publicly, claim amount correcntess is easily verifiable off-chain.
     *
     * userClaimedAmount tracks how much a user has claimed to date. A user's outstanding claim amount is given by
     * subtracting userClaimedAmount from their most recent claim commitment generated by the Claim Generator.
     */
    mapping (address => uint256) public userClaimedAmount;


    /* Total amount of claimable WETH. Accrues when commitments are submitted to `initiateTimelockedWithdrawal`,
     * `executeInstantWithdrawal`, and `settleSpentStake` and when `addClaimable` is called.
     */
    uint256 public totalClaimableAmount;

    event Staked(address indexed _stakeAddress, uint256 _channelNonce, uint256 _amount);
    event Claimed(address indexed _claimAddress, uint256 _amount);
    event CoordinatorChanged(address indexed _oldCoordinator, address indexed _newCoordinator);
    event ClaimGeneratorChanged(address indexed _oldClaimGenerator, address indexed _newClaimGenerator);
    event StakeWithdrawn(address indexed _stakeAddress, uint256 _channelNonce, uint256 _amount);
    event TimelockedWithdrawalInitiated(
        address indexed _stakeAddress, 
        uint256 _stakeSpent, 
        uint256 _stakeNonce, 
        uint256 _channelNonce, 
        uint256 _withdrawalTimelock);
    event AddedClaimable(uint256 _amount);
    event Settled(uint256 _refundedAmount, uint256 _accruedAmount);

    /* Represents a payment channel state. The Coordinator and Keepers will sign commitments to agree upon the current
     * Note, the `data` field is used off-chain to hold the hash of the previous commitment to ensure that the 
     * Coordinator and Keeper state for the payment channel is always consistent. The only time any assertions are made
     * on its value is `executeInstantWithdrawal` when it should equal `INSTANT_WITHDRAWAL_COMMITMENT_DATA`.
     */
    struct StakeCommitment {
        address stakeAddress;
        uint256 stakeSpent;
        uint256 stakeNonce;
        uint256 channelNonce;
        bytes data;
        bytes stakeAddressSignature;
        bytes coordinatorSignature;
    }

    /// @notice Retreives the payment channel state for the provided stake address.
    function getStakerState(
        address _stakeAddress
    ) public view returns (
        uint256 _stakedAmount, 
        uint256 _stakeNonce, 
        uint256 _stakeSpent, 
        uint256 _channelNonce, 
        uint256 _withdrawalTimelock
    ) {
        return (
            stakedAmount[_stakeAddress], 
            stakeNonce[_stakeAddress], 
            stakeSpent[_stakeAddress], 
            channelNonce[_stakeAddress], 
            getCurrentWithdrawalTimelock(_stakeAddress));
    }

    /** @notice Calculate the commitment hash used for signatures for a particular stake payment channel state.
      * @dev The Coordinator and Keepers transact off-chain by passing signed commitments back and forth. Signed stake 
      *      commitments are submitted on-chain only to perform settlements and withdrawals.
      */
    function stakeCommitmentHash(
        address _stakeAddress, 
        uint256 _stakeSpent, 
        uint256 _stakeNonce, 
        uint256 _channelNonce, 
        bytes memory _data
    ) public pure returns (bytes32) {
        return keccak256(abi.encode(_stakeAddress, _stakeSpent, _stakeNonce, _channelNonce, _data));
    }

    function stakeCommitmentHash(
        StakeCommitment memory _commitment
    ) internal pure returns (bytes32) {
        return stakeCommitmentHash(
            _commitment.stakeAddress, 
            _commitment.stakeSpent, 
            _commitment.stakeNonce, 
            _commitment.channelNonce, 
            _commitment.data);
    }

    /** @notice Calculate the commitment hash used for signatures for a particular claim payment channel state.
      *         Signed claim commitments are generated by the claim generator and used by users to perform claims.
      */
    function claimCommitmentHash(
        address _claimAddress, 
        uint256 _earningsToDate
    ) public pure returns (bytes32) {
        return keccak256(abi.encode(_claimAddress, _earningsToDate));
    }

    /** @notice Calculate the key to the entry in the withdrawalTimelockTimestamp map for a particular stake
      *         payment channel state.
      */
    function withdrawalTimelockKey(
        address _stakeAddress, 
        uint256 _stakeSpent, 
        uint256 _stakeNonce, 
        uint256 _channelNonce
    ) public pure returns (bytes32) {
        return keccak256(abi.encode(_stakeAddress, _stakeSpent, _stakeNonce, _channelNonce));
    }

    /** @notice Get the withdrawal timelock expiration for a payment channel. A return value of 0 indicates no timelock.
      * @dev The withdrawal timelock allows the Coordinator to withdrawals with newer commitments.
      */
    function getCurrentWithdrawalTimelock(
        address _stakeAddress
    ) public view returns (uint256) {
        return withdrawalTimelockTimestamp[
            withdrawalTimelockKey(
                _stakeAddress, 
                stakeSpent[_stakeAddress], 
                stakeNonce[_stakeAddress], 
                channelNonce[_stakeAddress])];
    }

    constructor(address _coordinator, address _claimGenerator) {
        coordinator = _coordinator;
        claimGenerator = _claimGenerator;
        blacklistRecoverableToken(address(WETH));
        emit CoordinatorChanged(address(0), _coordinator);
        emit ClaimGeneratorChanged(address(0), _claimGenerator);
    }

    fallback() external payable {}

    receive() external payable {}

    /** @notice Update the Coordinator address. This keypair is responsible for signing stake commitments.
      * @dev To migrate Coordinator addresses, any commitments signed by the old Coordinator must be resigned by the
      *      new Coordinator address.
      */ 
    function updateCoordinatorAddress(
        address _newCoordinator
    ) external onlyOperator {
        emit CoordinatorChanged(coordinator, _newCoordinator);
        coordinator = _newCoordinator;
    }

    /** @notice Update the claimGenerator address. This keypair is responsible to for signing user claim commitments.
      * @dev To migrate claimGenerator addresses, any commitments signed by the old Claim address must be resigned by
      *      the new claimGenerator address.
      */
    function updateClaimGeneratorAddress(
        address _newClaimGenerator
    ) external onlyOperator {
        emit ClaimGeneratorChanged(claimGenerator, _newClaimGenerator);
        claimGenerator = _newClaimGenerator;
    }

    /** @notice Add WETH to the payment channel of msg.sender. Cannot be done while in a timelocked withdrawal.
      * @dev Withdrawal of stake will require a signature from the Coordinator.
      */
    function stake(
        uint256 _amount
    ) public {
        require(getCurrentWithdrawalTimelock(msg.sender) == 0, "cannot stake while in withdrawal");
        WETH.safeTransferFrom(msg.sender, address(this), _amount);
        stakedAmount[msg.sender] += _amount;
        emit Staked(msg.sender, channelNonce[msg.sender], stakedAmount[msg.sender]);
    }

    /** @notice Used to add claimable WETH to the contract.
      * @dev Since claimable WETH otherwise only accrues on withdrawal or settlement of WETH, this can be used create a
      *      buffer of immediately claimable WETH so users do not need to wait for a Keeper to withdraw or for someone
      *      to call the `settleSpentStake` function. This can also be used to amend deficits from overgenerous claims.
      */
    function addClaimable(
        uint256 _amount
    ) public {
        WETH.safeTransferFrom(msg.sender, address(this), _amount);
        totalClaimableAmount += _amount;
        emit AddedClaimable(_amount);
    }

    /** @dev The stakeSpent for a payment channel will increase when a stake address makes payments and decrease
      *      when the Coordinator issues refunds. This function changes the totalClaimableAmount of WETH on the contract
      *      accordingly.
      */
    function adjustTotalClaimableAmountByStakeSpentChange(
        uint256 _oldStakeSpent, 
        uint256 _newStakeSpent
    ) internal {
        if (_newStakeSpent < _oldStakeSpent) {
            // If a stake address's new stakeSpent is less than their previously stored stakeSpent, then a refund was 
            // issued to the stakeAddress. We "refund" this WETH to the stakeAddress by subtracting 
            // the difference from totalClaimableAmount.
            uint256 refundAmount = _oldStakeSpent - _newStakeSpent;
            require(totalClaimableAmount >= refundAmount, "not enough claimable WETH to refund");
            totalClaimableAmount -= refundAmount;
        } else {
            // Otherwise we accrue any unsettled spent stake to totalClaimableAmount.
            totalClaimableAmount += _newStakeSpent - _oldStakeSpent;
        }
    }

    /** @notice Used to settle spent stake for a payment channel to accrue claimable WETH. 
      * @dev Note anyone can call this function since it requires a signature from both keepers and the coordinator.
      *      It will primarily be used by the coordinator but a user who would like to claim WETH immediately when there
      *      is no claimable WETH may also call this to accrue claimable WETH.
      * @param _commitments is a list of StakeCommitments. There should be one entry for each payment channel being
      *        settled and it should be the latest commitment for that channel. We only care about the most recent
      *        state for a payment channel, so evenif there are multiple commitments for a payment channel that have not
      *        been submitted on-chain, only the latest needs to be submitted. The resulting contract state will be the
      *        same regardless.
      */
    function settleSpentStake(
        StakeCommitment[] memory _commitments
    ) external {
        uint256 claimableWETHToAccrue = 0;
        uint256 claimableWETHToRefund = 0;
        for (uint i=0; i< _commitments.length; i++) {
            StakeCommitment memory commitment = _commitments[i];
            // Initiating withdrawal settles the spent stake in the payment channel. Thus we disable settling spent
            // stake using this function for a payment channel in withdrawal. Proper settlement of the payment channel
            // should be handled by the withdrawal process.
            require(getCurrentWithdrawalTimelock(commitment.stakeAddress) == 0, "cannot settle while in withdrawal");
            require(commitment.stakeSpent <= stakedAmount[commitment.stakeAddress], "cannot spend more than is staked");
            require(commitment.stakeNonce > stakeNonce[commitment.stakeAddress], "stake nonce is too old");
            require(commitment.channelNonce == channelNonce[commitment.stakeAddress], "incorrect channel nonce");

            address recoveredStakeAddress = ECDSA.recover(
                ECDSA.toEthSignedMessageHash(stakeCommitmentHash(commitment)), 
                commitment.stakeAddressSignature);
            require(recoveredStakeAddress == commitment.stakeAddress, "recovered address is not the stake address");
            address recoveredCoordinatorAddress =  ECDSA.recover(
                ECDSA.toEthSignedMessageHash(stakeCommitmentHash(commitment)), 
                commitment.coordinatorSignature);
            require(recoveredCoordinatorAddress == coordinator, "recovered address is not the coordinator");

            if (commitment.stakeSpent < stakeSpent[commitment.stakeAddress]) {
                // If a stake address's new stakeSpent is less than their previously stored stakeSpent, then a refund was 
                // issued to the stakeAddress. We "refund" this WETH to the stakeAddress by subtracting 
                // the difference from totalClaimableAmount.
                claimableWETHToRefund += stakeSpent[commitment.stakeAddress] - commitment.stakeSpent;
            } else {
                // Otherwise we accrue any unsettled spent stake to totalClaimableAmount
                claimableWETHToAccrue += commitment.stakeSpent - stakeSpent[commitment.stakeAddress];
            }
            stakeNonce[commitment.stakeAddress] = commitment.stakeNonce;
            stakeSpent[commitment.stakeAddress] = commitment.stakeSpent;
        }
        adjustTotalClaimableAmountByStakeSpentChange(claimableWETHToRefund, claimableWETHToAccrue);
        emit Settled(claimableWETHToRefund, claimableWETHToAccrue);
    }

    /** @notice Initiate or challenge withdrawal, which can be completed after a 7 day timelock expires.
      * @dev Used by the stake addresses to withdraw from and close the payment channel. Also used by the Coordinator to
      *      challenge a withdrawal. The most recent commitment for the payment channel that has been signed by both the
      *      stake address and the Coordinator should be used for the withdrawal. In order to prevent Keepers from
      *      immediately exiting with an old commitment, potentially taking WETH they do not own, there is a 7 day
      *      timelock on being able to complete the withdrawal. If the Coordinator has a more recent commitment, they
      *      will submit it to this function resetting the timelock.
      */
    function initiateTimelockedWithdrawal(
        StakeCommitment memory _commitment
    ) external {
        // This is to prevent people who are not the payment channel owner from initiating the withdrawal process.
        if (getCurrentWithdrawalTimelock(_commitment.stakeAddress) == 0) {
            require(msg.sender == _commitment.stakeAddress, "only stakeAddress can start the withdrawal process");
        }
        require(_commitment.stakeSpent <= stakedAmount[_commitment.stakeAddress], "cannot spend more than is staked");
        // The stakeNonce may have been seen in settleSpentStake so we must allow >= to here.
        // Note this means a malicious or compromised Coordinator has the ability to indefinitely reset the timelock.
        // This is fine since we can just change the Coordinator address.
        require(_commitment.stakeNonce >= stakeNonce[_commitment.stakeAddress], "stake nonce is too old");
        require(_commitment.channelNonce == channelNonce[_commitment.stakeAddress], "incorrect channel nonce");
        require(msg.sender == _commitment.stakeAddress || msg.sender == coordinator, 
            "only callable by stakeAdddress or coordinator");
        
        address recoveredStakeAddress = ECDSA.recover(
            ECDSA.toEthSignedMessageHash(stakeCommitmentHash(_commitment)), 
            _commitment.stakeAddressSignature);
        require(recoveredStakeAddress == _commitment.stakeAddress, "recovered address is not the stake address");
        address recoveredCoordinatorAddress =  ECDSA.recover(
            ECDSA.toEthSignedMessageHash(stakeCommitmentHash(_commitment)), 
            _commitment.coordinatorSignature);
        require(recoveredCoordinatorAddress == coordinator, "recovered address is not the coordinator");

        adjustTotalClaimableAmountByStakeSpentChange(stakeSpent[_commitment.stakeAddress], _commitment.stakeSpent);
        stakeNonce[_commitment.stakeAddress] = _commitment.stakeNonce;
        stakeSpent[_commitment.stakeAddress] = _commitment.stakeSpent;

        // Initiate the timelock
        withdrawalTimelockTimestamp[
            withdrawalTimelockKey(
                _commitment.stakeAddress, 
                _commitment.stakeSpent, 
                _commitment.stakeNonce, 
                _commitment.channelNonce
            )] = block.timestamp + 7 days;

        emit TimelockedWithdrawalInitiated(
            _commitment.stakeAddress, 
            _commitment.stakeSpent, 
            _commitment.stakeNonce, 
            _commitment.channelNonce, 
            block.timestamp + 7 days);
    }

    /** @notice Withdraw remaining stake from the payment channel after the withdrawal timelock has concluded.
      * @dev Closing the payment channel zeros out all payment channel state for the stake address aside from the
      *      channelNonce which is incremented. This is to prevent any channel reuse edge cases. A stake address that
      *      has closed their payment channel and withdrawn is able to create a new payment channel by staking WETH. 
      */
    function executeTimelockedWithdrawal(
        address _stakeAddress
    ) public {
        uint256 _channelNonce = channelNonce[_stakeAddress];
        require(getCurrentWithdrawalTimelock(_stakeAddress) > 0, "must initiate timelocked withdrawal first");
        require(block.timestamp > getCurrentWithdrawalTimelock(_stakeAddress), "still in withdrawal timelock");
        
        uint256 withdrawalAmount = stakedAmount[_stakeAddress] - stakeSpent[_stakeAddress];
        stakeNonce[_stakeAddress] = 0;
        stakeSpent[_stakeAddress] = 0;
        stakedAmount[_stakeAddress] = 0;
        channelNonce[_stakeAddress] += 1;

        WETH.safeTransfer(_stakeAddress, withdrawalAmount);

        emit StakeWithdrawn(_stakeAddress, _channelNonce, withdrawalAmount);
    }

    /** @notice Instantly withdraw remaining stake in payment channel.
      * @dev To perform an instant withdrawal a Keeper will ask the coordinator for an instant withdrawal signature. 
      *      This `data` field in the commitment used to produce the signature should be populated with 
      *      `INSTANT_WITHDRAWAL_COMMITMENT_DATA`. We don't want a compromised Coordinator to instantly settle the 
      *      channel with an old commitment, so we also require a stakeAddress instant withdrawal signature.
      *      Closing the payment channel zeros out all payment channel state for the stake address aside from the
      *      channelNonce which is incremented. This is to prevent any channel reuse edge cases. A stake address that
      *      has closed their payment channel and withdrawn is able to create a new payment channel by staking WETH.
      */
    function executeInstantWithdrawal(
        StakeCommitment memory _commitment
    ) external {
        require(msg.sender == _commitment.stakeAddress, "only stakeAddress can perform instant withdrawal");
        require(_commitment.stakeSpent <= stakedAmount[_commitment.stakeAddress], "cannot spend more than is staked");
        // The stakeNonce may have been seen in settleSpentStake so we must allow >= to here.
        // Note this means a malicious or compromised Coordinator has the ability to indefinitely reset the timelock.
        // This is fine since we can just change the Coordinator address.
        require(_commitment.stakeNonce >= stakeNonce[_commitment.stakeAddress], "stake nonce is too old");
        require(_commitment.channelNonce == channelNonce[_commitment.stakeAddress], "incorrect channel nonce");
        require(keccak256(_commitment.data) == keccak256(INSTANT_WITHDRAWAL_COMMITMENT_DATA), "incorrect data payload");

        address recoveredStakeAddress =  ECDSA.recover(
            ECDSA.toEthSignedMessageHash(stakeCommitmentHash(_commitment)),
            _commitment.stakeAddressSignature);
        require(recoveredStakeAddress == _commitment.stakeAddress, "recovered address is not the stake address");
        address recoveredCoordinatorAddress =  ECDSA.recover(
            ECDSA.toEthSignedMessageHash(stakeCommitmentHash(_commitment)),
            _commitment.coordinatorSignature);
        require(recoveredCoordinatorAddress == coordinator, "recovered address is not the coordinator");

        adjustTotalClaimableAmountByStakeSpentChange(stakeSpent[_commitment.stakeAddress], _commitment.stakeSpent);
        uint256 withdrawalAmount = stakedAmount[_commitment.stakeAddress] - _commitment.stakeSpent;
        stakeNonce[_commitment.stakeAddress] = 0;
        stakeSpent[_commitment.stakeAddress] = 0;
        stakedAmount[_commitment.stakeAddress] = 0;
        channelNonce[_commitment.stakeAddress] += 1;

        WETH.safeTransfer(_commitment.stakeAddress, withdrawalAmount);

        emit StakeWithdrawn(_commitment.stakeAddress, _commitment.channelNonce, withdrawalAmount);
    }


    /** @notice Claim accumulated earnings. Claim amounts are determined off-chain and signed by the claimGenerator
      *         address. Claim amounts are calculated as a pre-determined percentage of a Keeper's bid, and Keeper bids
      *         are signed by both Keepers and the Coordinator, so claim amount correctness is easily verifiable
      *         off-chain by consumers even if it is not verified on-chain.
      * @dev Note that it is not feasible to verify claim amount correctness on-chain as the total claim amount for
      *      a user can be the sum of claims generated from any number of bid commitments. Claimers only rely on the
      *      claim generator to calculate the claim as a percentage of Keeper bids correctly and this is easily
      *      verifiable by consumers off-chain given the signed bid commitments are public.
      *      A user has claimable WETH if the Claim Generator has generated a commitment for them where `earningsToDate`
      *      is greater than `userClaimedAmount[_claimAddress]`.
      */
    function claim(
        address _claimAddress,
        uint256 _earningsToDate,
        bytes memory _claimGeneratorSignature
    ) external {
        processClaim(
            _claimAddress,
            _earningsToDate,
            _claimGeneratorSignature,
            false
        );
    }

    /** @notice Claim accumulated earnings, and unwrap to ETH. Claim amounts are determined off-chain and signed by the claimGenerator
      *         address. Claim amounts are calculated as a pre-determined percentage of a Keeper's bid, and Keeper bids
      *         are signed by both Keepers and the Coordinator, so claim amount correctness is easily verifiable
      *         off-chain by consumers even if it is not verified on-chain.
      * @dev Note that it is not feasible to verify claim amount correctness on-chain as the total claim amount for
      *      a user can be the sum of claims generated from any number of bid commitments. Claimers only rely on the
      *      claim generator to calculate the claim as a percentage of Keeper bids correctly and this is easily
      *      verifiable by consumers off-chain given the signed bid commitments are public.
      *      A user has claimable WETH if the Claim Generator has generated a commitment for them where `earningsToDate`
      *      is greater than `userClaimedAmount[_claimAddress]`.
      */
    function claimAndUnwrap(
        address _claimAddress,
        uint256 _earningsToDate,
        bytes memory _claimGeneratorSignature
    ) external {
        processClaim(
            _claimAddress,
            _earningsToDate,
            _claimGeneratorSignature,
            true
        );
    }

    /** @notice Claim accumulated earnings. Claim amounts are determined off-chain and signed by the claimGenerator
      *         address. Claim amounts are calculated as a pre-determined percentage of a Keeper's bid, and Keeper bids
      *         are signed by both Keepers and the Coordinator, so claim amount correctness is easily verifiable
      *         off-chain by consumers even if it is not verified on-chain.
      * @dev Note that it is not feasible to verify claim amount correctness on-chain as the total claim amount for
      *      a user can be the sum of claims generated from any number of bid commitments. Claimers only rely on the
      *      claim generator to calculate the claim as a percentage of Keeper bids correctly and this is easily
      *      verifiable by consumers off-chain given the signed bid commitments are public.
      *      A user has claimable WETH if the Claim Generator has generated a commitment for them where `earningsToDate`
      *      is greater than `userClaimedAmount[_claimAddress]`.
      */
    function processClaim(
        address _claimAddress,
        uint256 _earningsToDate,
        bytes memory _claimGeneratorSignature,
        bool unwrap
    ) internal {
        require(_earningsToDate > userClaimedAmount[_claimAddress], "nothing to claim");

        // Validate the signature
        address recoveredClaimGeneratorAddress = ECDSA.recover(
            ECDSA.toEthSignedMessageHash(claimCommitmentHash(_claimAddress, _earningsToDate)),
            _claimGeneratorSignature);
        require(recoveredClaimGeneratorAddress == claimGenerator, "recoveredClaimGeneratorAddress is not the account manager");

        // Calculate the claimAmount
        uint256 claimAmount = _earningsToDate - userClaimedAmount[_claimAddress];
        require(claimAmount <= totalClaimableAmount, "claim amount exceeds balance on contract");
        userClaimedAmount[_claimAddress] = _earningsToDate;
        totalClaimableAmount -= claimAmount;

        // Transfer the earnings to _claimAddress
        if (unwrap) {
            IWETH(address(WETH)).withdraw(claimAmount);
            (bool sent, bytes memory data) = _claimAddress.call{value: claimAmount}("");
            require(sent, "Failed to send Ether");
        } else {
            WETH.safeTransfer(_claimAddress, claimAmount);
        }

        emit Claimed(_claimAddress, claimAmount);
    }
}

File 2 of 12 : AccessControl.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    function hasRole(bytes32 role, address account) external view returns (bool);
    function getRoleAdmin(bytes32 role) external view returns (bytes32);
    function grantRole(bytes32 role, address account) external;
    function revokeRole(bytes32 role, address account) external;
    function renounceRole(bytes32 role, address account) external;
}

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping (address => bool) members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId
            || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if(!hasRole(role, account)) {
            revert(string(abi.encodePacked(
                "AccessControl: account ",
                Strings.toHexString(uint160(account), 20),
                " is missing role ",
                Strings.toHexString(uint256(role), 32)
            )));
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 3 of 12 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 4 of 12 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

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

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 5 of 12 : Address.sol
// SPDX-License-Identifier: MIT

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;
        // solhint-disable-next-line no-inline-assembly
        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");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 6 of 12 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

    function _msgData() internal view virtual returns (bytes calldata) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 7 of 12 : ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            // solhint-disable-next-line no-inline-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
        } else if (signature.length == 64) {
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let vs := mload(add(signature, 0x40))
                r := mload(add(signature, 0x20))
                s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                v := add(shr(255, vs), 27)
            }
        } else {
            revert("ECDSA: invalid signature length");
        }

        return recover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
        require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 8 of 12 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 9 of 12 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 10 of 12 : Strings.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant alphabet = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = alphabet[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

}

File 11 of 12 : CanReclaimTokens.sol
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity 0.8.6;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./KRoles.sol";

contract CanReclaimTokens is KRoles {
    using SafeERC20 for IERC20;

    mapping(address => bool) private recoverableTokensBlacklist;

    function blacklistRecoverableToken(address _token) public onlyOperator {
        recoverableTokensBlacklist[_token] = true;
    }

    /// @notice Allow the owner of the contract to recover funds accidentally
    /// sent to the contract. To withdraw ETH, the token should be set to `0x0`.
    function recoverTokens(address _token) external onlyOperator {
        require(
            !recoverableTokensBlacklist[_token],
            "CanReclaimTokens: token is not recoverable"
        );

        if (_token == address(0x0)) {
           (bool success,) = msg.sender.call{ value: address(this).balance }("");
            require(success, "Transfer Failed");
        } else {
            IERC20(_token).safeTransfer(
                msg.sender,
                IERC20(_token).balanceOf(address(this))
            );
        }
    }
}

File 12 of 12 : KRoles.sol
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity 0.8.6;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract KRoles is AccessControl {
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

    event OperatorAdded(address indexed account);
    event OperatorRemoved(address indexed account);

    constructor() {
        /// Note, given that it is possible for both the DEFAULT_ADMIN_ROLE and OPERATOR_ROLE to renounce their roles,
        /// the contract can reach a state where there are not operators or admins. Users of inherting contracts should
        /// be sure to avoid reaching this state, as they will be permanently locked out of using any functions relying
        /// on the `onlyOperator` modifier for access control.
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(OPERATOR_ROLE, _msgSender());
        _setRoleAdmin (OPERATOR_ROLE, DEFAULT_ADMIN_ROLE) ;
    }

    modifier onlyOperator() {
        require(isOperator(_msgSender()), "OperatorRole: caller does not have the Operator role");
        _;
    }

    function isOperator(address account) public view returns (bool) {
        return hasRole(OPERATOR_ROLE, account);
    }

    function addOperator(address account) public {
        _addOperator(account);
    }

    /** @notice Renounces operator role of msg.sender. Note that it is possible for all operators to be renounced, which
      * will lock functions relying on the `onlyOperator` modifier for access control.
      */
    function renounceOperator() public virtual {
        _renounceOperator(msg.sender);
    }

    function _addOperator(address account) internal {
        grantRole(OPERATOR_ROLE, account);
        emit OperatorAdded(account);
    }

    function _renounceOperator(address account) internal {
        renounceRole(OPERATOR_ROLE, account);
        emit OperatorRemoved(account);
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_coordinator","type":"address"},{"internalType":"address","name":"_claimGenerator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"AddedClaimable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_oldClaimGenerator","type":"address"},{"indexed":true,"internalType":"address","name":"_newClaimGenerator","type":"address"}],"name":"ClaimGeneratorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_claimAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_oldCoordinator","type":"address"},{"indexed":true,"internalType":"address","name":"_newCoordinator","type":"address"}],"name":"CoordinatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"OperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"OperatorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_refundedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_accruedAmount","type":"uint256"}],"name":"Settled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_stakeAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_channelNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"StakeWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_stakeAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_channelNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_stakeAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_stakeSpent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_stakeNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_channelNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_withdrawalTimelock","type":"uint256"}],"name":"TimelockedWithdrawalInitiated","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INSTANT_WITHDRAWAL_COMMITMENT_DATA","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"addClaimable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"blacklistRecoverableToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"channelNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_claimAddress","type":"address"},{"internalType":"uint256","name":"_earningsToDate","type":"uint256"},{"internalType":"bytes","name":"_claimGeneratorSignature","type":"bytes"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_claimAddress","type":"address"},{"internalType":"uint256","name":"_earningsToDate","type":"uint256"},{"internalType":"bytes","name":"_claimGeneratorSignature","type":"bytes"}],"name":"claimAndUnwrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_claimAddress","type":"address"},{"internalType":"uint256","name":"_earningsToDate","type":"uint256"}],"name":"claimCommitmentHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"claimGenerator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"coordinator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"stakeAddress","type":"address"},{"internalType":"uint256","name":"stakeSpent","type":"uint256"},{"internalType":"uint256","name":"stakeNonce","type":"uint256"},{"internalType":"uint256","name":"channelNonce","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"stakeAddressSignature","type":"bytes"},{"internalType":"bytes","name":"coordinatorSignature","type":"bytes"}],"internalType":"struct CoordinationPaymentChannels.StakeCommitment","name":"_commitment","type":"tuple"}],"name":"executeInstantWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakeAddress","type":"address"}],"name":"executeTimelockedWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakeAddress","type":"address"}],"name":"getCurrentWithdrawalTimelock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_stakeAddress","type":"address"}],"name":"getStakerState","outputs":[{"internalType":"uint256","name":"_stakedAmount","type":"uint256"},{"internalType":"uint256","name":"_stakeNonce","type":"uint256"},{"internalType":"uint256","name":"_stakeSpent","type":"uint256"},{"internalType":"uint256","name":"_channelNonce","type":"uint256"},{"internalType":"uint256","name":"_withdrawalTimelock","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"stakeAddress","type":"address"},{"internalType":"uint256","name":"stakeSpent","type":"uint256"},{"internalType":"uint256","name":"stakeNonce","type":"uint256"},{"internalType":"uint256","name":"channelNonce","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"stakeAddressSignature","type":"bytes"},{"internalType":"bytes","name":"coordinatorSignature","type":"bytes"}],"internalType":"struct CoordinationPaymentChannels.StakeCommitment","name":"_commitment","type":"tuple"}],"name":"initiateTimelockedWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"recoverTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"stakeAddress","type":"address"},{"internalType":"uint256","name":"stakeSpent","type":"uint256"},{"internalType":"uint256","name":"stakeNonce","type":"uint256"},{"internalType":"uint256","name":"channelNonce","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"stakeAddressSignature","type":"bytes"},{"internalType":"bytes","name":"coordinatorSignature","type":"bytes"}],"internalType":"struct CoordinationPaymentChannels.StakeCommitment[]","name":"_commitments","type":"tuple[]"}],"name":"settleSpentStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakeAddress","type":"address"},{"internalType":"uint256","name":"_stakeSpent","type":"uint256"},{"internalType":"uint256","name":"_stakeNonce","type":"uint256"},{"internalType":"uint256","name":"_channelNonce","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"stakeCommitmentHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakeNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakeSpent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalClaimableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newClaimGenerator","type":"address"}],"name":"updateClaimGeneratorAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newCoordinator","type":"address"}],"name":"updateCoordinatorAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userClaimedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_stakeAddress","type":"address"},{"internalType":"uint256","name":"_stakeSpent","type":"uint256"},{"internalType":"uint256","name":"_stakeNonce","type":"uint256"},{"internalType":"uint256","name":"_channelNonce","type":"uint256"}],"name":"withdrawalTimelockKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"withdrawalTimelockTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040523480156200001157600080fd5b50604051620033d5380380620033d583398101604081905262000034916200032c565b620000416000336200013b565b6200005c600080516020620033b5833981519152336200013b565b62000078600080516020620033b583398151915260006200014b565b600280546001600160a01b038085166001600160a01b0319928316179092556003805492841692909116919091179055620000c773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26200019f565b6040516001600160a01b038316906000907f9a77f4a82be606b9a5cf52d398d0418e87b06091d64c494390eb0222a83652e3908290a36040516001600160a01b038216906000907f9725454688f311a9efb31cf30b914736cea0be217f90e29e7ca4618586ab7e26908290a3505062000364565b6200014782826200026f565b5050565b600082815260208190526040902060010154819060405184907fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff90600090a460009182526020829052604090912060010155565b3360009081527fee57cd81e84075558e8fcc182a1f4393f91fc97f963a136e66b7f949a62f319f602052604090205460ff16620002485760405162461bcd60e51b815260206004820152603460248201527f4f70657261746f72526f6c653a2063616c6c657220646f6573206e6f7420686160448201527f766520746865204f70657261746f7220726f6c65000000000000000000000000606482015260840160405180910390fd5b6001600160a01b03166000908152600160208190526040909120805460ff19169091179055565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000147576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620002cb3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b80516001600160a01b03811681146200032757600080fd5b919050565b600080604083850312156200034057600080fd5b6200034b836200030f565b91506200035b602084016200030f565b90509250929050565b61304180620003746000396000f3fe6080604052600436106102115760003560e01c806383d5129311610117578063b28a1d7e116100a5578063e3e6993c1161006c578063e3e6993c146106ee578063ef81f20d1461070e578063f5b541a61461072e578063f65d901c14610750578063f99318551461077057005b8063b28a1d7e1461062e578063b37b32ae1461066e578063c1dc6b001461068e578063d547741f146106ae578063e10ea844146106ce57005b806391d14854116100e957806391d14854146105a35780639870d7fe146105c3578063a217fddf146105e3578063a694fc3a146105f8578063a89989dd1461061857005b806383d512931461051657806387a2a7381461053657806387c2a3ea146105565780638f0bc1521461058357005b80632f2ff15d1161019f57806342e92d1c1161016657806342e92d1c146104415780635e67405c1461046e5780636d70f7ae1461048e5780636eba7382146104ae5780637db32a2e146104f657005b80632f2ff15d146103875780632f926723146103a757806336568abe146103d45780633a98444d146103f45780633b9e3e091461042157005b80631865a4eb116101e35780631865a4eb146102c75780631e408dfa146102e75780632002052714610322578063248a9ca3146103425780632ab6f8db1461037257005b806301ffc9a71461021a5780630a0090971461024f5780630feff83c1461028757806316114acd146102a757005b3661021857005b005b34801561022657600080fd5b5061023a610235366004612ba6565b61079d565b60405190151581526020015b60405180910390f35b34801561025b57600080fd5b5060025461026f906001600160a01b031681565b6040516001600160a01b039091168152602001610246565b34801561029357600080fd5b506102186102a2366004612937565b6107d4565b3480156102b357600080fd5b506102186102c2366004612937565b61085e565b3480156102d357600080fd5b5060035461026f906001600160a01b031681565b3480156102f357600080fd5b50610314610302366004612937565b60066020526000908152604090205481565b604051908152602001610246565b34801561032e57600080fd5b5061021861033d366004612b61565b610a2c565b34801561034e57600080fd5b5061031461035d366004612b61565b60009081526020819052604090206001015490565b34801561037e57600080fd5b50610218610a99565b34801561039357600080fd5b506102186103a2366004612b7a565b610aa4565b3480156103b357600080fd5b506103146103c2366004612937565b60096020526000908152604090205481565b3480156103e057600080fd5b506102186103ef366004612b7a565b610acf565b34801561040057600080fd5b5061031461040f366004612937565b60076020526000908152604090205481565b34801561042d57600080fd5b5061031461043c3660046129d3565b610b49565b34801561044d57600080fd5b5061031461045c366004612937565b60056020526000908152604090205481565b34801561047a57600080fd5b50610218610489366004612a77565b610b96565b34801561049a57600080fd5b5061023a6104a9366004612937565b610eff565b3480156104ba57600080fd5b506104ce6104c9366004612937565b610f19565b604080519586526020860194909452928401919091526060830152608082015260a001610246565b34801561050257600080fd5b50610314610511366004612937565b610f74565b34801561052257600080fd5b50610218610531366004612937565b610fc8565b34801561054257600080fd5b50610314610551366004612a0c565b611049565b34801561056257600080fd5b50610314610571366004612b61565b60086020526000908152604090205481565b34801561058f57600080fd5b5061021861059e36600461297c565b611085565b3480156105af57600080fd5b5061023a6105be366004612b7a565b611092565b3480156105cf57600080fd5b506102186105de366004612937565b6110bb565b3480156105ef57600080fd5b50610314600081565b34801561060457600080fd5b50610218610613366004612b61565b6110c4565b34801561062457600080fd5b50610314600a5481565b34801561063a57600080fd5b5061066160405180604001604052806007815260200166125394d510539560ca1b81525081565b6040516102469190612d0e565b34801561067a57600080fd5b50610218610689366004612937565b6111bb565b34801561069a57600080fd5b506102186106a9366004612bd0565b611380565b3480156106ba57600080fd5b506102186106c9366004612b7a565b6116f7565b3480156106da57600080fd5b506102186106e936600461297c565b61171d565b3480156106fa57600080fd5b50610314610709366004612952565b61172a565b34801561071a57600080fd5b50610218610729366004612bd0565b611767565b34801561073a57600080fd5b50610314600080516020612fec83398151915281565b34801561075c57600080fd5b5061021861076b366004612937565b611ac1565b34801561077c57600080fd5b5061031461078b366004612937565b60046020526000908152604090205481565b60006001600160e01b03198216637965db0b60e01b14806107ce57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6107dd33610eff565b6108025760405162461bcd60e51b81526004016107f990612d21565b60405180910390fd5b6003546040516001600160a01b038084169216907f9725454688f311a9efb31cf30b914736cea0be217f90e29e7ca4618586ab7e2690600090a3600380546001600160a01b0319166001600160a01b0392909216919091179055565b61086733610eff565b6108835760405162461bcd60e51b81526004016107f990612d21565b6001600160a01b03811660009081526001602052604090205460ff16156108ff5760405162461bcd60e51b815260206004820152602a60248201527f43616e5265636c61696d546f6b656e733a20746f6b656e206973206e6f74207260448201526965636f76657261626c6560b01b60648201526084016107f9565b6001600160a01b03811661099b57604051600090339047908381818185875af1925050503d806000811461094f576040519150601f19603f3d011682016040523d82523d6000602084013e610954565b606091505b50509050806109975760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8811985a5b1959608a1b60448201526064016107f9565b5050565b6040516370a0823160e01b8152306004820152610a299033906001600160a01b038416906370a082319060240160206040518083038186803b1580156109e057600080fd5b505afa1580156109f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a189190612c05565b6001600160a01b0384169190611b0d565b50565b610a4c73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2333084611b70565b80600a6000828254610a5e9190612efd565b90915550506040518181527f777d17221091b7437a19b5bffc96cdde0b40d18e3c4b935901cd1a82625a78d29060200160405180910390a150565b610aa233611bae565b565b600082815260208190526040902060010154610ac08133611bfd565b610aca8383611c61565b505050565b6001600160a01b0381163314610b3f5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016107f9565b6109978282611ce5565b604080516001600160a01b0386166020820152908101849052606081018390526080810182905260009060a001604051602081830303815290604052805190602001209050949350505050565b60008060005b8351811015610eb6576000848281518110610bb957610bb9612fbf565b60200260200101519050610bd08160000151610f74565b15610c275760405162461bcd60e51b815260206004820152602160248201527f63616e6e6f7420736574746c65207768696c6520696e207769746864726177616044820152601b60fa1b60648201526084016107f9565b80516001600160a01b0316600090815260046020908152604090912054908201511115610c665760405162461bcd60e51b81526004016107f990612d75565b80516001600160a01b031660009081526005602052604090819020549082015111610ca35760405162461bcd60e51b81526004016107f990612df4565b80516001600160a01b0316600090815260076020526040902054606082015114610cdf5760405162461bcd60e51b81526004016107f990612e6c565b6000610d4d610d43610cf084611d4a565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b8360a00151611d6d565b905081600001516001600160a01b0316816001600160a01b031614610d845760405162461bcd60e51b81526004016107f990612daa565b6000610d9f610d95610cf085611d4a565b8460c00151611d6d565b6002549091506001600160a01b03808316911614610dcf5760405162461bcd60e51b81526004016107f990612e24565b82516001600160a01b0316600090815260066020908152604090912054908401511015610e325760208084015184516001600160a01b031660009081526006909252604090912054610e219190612f34565b610e2b9086612efd565b9450610e6a565b82516001600160a01b031660009081526006602090815260409091205490840151610e5d9190612f34565b610e679087612efd565b95505b505060408082015182516001600160a01b039081166000908152600560209081528482209390935582850151945190911681526006909152205580610eae81612f8e565b915050610b9c565b50610ec18183611e23565b60408051828152602081018490527ff5b268a3ff315cc44ccceeef86259c9e8eef81ceecb14001543809115380dd62910160405180910390a1505050565b60006107ce600080516020612fec83398151915283611092565b6001600160a01b038116600090815260046020908152604080832054600583528184205460068452828520546007909452918420548493849384938493909290610f628a610f74565b939a9299509097509550909350915050565b6001600160a01b03811660009081526006602090815260408083205460058352818420546007909352908320546008928492610fb292879290610b49565b8152602001908152602001600020549050919050565b610fd133610eff565b610fed5760405162461bcd60e51b81526004016107f990612d21565b6002546040516001600160a01b038084169216907f9a77f4a82be606b9a5cf52d398d0418e87b06091d64c494390eb0222a83652e390600090a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b60008585858585604051602001611064959493929190612cdb565b60405160208183030381529060405280519060200120905095945050505050565b610aca8383836000611ed9565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610a2981612203565b6110cd33610f74565b1561111a5760405162461bcd60e51b815260206004820181905260248201527f63616e6e6f74207374616b65207768696c6520696e207769746864726177616c60448201526064016107f9565b61113a73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2333084611b70565b3360009081526004602052604081208054839290611159908490612efd565b9091555050336000818152600760209081526040808320546004909252918290205491517f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee90926111b0928252602082015260400190565b60405180910390a250565b6001600160a01b038116600090815260076020526040812054906111de83610f74565b1161123d5760405162461bcd60e51b815260206004820152602960248201527f6d75737420696e6974696174652074696d656c6f636b656420776974686472616044820152681dd85b08199a5c9cdd60ba1b60648201526084016107f9565b61124682610f74565b42116112945760405162461bcd60e51b815260206004820152601c60248201527f7374696c6c20696e207769746864726177616c2074696d656c6f636b0000000060448201526064016107f9565b6001600160a01b03821660009081526006602090815260408083205460049092528220546112c29190612f34565b6001600160a01b03841660009081526005602090815260408083208390556006825280832083905560048252808320839055600790915281208054929350600192909190611311908490612efd565b90915550611336905073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28483611b0d565b60408051838152602081018390526001600160a01b038516917f933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafef91015b60405180910390a2505050565b80516001600160a01b031633146113f25760405162461bcd60e51b815260206004820152603060248201527f6f6e6c79207374616b65416464726573732063616e20706572666f726d20696e60448201526f1cdd185b9d081dda5d1a191c985dd85b60821b60648201526084016107f9565b80516001600160a01b03166000908152600460209081526040909120549082015111156114315760405162461bcd60e51b81526004016107f990612d75565b80516001600160a01b0316600090815260056020526040908190205490820151101561146f5760405162461bcd60e51b81526004016107f990612df4565b80516001600160a01b03166000908152600760205260409020546060820151146114ab5760405162461bcd60e51b81526004016107f990612e6c565b604080518082019091526007815266125394d510539560ca1b602091820152608082015180519101207f95f9e5276b62430bf81e5586f1eaa799db1c11b5b20e060bc0001d1d781be52f1461153b5760405162461bcd60e51b81526020600482015260166024820152751a5b98dbdc9c9958dd0819185d18481c185e5b1bd85960521b60448201526064016107f9565b600061154c610d43610cf084611d4a565b905081600001516001600160a01b0316816001600160a01b0316146115835760405162461bcd60e51b81526004016107f990612daa565b6000611594610d95610cf085611d4a565b6002549091506001600160a01b038083169116146115c45760405162461bcd60e51b81526004016107f990612e24565b82516001600160a01b0316600090815260066020908152604090912054908401516115ef9190611e23565b60208084015184516001600160a01b03166000908152600490925260408220546116199190612f34565b84516001600160a01b03908116600090815260056020908152604080832083905588518416835260068252808320839055885184168352600482528083208390558851909316825260079052908120805492935060019290919061167e908490612efd565b909155505083516116a59073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc29083611b0d565b8351606085015160408051918252602082018490526001600160a01b03909216917f933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafef910160405180910390a250505050565b6000828152602081905260409020600101546117138133611bfd565b610aca8383611ce5565b610aca8383836001611ed9565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b805161177290610f74565b6117ea5780516001600160a01b031633146117ea5760405162461bcd60e51b815260206004820152603260248201527f6f6e6c79207374616b65416464726573732063616e20737461727420746865206044820152717769746864726177616c2070726f6365737360701b60648201526084016107f9565b80516001600160a01b03166000908152600460209081526040909120549082015111156118295760405162461bcd60e51b81526004016107f990612d75565b80516001600160a01b031660009081526005602052604090819020549082015110156118675760405162461bcd60e51b81526004016107f990612df4565b80516001600160a01b03166000908152600760205260409020546060820151146118a35760405162461bcd60e51b81526004016107f990612e6c565b80516001600160a01b03163314806118c557506002546001600160a01b031633145b6119275760405162461bcd60e51b815260206004820152602d60248201527f6f6e6c792063616c6c61626c65206279207374616b654164646472657373206f60448201526c391031b7b7b93234b730ba37b960991b60648201526084016107f9565b6000611938610d43610cf084611d4a565b905081600001516001600160a01b0316816001600160a01b03161461196f5760405162461bcd60e51b81526004016107f990612daa565b6000611980610d95610cf085611d4a565b6002549091506001600160a01b038083169116146119b05760405162461bcd60e51b81526004016107f990612e24565b82516001600160a01b0316600090815260066020908152604090912054908401516119db9190611e23565b60408084015184516001600160a01b03908116600090815260056020908152848220939093558287015187519092168152600690925291902055611a224262093a80612efd565b60086000611a428660000151876020015188604001518960600151610b49565b81526020019081526020016000208190555082600001516001600160a01b03167f530f971e91a16cdf0c6acd4e66985535ca021e6a372d18d3c81ae1f8d32f0f398460200151856040015186606001514262093a80611aa19190612efd565b604080519485526020850193909352918301526060820152608001611373565b611aca33610eff565b611ae65760405162461bcd60e51b81526004016107f990612d21565b6001600160a01b03166000908152600160208190526040909120805460ff19169091179055565b6040516001600160a01b038316602482015260448101829052610aca90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612252565b6040516001600160a01b0380851660248301528316604482015260648101829052611ba89085906323b872dd60e01b90608401611b39565b50505050565b611bc6600080516020612fec83398151915282610acf565b6040516001600160a01b038216907f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d90600090a250565b611c078282611092565b61099757611c1f816001600160a01b03166014612324565b611c2a836020612324565b604051602001611c3b929190612c66565b60408051601f198184030181529082905262461bcd60e51b82526107f991600401612d0e565b611c6b8282611092565b610997576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055611ca13390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b611cef8282611092565b15610997576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006107ce82600001518360200151846040015185606001518660800151611049565b600080600080845160411415611d975750505060208201516040830151606084015160001a611e0d565b845160401415611dc55750505060408201516020830151906001600160ff1b0381169060ff1c601b01611e0d565b60405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016107f9565b611e19868285856124c7565b9695505050505050565b81811015611eb5576000611e378284612f34565b905080600a541015611e975760405162461bcd60e51b815260206004820152602360248201527f6e6f7420656e6f75676820636c61696d61626c65205745544820746f207265666044820152621d5b9960ea1b60648201526084016107f9565b80600a6000828254611ea99190612f34565b90915550610997915050565b611ebf8282612f34565b600a6000828254611ed09190612efd565b90915550505050565b6001600160a01b0384166000908152600960205260409020548311611f335760405162461bcd60e51b815260206004820152601060248201526f6e6f7468696e6720746f20636c61696d60801b60448201526064016107f9565b6000611f4b611f45610cf0878761172a565b84611d6d565b6003549091506001600160a01b03808316911614611fd15760405162461bcd60e51b815260206004820152603960248201527f7265636f7665726564436c61696d47656e657261746f7241646472657373206960448201527f73206e6f7420746865206163636f756e74206d616e616765720000000000000060648201526084016107f9565b6001600160a01b038516600090815260096020526040812054611ff49086612f34565b9050600a548111156120595760405162461bcd60e51b815260206004820152602860248201527f636c61696d20616d6f756e7420657863656564732062616c616e6365206f6e2060448201526718dbdb9d1c9858dd60c21b60648201526084016107f9565b6001600160a01b0386166000908152600960205260408120869055600a8054839290612086908490612f34565b9091555050821561219957604051632e1a7d4d60e01b81526004810182905273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290632e1a7d4d90602401600060405180830381600087803b1580156120de57600080fd5b505af11580156120f2573d6000803e3d6000fd5b50505050600080876001600160a01b03168360405160006040518083038185875af1925050503d8060008114612144576040519150601f19603f3d011682016040523d82523d6000602084013e612149565b606091505b5091509150816121925760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a379039b2b7321022ba3432b960611b60448201526064016107f9565b50506121b8565b6121b873c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28783611b0d565b856001600160a01b03167fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a826040516121f391815260200190565b60405180910390a2505050505050565b61221b600080516020612fec83398151915282610aa4565b6040516001600160a01b038216907fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d90600090a250565b60006122a7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166126709092919063ffffffff16565b805190915015610aca57808060200190518101906122c59190612b3f565b610aca5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f9565b60606000612333836002612f15565b61233e906002612efd565b67ffffffffffffffff81111561235657612356612fd5565b6040519080825280601f01601f191660200182016040528015612380576020820181803683370190505b509050600360fc1b8160008151811061239b5761239b612fbf565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106123ca576123ca612fbf565b60200101906001600160f81b031916908160001a90535060006123ee846002612f15565b6123f9906001612efd565b90505b6001811115612471576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061242d5761242d612fbf565b1a60f81b82828151811061244357612443612fbf565b60200101906001600160f81b031916908160001a90535060049490941c9361246a81612f77565b90506123fc565b5083156124c05760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016107f9565b9392505050565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156125445760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016107f9565b8360ff16601b148061255957508360ff16601c145b6125b05760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016107f9565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015612604573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166126675760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016107f9565b95945050505050565b606061267f8484600085612687565b949350505050565b6060824710156126e85760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016107f9565b843b6127365760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f9565b600080866001600160a01b031685876040516127529190612c4a565b60006040518083038185875af1925050503d806000811461278f576040519150601f19603f3d011682016040523d82523d6000602084013e612794565b606091505b50915091506127a48282866127af565b979650505050505050565b606083156127be5750816124c0565b8251156127ce5782518084602001fd5b8160405162461bcd60e51b81526004016107f99190612d0e565b80356001600160a01b03811681146127ff57600080fd5b919050565b600082601f83011261281557600080fd5b813567ffffffffffffffff81111561282f5761282f612fd5565b612842601f8201601f1916602001612ecc565b81815284602083860101111561285757600080fd5b816020850160208301376000918101602001919091529392505050565b600060e0828403121561288657600080fd5b61288e612ea3565b9050612899826127e8565b8152602082013560208201526040820135604082015260608201356060820152608082013567ffffffffffffffff808211156128d457600080fd5b6128e085838601612804565b608084015260a08401359150808211156128f957600080fd5b61290585838601612804565b60a084015260c084013591508082111561291e57600080fd5b5061292b84828501612804565b60c08301525092915050565b60006020828403121561294957600080fd5b6124c0826127e8565b6000806040838503121561296557600080fd5b61296e836127e8565b946020939093013593505050565b60008060006060848603121561299157600080fd5b61299a846127e8565b925060208401359150604084013567ffffffffffffffff8111156129bd57600080fd5b6129c986828701612804565b9150509250925092565b600080600080608085870312156129e957600080fd5b6129f2856127e8565b966020860135965060408601359560600135945092505050565b600080600080600060a08688031215612a2457600080fd5b612a2d866127e8565b9450602086013593506040860135925060608601359150608086013567ffffffffffffffff811115612a5e57600080fd5b612a6a88828901612804565b9150509295509295909350565b60006020808385031215612a8a57600080fd5b823567ffffffffffffffff80821115612aa257600080fd5b818501915085601f830112612ab657600080fd5b813581811115612ac857612ac8612fd5565b8060051b612ad7858201612ecc565b8281528581019085870183870188018b1015612af257600080fd5b60009350835b85811015612b2f57813587811115612b0e578586fd5b612b1c8d8b838c0101612874565b8552509288019290880190600101612af8565b50909a9950505050505050505050565b600060208284031215612b5157600080fd5b815180151581146124c057600080fd5b600060208284031215612b7357600080fd5b5035919050565b60008060408385031215612b8d57600080fd5b82359150612b9d602084016127e8565b90509250929050565b600060208284031215612bb857600080fd5b81356001600160e01b0319811681146124c057600080fd5b600060208284031215612be257600080fd5b813567ffffffffffffffff811115612bf957600080fd5b61267f84828501612874565b600060208284031215612c1757600080fd5b5051919050565b60008151808452612c36816020860160208601612f4b565b601f01601f19169290920160200192915050565b60008251612c5c818460208701612f4b565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351612c9e816017850160208801612f4b565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351612ccf816028840160208801612f4b565b01602801949350505050565b60018060a01b038616815284602082015283604082015282606082015260a0608082015260006127a460a0830184612c1e565b6020815260006124c06020830184612c1e565b60208082526034908201527f4f70657261746f72526f6c653a2063616c6c657220646f6573206e6f74206861604082015273766520746865204f70657261746f7220726f6c6560601b606082015260800190565b6020808252818101527f63616e6e6f74207370656e64206d6f7265207468616e206973207374616b6564604082015260600190565b6020808252602a908201527f7265636f76657265642061646472657373206973206e6f7420746865207374616040820152696b65206164647265737360b01b606082015260800190565b6020808252601690820152751cdd185ad9481b9bdb98d9481a5cc81d1bdbc81bdb1960521b604082015260600190565b60208082526028908201527f7265636f76657265642061646472657373206973206e6f742074686520636f6f604082015267393234b730ba37b960c11b606082015260800190565b60208082526017908201527f696e636f7272656374206368616e6e656c206e6f6e6365000000000000000000604082015260600190565b60405160e0810167ffffffffffffffff81118282101715612ec657612ec6612fd5565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715612ef557612ef5612fd5565b604052919050565b60008219821115612f1057612f10612fa9565b500190565b6000816000190483118215151615612f2f57612f2f612fa9565b500290565b600082821015612f4657612f46612fa9565b500390565b60005b83811015612f66578181015183820152602001612f4e565b83811115611ba85750506000910152565b600081612f8657612f86612fa9565b506000190190565b6000600019821415612fa257612fa2612fa9565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfe97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929a2646970667358221220e21c751c1383342f9175b01b03f5adeb8d72a8c7b3d7b62fd39a11b744e7b6db64736f6c6343000806003397667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9290000000000000000000000003a8f4dff08c45814d730b5f6b268ad8378bebb360000000000000000000000003a8f4dff08c45814d730b5f6b268ad8378bebb36

Deployed Bytecode

0x6080604052600436106102115760003560e01c806383d5129311610117578063b28a1d7e116100a5578063e3e6993c1161006c578063e3e6993c146106ee578063ef81f20d1461070e578063f5b541a61461072e578063f65d901c14610750578063f99318551461077057005b8063b28a1d7e1461062e578063b37b32ae1461066e578063c1dc6b001461068e578063d547741f146106ae578063e10ea844146106ce57005b806391d14854116100e957806391d14854146105a35780639870d7fe146105c3578063a217fddf146105e3578063a694fc3a146105f8578063a89989dd1461061857005b806383d512931461051657806387a2a7381461053657806387c2a3ea146105565780638f0bc1521461058357005b80632f2ff15d1161019f57806342e92d1c1161016657806342e92d1c146104415780635e67405c1461046e5780636d70f7ae1461048e5780636eba7382146104ae5780637db32a2e146104f657005b80632f2ff15d146103875780632f926723146103a757806336568abe146103d45780633a98444d146103f45780633b9e3e091461042157005b80631865a4eb116101e35780631865a4eb146102c75780631e408dfa146102e75780632002052714610322578063248a9ca3146103425780632ab6f8db1461037257005b806301ffc9a71461021a5780630a0090971461024f5780630feff83c1461028757806316114acd146102a757005b3661021857005b005b34801561022657600080fd5b5061023a610235366004612ba6565b61079d565b60405190151581526020015b60405180910390f35b34801561025b57600080fd5b5060025461026f906001600160a01b031681565b6040516001600160a01b039091168152602001610246565b34801561029357600080fd5b506102186102a2366004612937565b6107d4565b3480156102b357600080fd5b506102186102c2366004612937565b61085e565b3480156102d357600080fd5b5060035461026f906001600160a01b031681565b3480156102f357600080fd5b50610314610302366004612937565b60066020526000908152604090205481565b604051908152602001610246565b34801561032e57600080fd5b5061021861033d366004612b61565b610a2c565b34801561034e57600080fd5b5061031461035d366004612b61565b60009081526020819052604090206001015490565b34801561037e57600080fd5b50610218610a99565b34801561039357600080fd5b506102186103a2366004612b7a565b610aa4565b3480156103b357600080fd5b506103146103c2366004612937565b60096020526000908152604090205481565b3480156103e057600080fd5b506102186103ef366004612b7a565b610acf565b34801561040057600080fd5b5061031461040f366004612937565b60076020526000908152604090205481565b34801561042d57600080fd5b5061031461043c3660046129d3565b610b49565b34801561044d57600080fd5b5061031461045c366004612937565b60056020526000908152604090205481565b34801561047a57600080fd5b50610218610489366004612a77565b610b96565b34801561049a57600080fd5b5061023a6104a9366004612937565b610eff565b3480156104ba57600080fd5b506104ce6104c9366004612937565b610f19565b604080519586526020860194909452928401919091526060830152608082015260a001610246565b34801561050257600080fd5b50610314610511366004612937565b610f74565b34801561052257600080fd5b50610218610531366004612937565b610fc8565b34801561054257600080fd5b50610314610551366004612a0c565b611049565b34801561056257600080fd5b50610314610571366004612b61565b60086020526000908152604090205481565b34801561058f57600080fd5b5061021861059e36600461297c565b611085565b3480156105af57600080fd5b5061023a6105be366004612b7a565b611092565b3480156105cf57600080fd5b506102186105de366004612937565b6110bb565b3480156105ef57600080fd5b50610314600081565b34801561060457600080fd5b50610218610613366004612b61565b6110c4565b34801561062457600080fd5b50610314600a5481565b34801561063a57600080fd5b5061066160405180604001604052806007815260200166125394d510539560ca1b81525081565b6040516102469190612d0e565b34801561067a57600080fd5b50610218610689366004612937565b6111bb565b34801561069a57600080fd5b506102186106a9366004612bd0565b611380565b3480156106ba57600080fd5b506102186106c9366004612b7a565b6116f7565b3480156106da57600080fd5b506102186106e936600461297c565b61171d565b3480156106fa57600080fd5b50610314610709366004612952565b61172a565b34801561071a57600080fd5b50610218610729366004612bd0565b611767565b34801561073a57600080fd5b50610314600080516020612fec83398151915281565b34801561075c57600080fd5b5061021861076b366004612937565b611ac1565b34801561077c57600080fd5b5061031461078b366004612937565b60046020526000908152604090205481565b60006001600160e01b03198216637965db0b60e01b14806107ce57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6107dd33610eff565b6108025760405162461bcd60e51b81526004016107f990612d21565b60405180910390fd5b6003546040516001600160a01b038084169216907f9725454688f311a9efb31cf30b914736cea0be217f90e29e7ca4618586ab7e2690600090a3600380546001600160a01b0319166001600160a01b0392909216919091179055565b61086733610eff565b6108835760405162461bcd60e51b81526004016107f990612d21565b6001600160a01b03811660009081526001602052604090205460ff16156108ff5760405162461bcd60e51b815260206004820152602a60248201527f43616e5265636c61696d546f6b656e733a20746f6b656e206973206e6f74207260448201526965636f76657261626c6560b01b60648201526084016107f9565b6001600160a01b03811661099b57604051600090339047908381818185875af1925050503d806000811461094f576040519150601f19603f3d011682016040523d82523d6000602084013e610954565b606091505b50509050806109975760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8811985a5b1959608a1b60448201526064016107f9565b5050565b6040516370a0823160e01b8152306004820152610a299033906001600160a01b038416906370a082319060240160206040518083038186803b1580156109e057600080fd5b505afa1580156109f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a189190612c05565b6001600160a01b0384169190611b0d565b50565b610a4c73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2333084611b70565b80600a6000828254610a5e9190612efd565b90915550506040518181527f777d17221091b7437a19b5bffc96cdde0b40d18e3c4b935901cd1a82625a78d29060200160405180910390a150565b610aa233611bae565b565b600082815260208190526040902060010154610ac08133611bfd565b610aca8383611c61565b505050565b6001600160a01b0381163314610b3f5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016107f9565b6109978282611ce5565b604080516001600160a01b0386166020820152908101849052606081018390526080810182905260009060a001604051602081830303815290604052805190602001209050949350505050565b60008060005b8351811015610eb6576000848281518110610bb957610bb9612fbf565b60200260200101519050610bd08160000151610f74565b15610c275760405162461bcd60e51b815260206004820152602160248201527f63616e6e6f7420736574746c65207768696c6520696e207769746864726177616044820152601b60fa1b60648201526084016107f9565b80516001600160a01b0316600090815260046020908152604090912054908201511115610c665760405162461bcd60e51b81526004016107f990612d75565b80516001600160a01b031660009081526005602052604090819020549082015111610ca35760405162461bcd60e51b81526004016107f990612df4565b80516001600160a01b0316600090815260076020526040902054606082015114610cdf5760405162461bcd60e51b81526004016107f990612e6c565b6000610d4d610d43610cf084611d4a565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b8360a00151611d6d565b905081600001516001600160a01b0316816001600160a01b031614610d845760405162461bcd60e51b81526004016107f990612daa565b6000610d9f610d95610cf085611d4a565b8460c00151611d6d565b6002549091506001600160a01b03808316911614610dcf5760405162461bcd60e51b81526004016107f990612e24565b82516001600160a01b0316600090815260066020908152604090912054908401511015610e325760208084015184516001600160a01b031660009081526006909252604090912054610e219190612f34565b610e2b9086612efd565b9450610e6a565b82516001600160a01b031660009081526006602090815260409091205490840151610e5d9190612f34565b610e679087612efd565b95505b505060408082015182516001600160a01b039081166000908152600560209081528482209390935582850151945190911681526006909152205580610eae81612f8e565b915050610b9c565b50610ec18183611e23565b60408051828152602081018490527ff5b268a3ff315cc44ccceeef86259c9e8eef81ceecb14001543809115380dd62910160405180910390a1505050565b60006107ce600080516020612fec83398151915283611092565b6001600160a01b038116600090815260046020908152604080832054600583528184205460068452828520546007909452918420548493849384938493909290610f628a610f74565b939a9299509097509550909350915050565b6001600160a01b03811660009081526006602090815260408083205460058352818420546007909352908320546008928492610fb292879290610b49565b8152602001908152602001600020549050919050565b610fd133610eff565b610fed5760405162461bcd60e51b81526004016107f990612d21565b6002546040516001600160a01b038084169216907f9a77f4a82be606b9a5cf52d398d0418e87b06091d64c494390eb0222a83652e390600090a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b60008585858585604051602001611064959493929190612cdb565b60405160208183030381529060405280519060200120905095945050505050565b610aca8383836000611ed9565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610a2981612203565b6110cd33610f74565b1561111a5760405162461bcd60e51b815260206004820181905260248201527f63616e6e6f74207374616b65207768696c6520696e207769746864726177616c60448201526064016107f9565b61113a73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2333084611b70565b3360009081526004602052604081208054839290611159908490612efd565b9091555050336000818152600760209081526040808320546004909252918290205491517f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee90926111b0928252602082015260400190565b60405180910390a250565b6001600160a01b038116600090815260076020526040812054906111de83610f74565b1161123d5760405162461bcd60e51b815260206004820152602960248201527f6d75737420696e6974696174652074696d656c6f636b656420776974686472616044820152681dd85b08199a5c9cdd60ba1b60648201526084016107f9565b61124682610f74565b42116112945760405162461bcd60e51b815260206004820152601c60248201527f7374696c6c20696e207769746864726177616c2074696d656c6f636b0000000060448201526064016107f9565b6001600160a01b03821660009081526006602090815260408083205460049092528220546112c29190612f34565b6001600160a01b03841660009081526005602090815260408083208390556006825280832083905560048252808320839055600790915281208054929350600192909190611311908490612efd565b90915550611336905073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28483611b0d565b60408051838152602081018390526001600160a01b038516917f933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafef91015b60405180910390a2505050565b80516001600160a01b031633146113f25760405162461bcd60e51b815260206004820152603060248201527f6f6e6c79207374616b65416464726573732063616e20706572666f726d20696e60448201526f1cdd185b9d081dda5d1a191c985dd85b60821b60648201526084016107f9565b80516001600160a01b03166000908152600460209081526040909120549082015111156114315760405162461bcd60e51b81526004016107f990612d75565b80516001600160a01b0316600090815260056020526040908190205490820151101561146f5760405162461bcd60e51b81526004016107f990612df4565b80516001600160a01b03166000908152600760205260409020546060820151146114ab5760405162461bcd60e51b81526004016107f990612e6c565b604080518082019091526007815266125394d510539560ca1b602091820152608082015180519101207f95f9e5276b62430bf81e5586f1eaa799db1c11b5b20e060bc0001d1d781be52f1461153b5760405162461bcd60e51b81526020600482015260166024820152751a5b98dbdc9c9958dd0819185d18481c185e5b1bd85960521b60448201526064016107f9565b600061154c610d43610cf084611d4a565b905081600001516001600160a01b0316816001600160a01b0316146115835760405162461bcd60e51b81526004016107f990612daa565b6000611594610d95610cf085611d4a565b6002549091506001600160a01b038083169116146115c45760405162461bcd60e51b81526004016107f990612e24565b82516001600160a01b0316600090815260066020908152604090912054908401516115ef9190611e23565b60208084015184516001600160a01b03166000908152600490925260408220546116199190612f34565b84516001600160a01b03908116600090815260056020908152604080832083905588518416835260068252808320839055885184168352600482528083208390558851909316825260079052908120805492935060019290919061167e908490612efd565b909155505083516116a59073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc29083611b0d565b8351606085015160408051918252602082018490526001600160a01b03909216917f933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafef910160405180910390a250505050565b6000828152602081905260409020600101546117138133611bfd565b610aca8383611ce5565b610aca8383836001611ed9565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b805161177290610f74565b6117ea5780516001600160a01b031633146117ea5760405162461bcd60e51b815260206004820152603260248201527f6f6e6c79207374616b65416464726573732063616e20737461727420746865206044820152717769746864726177616c2070726f6365737360701b60648201526084016107f9565b80516001600160a01b03166000908152600460209081526040909120549082015111156118295760405162461bcd60e51b81526004016107f990612d75565b80516001600160a01b031660009081526005602052604090819020549082015110156118675760405162461bcd60e51b81526004016107f990612df4565b80516001600160a01b03166000908152600760205260409020546060820151146118a35760405162461bcd60e51b81526004016107f990612e6c565b80516001600160a01b03163314806118c557506002546001600160a01b031633145b6119275760405162461bcd60e51b815260206004820152602d60248201527f6f6e6c792063616c6c61626c65206279207374616b654164646472657373206f60448201526c391031b7b7b93234b730ba37b960991b60648201526084016107f9565b6000611938610d43610cf084611d4a565b905081600001516001600160a01b0316816001600160a01b03161461196f5760405162461bcd60e51b81526004016107f990612daa565b6000611980610d95610cf085611d4a565b6002549091506001600160a01b038083169116146119b05760405162461bcd60e51b81526004016107f990612e24565b82516001600160a01b0316600090815260066020908152604090912054908401516119db9190611e23565b60408084015184516001600160a01b03908116600090815260056020908152848220939093558287015187519092168152600690925291902055611a224262093a80612efd565b60086000611a428660000151876020015188604001518960600151610b49565b81526020019081526020016000208190555082600001516001600160a01b03167f530f971e91a16cdf0c6acd4e66985535ca021e6a372d18d3c81ae1f8d32f0f398460200151856040015186606001514262093a80611aa19190612efd565b604080519485526020850193909352918301526060820152608001611373565b611aca33610eff565b611ae65760405162461bcd60e51b81526004016107f990612d21565b6001600160a01b03166000908152600160208190526040909120805460ff19169091179055565b6040516001600160a01b038316602482015260448101829052610aca90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612252565b6040516001600160a01b0380851660248301528316604482015260648101829052611ba89085906323b872dd60e01b90608401611b39565b50505050565b611bc6600080516020612fec83398151915282610acf565b6040516001600160a01b038216907f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d90600090a250565b611c078282611092565b61099757611c1f816001600160a01b03166014612324565b611c2a836020612324565b604051602001611c3b929190612c66565b60408051601f198184030181529082905262461bcd60e51b82526107f991600401612d0e565b611c6b8282611092565b610997576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055611ca13390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b611cef8282611092565b15610997576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006107ce82600001518360200151846040015185606001518660800151611049565b600080600080845160411415611d975750505060208201516040830151606084015160001a611e0d565b845160401415611dc55750505060408201516020830151906001600160ff1b0381169060ff1c601b01611e0d565b60405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016107f9565b611e19868285856124c7565b9695505050505050565b81811015611eb5576000611e378284612f34565b905080600a541015611e975760405162461bcd60e51b815260206004820152602360248201527f6e6f7420656e6f75676820636c61696d61626c65205745544820746f207265666044820152621d5b9960ea1b60648201526084016107f9565b80600a6000828254611ea99190612f34565b90915550610997915050565b611ebf8282612f34565b600a6000828254611ed09190612efd565b90915550505050565b6001600160a01b0384166000908152600960205260409020548311611f335760405162461bcd60e51b815260206004820152601060248201526f6e6f7468696e6720746f20636c61696d60801b60448201526064016107f9565b6000611f4b611f45610cf0878761172a565b84611d6d565b6003549091506001600160a01b03808316911614611fd15760405162461bcd60e51b815260206004820152603960248201527f7265636f7665726564436c61696d47656e657261746f7241646472657373206960448201527f73206e6f7420746865206163636f756e74206d616e616765720000000000000060648201526084016107f9565b6001600160a01b038516600090815260096020526040812054611ff49086612f34565b9050600a548111156120595760405162461bcd60e51b815260206004820152602860248201527f636c61696d20616d6f756e7420657863656564732062616c616e6365206f6e2060448201526718dbdb9d1c9858dd60c21b60648201526084016107f9565b6001600160a01b0386166000908152600960205260408120869055600a8054839290612086908490612f34565b9091555050821561219957604051632e1a7d4d60e01b81526004810182905273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290632e1a7d4d90602401600060405180830381600087803b1580156120de57600080fd5b505af11580156120f2573d6000803e3d6000fd5b50505050600080876001600160a01b03168360405160006040518083038185875af1925050503d8060008114612144576040519150601f19603f3d011682016040523d82523d6000602084013e612149565b606091505b5091509150816121925760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a379039b2b7321022ba3432b960611b60448201526064016107f9565b50506121b8565b6121b873c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28783611b0d565b856001600160a01b03167fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a826040516121f391815260200190565b60405180910390a2505050505050565b61221b600080516020612fec83398151915282610aa4565b6040516001600160a01b038216907fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d90600090a250565b60006122a7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166126709092919063ffffffff16565b805190915015610aca57808060200190518101906122c59190612b3f565b610aca5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f9565b60606000612333836002612f15565b61233e906002612efd565b67ffffffffffffffff81111561235657612356612fd5565b6040519080825280601f01601f191660200182016040528015612380576020820181803683370190505b509050600360fc1b8160008151811061239b5761239b612fbf565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106123ca576123ca612fbf565b60200101906001600160f81b031916908160001a90535060006123ee846002612f15565b6123f9906001612efd565b90505b6001811115612471576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061242d5761242d612fbf565b1a60f81b82828151811061244357612443612fbf565b60200101906001600160f81b031916908160001a90535060049490941c9361246a81612f77565b90506123fc565b5083156124c05760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016107f9565b9392505050565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156125445760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016107f9565b8360ff16601b148061255957508360ff16601c145b6125b05760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016107f9565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015612604573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166126675760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016107f9565b95945050505050565b606061267f8484600085612687565b949350505050565b6060824710156126e85760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016107f9565b843b6127365760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f9565b600080866001600160a01b031685876040516127529190612c4a565b60006040518083038185875af1925050503d806000811461278f576040519150601f19603f3d011682016040523d82523d6000602084013e612794565b606091505b50915091506127a48282866127af565b979650505050505050565b606083156127be5750816124c0565b8251156127ce5782518084602001fd5b8160405162461bcd60e51b81526004016107f99190612d0e565b80356001600160a01b03811681146127ff57600080fd5b919050565b600082601f83011261281557600080fd5b813567ffffffffffffffff81111561282f5761282f612fd5565b612842601f8201601f1916602001612ecc565b81815284602083860101111561285757600080fd5b816020850160208301376000918101602001919091529392505050565b600060e0828403121561288657600080fd5b61288e612ea3565b9050612899826127e8565b8152602082013560208201526040820135604082015260608201356060820152608082013567ffffffffffffffff808211156128d457600080fd5b6128e085838601612804565b608084015260a08401359150808211156128f957600080fd5b61290585838601612804565b60a084015260c084013591508082111561291e57600080fd5b5061292b84828501612804565b60c08301525092915050565b60006020828403121561294957600080fd5b6124c0826127e8565b6000806040838503121561296557600080fd5b61296e836127e8565b946020939093013593505050565b60008060006060848603121561299157600080fd5b61299a846127e8565b925060208401359150604084013567ffffffffffffffff8111156129bd57600080fd5b6129c986828701612804565b9150509250925092565b600080600080608085870312156129e957600080fd5b6129f2856127e8565b966020860135965060408601359560600135945092505050565b600080600080600060a08688031215612a2457600080fd5b612a2d866127e8565b9450602086013593506040860135925060608601359150608086013567ffffffffffffffff811115612a5e57600080fd5b612a6a88828901612804565b9150509295509295909350565b60006020808385031215612a8a57600080fd5b823567ffffffffffffffff80821115612aa257600080fd5b818501915085601f830112612ab657600080fd5b813581811115612ac857612ac8612fd5565b8060051b612ad7858201612ecc565b8281528581019085870183870188018b1015612af257600080fd5b60009350835b85811015612b2f57813587811115612b0e578586fd5b612b1c8d8b838c0101612874565b8552509288019290880190600101612af8565b50909a9950505050505050505050565b600060208284031215612b5157600080fd5b815180151581146124c057600080fd5b600060208284031215612b7357600080fd5b5035919050565b60008060408385031215612b8d57600080fd5b82359150612b9d602084016127e8565b90509250929050565b600060208284031215612bb857600080fd5b81356001600160e01b0319811681146124c057600080fd5b600060208284031215612be257600080fd5b813567ffffffffffffffff811115612bf957600080fd5b61267f84828501612874565b600060208284031215612c1757600080fd5b5051919050565b60008151808452612c36816020860160208601612f4b565b601f01601f19169290920160200192915050565b60008251612c5c818460208701612f4b565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351612c9e816017850160208801612f4b565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351612ccf816028840160208801612f4b565b01602801949350505050565b60018060a01b038616815284602082015283604082015282606082015260a0608082015260006127a460a0830184612c1e565b6020815260006124c06020830184612c1e565b60208082526034908201527f4f70657261746f72526f6c653a2063616c6c657220646f6573206e6f74206861604082015273766520746865204f70657261746f7220726f6c6560601b606082015260800190565b6020808252818101527f63616e6e6f74207370656e64206d6f7265207468616e206973207374616b6564604082015260600190565b6020808252602a908201527f7265636f76657265642061646472657373206973206e6f7420746865207374616040820152696b65206164647265737360b01b606082015260800190565b6020808252601690820152751cdd185ad9481b9bdb98d9481a5cc81d1bdbc81bdb1960521b604082015260600190565b60208082526028908201527f7265636f76657265642061646472657373206973206e6f742074686520636f6f604082015267393234b730ba37b960c11b606082015260800190565b60208082526017908201527f696e636f7272656374206368616e6e656c206e6f6e6365000000000000000000604082015260600190565b60405160e0810167ffffffffffffffff81118282101715612ec657612ec6612fd5565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715612ef557612ef5612fd5565b604052919050565b60008219821115612f1057612f10612fa9565b500190565b6000816000190483118215151615612f2f57612f2f612fa9565b500290565b600082821015612f4657612f46612fa9565b500390565b60005b83811015612f66578181015183820152602001612f4e565b83811115611ba85750506000910152565b600081612f8657612f86612fa9565b506000190190565b6000600019821415612fa257612fa2612fa9565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfe97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929a2646970667358221220e21c751c1383342f9175b01b03f5adeb8d72a8c7b3d7b62fd39a11b744e7b6db64736f6c63430008060033

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

0000000000000000000000003a8f4dff08c45814d730b5f6b268ad8378bebb360000000000000000000000003a8f4dff08c45814d730b5f6b268ad8378bebb36

-----Decoded View---------------
Arg [0] : _coordinator (address): 0x3a8f4dFF08c45814d730b5f6B268aD8378BebB36
Arg [1] : _claimGenerator (address): 0x3a8f4dFF08c45814d730b5f6B268aD8378BebB36

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000003a8f4dff08c45814d730b5f6b268ad8378bebb36
Arg [1] : 0000000000000000000000003a8f4dff08c45814d730b5f6b268ad8378bebb36


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.