MATIC Price: $0.98 (-3.63%)
Gas: 117 GWei
 

Overview

MATIC Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo2,854.688074264130586129 MATIC

MATIC Value

$2,811.16 (@ $0.98/MATIC)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Relay Call552071832024-03-29 8:08:265 mins ago1711699706IN
0x6C28AfC1...C9e7d750d
0 MATIC0.04157806143.88913509
Register Relay S...552071432024-03-29 8:07:027 mins ago1711699622IN
0x6C28AfC1...C9e7d750d
0 MATIC0.00489928108.86818758
Register Relay S...552070052024-03-29 8:02:0812 mins ago1711699328IN
0x6C28AfC1...C9e7d750d
0 MATIC0.00498243110.59778949
Relay Call552070042024-03-29 8:02:0612 mins ago1711699326IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0480304159.40526478
Relay Call552067782024-03-29 7:53:3320 mins ago1711698813IN
0x6C28AfC1...C9e7d750d
0 MATIC0.04720212183.65513811
Register Relay S...552064682024-03-29 7:42:0132 mins ago1711698121IN
0x6C28AfC1...C9e7d750d
0 MATIC0.00573544128.87475777
Register Relay S...552061402024-03-29 7:29:5144 mins ago1711697391IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0041085491.29685169
Register Relay S...552059992024-03-29 7:24:1950 mins ago1711697059IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0040901290.79088834
Register Relay S...552054642024-03-29 7:05:211 hr ago1711695921IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0046313104.06499062
Relay Call552053652024-03-29 7:01:191 hr ago1711695679IN
0x6C28AfC1...C9e7d750d
0 MATIC0.01989393175.18278043
Relay Call552053632024-03-29 7:01:151 hr ago1711695675IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0527626175.18278043
Relay Call552052972024-03-29 6:58:551 hr ago1711695535IN
0x6C28AfC1...C9e7d750d
0 MATIC0.07771712180.31523358
Register Relay S...552050652024-03-29 6:50:091 hr ago1711695009IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0037962684.35765371
Register Relay S...552049922024-03-29 6:47:351 hr ago1711694855IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0037814883.9397642
Register Relay S...552044552024-03-29 6:27:591 hr ago1711693679IN
0x6C28AfC1...C9e7d750d
0 MATIC0.00496157111.48610522
Register Relay S...552040562024-03-29 6:13:172 hrs ago1711692797IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0065441145.41817593
Register Relay S...552039842024-03-29 6:10:452 hrs ago1711692645IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0055513123.22542577
Relay Call552036332024-03-29 5:57:462 hrs ago1711691866IN
0x6C28AfC1...C9e7d750d
0 MATIC0.06259965112.33049666
Register Relay S...552034482024-03-29 5:50:402 hrs ago1711691440IN
0x6C28AfC1...C9e7d750d
0 MATIC0.00467943105.14642667
Register Relay S...552030492024-03-29 5:36:302 hrs ago1711690590IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0031769870.596475
Register Relay S...552029722024-03-29 5:33:142 hrs ago1711690394IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0027697361.48137128
Relay Call552028732024-03-29 5:29:442 hrs ago1711690184IN
0x6C28AfC1...C9e7d750d
0 MATIC0.03597947131.24729284
Relay Call552026382024-03-29 5:20:512 hrs ago1711689651IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0171775875.02505485
Relay Call552024902024-03-29 5:15:352 hrs ago1711689335IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0129657672.27376758
Register Relay S...552024352024-03-29 5:13:393 hrs ago1711689219IN
0x6C28AfC1...C9e7d750d
0 MATIC0.0032783373.66377611
View all transactions

Latest 25 internal transactions (View All)

Parent Txn Hash Block From To Value
552070042024-03-29 8:02:0612 mins ago1711699326
0x6C28AfC1...C9e7d750d
0.13549447 MATIC
552053632024-03-29 7:01:151 hr ago1711695675
0x6C28AfC1...C9e7d750d
0.14890536 MATIC
552036332024-03-29 5:57:462 hrs ago1711691866
0x6C28AfC1...C9e7d750d
0.09548092 MATIC
551988062024-03-29 2:55:285 hrs ago1711680928
0x6C28AfC1...C9e7d750d
0.07309183 MATIC
551987882024-03-29 2:54:505 hrs ago1711680890
0x6C28AfC1...C9e7d750d
0.11096742 MATIC
551983712024-03-29 2:38:365 hrs ago1711679916
0x6C28AfC1...C9e7d750d
0.07266621 MATIC
551965932024-03-29 1:28:086 hrs ago1711675688
0x6C28AfC1...C9e7d750d
0.07063171 MATIC
551965762024-03-29 1:27:286 hrs ago1711675648
0x6C28AfC1...C9e7d750d
0.07733401 MATIC
551965492024-03-29 1:26:106 hrs ago1711675570
0x6C28AfC1...C9e7d750d
0.07263872 MATIC
551961042024-03-29 1:09:027 hrs ago1711674542
0x6C28AfC1...C9e7d750d
0.10792056 MATIC
551944232024-03-29 0:02:488 hrs ago1711670568
0x6C28AfC1...C9e7d750d
0.0407636 MATIC
551914772024-03-28 22:11:4410 hrs ago1711663904
0x6C28AfC1...C9e7d750d
0.06040407 MATIC
551914382024-03-28 22:10:2010 hrs ago1711663820
0x6C28AfC1...C9e7d750d
0.08098534 MATIC
551868712024-03-28 19:19:0812 hrs ago1711653548
0x6C28AfC1...C9e7d750d
0.04784982 MATIC
551856202024-03-28 18:31:1613 hrs ago1711650676
0x6C28AfC1...C9e7d750d
0.08421353 MATIC
551855132024-03-28 18:26:5213 hrs ago1711650412
0x6C28AfC1...C9e7d750d
0.09996321 MATIC
551847732024-03-28 17:59:0214 hrs ago1711648742
0x6C28AfC1...C9e7d750d
0.12962597 MATIC
551843882024-03-28 17:43:4014 hrs ago1711647820
0x6C28AfC1...C9e7d750d
0.08617346 MATIC
551843212024-03-28 17:41:1814 hrs ago1711647678
0x6C28AfC1...C9e7d750d
0.13993662 MATIC
551838432024-03-28 17:23:1614 hrs ago1711646596
0x6C28AfC1...C9e7d750d
0.14001361 MATIC
551792072024-03-28 14:28:1417 hrs ago1711636094
0x6C28AfC1...C9e7d750d
0.19482712 MATIC
551782822024-03-28 13:53:2018 hrs ago1711634000
0x6C28AfC1...C9e7d750d
100 MATIC
551770002024-03-28 13:05:1619 hrs ago1711631116
0x6C28AfC1...C9e7d750d
0.19162307 MATIC
551758442024-03-28 12:22:1019 hrs ago1711628530
0x6C28AfC1...C9e7d750d
0.13857971 MATIC
551758172024-03-28 12:21:1219 hrs ago1711628472
0x6C28AfC1...C9e7d750d
0.10133574 MATIC
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RelayHub

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, None license

Contract Source Code (Solidity)

/**
 *Submitted for verification at polygonscan.com on 2021-06-30
*/

// File: src/utils/MinLibBytes.sol

// SPDX-License-Identifier: MIT
// minimal bytes manipulation required by GSN
// a minimal subset from 0x/LibBytes
/* solhint-disable no-inline-assembly */
pragma solidity >=0.7.6;

library MinLibBytes {

    //truncate the given parameter (in-place) if its length is above the given maximum length
    // do nothing otherwise.
    //NOTE: solidity warns unless the method is marked "pure", but it DOES modify its parameter.
    function truncateInPlace(bytes memory data, uint256 maxlen) internal pure {
        if (data.length > maxlen) {
            assembly { mstore(data, maxlen) }
        }
    }

    /// @dev Reads an address from a position in a byte array.
    /// @param b Byte array containing an address.
    /// @param index Index in byte array of address.
    /// @return result address from byte array.
    function readAddress(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (address result)
    {
        require (b.length >= index + 20, "readAddress: data too short");

        // Add offset to index:
        // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
        // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
        index += 20;

        // Read address from array memory
        assembly {
            // 1. Add index to address of bytes array
            // 2. Load 32-byte word from memory
            // 3. Apply 20-byte mask to obtain address
            result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
        }
        return result;
    }

    function readBytes32(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (bytes32 result)
    {
        require(b.length >= index + 32, "readBytes32: data too short" );

        // Read the bytes32 from array memory
        assembly {
            result := mload(add(b, add(index,32)))
        }
        return result;
    }

    /// @dev Reads a uint256 value from a position in a byte array.
    /// @param b Byte array containing a uint256 value.
    /// @param index Index in byte array of uint256 value.
    /// @return result uint256 value from byte array.
    function readUint256(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (uint256 result)
    {
        result = uint256(readBytes32(b, index));
        return result;
    }

    function readBytes4(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (bytes4 result)
    {
        require(b.length >= index + 4, "readBytes4: data too short");

        // Read the bytes4 from array memory
        assembly {
            result := mload(add(b, add(index,32)))
            // Solidity does not require us to clean the trailing bytes.
            // We do it anyway
            result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
        }
        return result;
    }
}

// File: @openzeppelin/contracts/math/SafeMath.sol

// (SPDX)-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

// File: @openzeppelin/contracts/utils/Context.sol

// (SPDX)-License-Identifier: MIT

pragma solidity >=0.6.0 <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 GSN 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 payable) {
        return msg.sender;
    }

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

// File: @openzeppelin/contracts/access/Ownable.sol

// (SPDX)-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

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

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

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

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

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

// File: src/utils/GsnUtils.sol

/* solhint-disable no-inline-assembly */
// (SPDX)-License-Identifier:MIT
pragma solidity >=0.7.6;


library GsnUtils {

    /**
     * extract method sig from encoded function call
     */
    function getMethodSig(bytes memory msgData) internal pure returns (bytes4) {
        return MinLibBytes.readBytes4(msgData, 0);
    }

    /**
     * extract parameter from encoded-function block.
     * see: https://solidity.readthedocs.io/en/develop/abi-spec.html#formal-specification-of-the-encoding
     * the return value should be casted to the right type (uintXXX/bytesXXX/address/bool/enum)
     */
    function getParam(bytes memory msgData, uint index) internal pure returns (uint) {
        return MinLibBytes.readUint256(msgData, 4 + index * 32);
    }

    //re-throw revert with the same revert data.
    function revertWithData(bytes memory data) internal pure {
        assembly {
            revert(add(data,32), mload(data))
        }
    }

}

// File: src/forwarder/IForwarder.sol

// (SPDX)-License-Identifier:MIT
pragma solidity >=0.7.6;
pragma abicoder v2;

interface IForwarder {

    struct ForwardRequest {
        address from;
        address to;
        uint256 value;
        uint256 gas;
        uint256 nonce;
        bytes data;
        uint256 validUntil;
    }

    event DomainRegistered(bytes32 indexed domainSeparator, bytes domainValue);

    event RequestTypeRegistered(bytes32 indexed typeHash, string typeStr);

    function getNonce(address from)
    external view
    returns(uint256);

    /**
     * verify the transaction would execute.
     * validate the signature and the nonce of the request.
     * revert if either signature or nonce are incorrect.
     * also revert if domainSeparator or requestTypeHash are not registered.
     */
    function verify(
        ForwardRequest calldata forwardRequest,
        bytes32 domainSeparator,
        bytes32 requestTypeHash,
        bytes calldata suffixData,
        bytes calldata signature
    ) external view;

    /**
     * execute a transaction
     * @param forwardRequest - all transaction parameters
     * @param domainSeparator - domain used when signing this request
     * @param requestTypeHash - request type used when signing this request.
     * @param suffixData - the extension data used when signing this request.
     * @param signature - signature to validate.
     *
     * the transaction is verified, and then executed.
     * the success and ret of "call" are returned.
     * This method would revert only verification errors. target errors
     * are reported using the returned "success" and ret string
     */
    function execute(
        ForwardRequest calldata forwardRequest,
        bytes32 domainSeparator,
        bytes32 requestTypeHash,
        bytes calldata suffixData,
        bytes calldata signature
    )
    external payable
    returns (bool success, bytes memory ret);

    /**
     * Register a new Request typehash.
     * @param typeName - the name of the request type.
     * @param typeSuffix - any extra data after the generic params.
     *  (must add at least one param. The generic ForwardRequest type is always registered by the constructor)
     */
    function registerRequestType(string calldata typeName, string calldata typeSuffix) external;

    /**
     * Register a new domain separator.
     * The domain separator must have the following fields: name,version,chainId, verifyingContract.
     * the chainId is the current network's chainId, and the verifyingContract is this forwarder.
     * This method is given the domain name and version to create and register the domain separator value.
     * @param name the domain's display name
     * @param version the domain/protocol version
     */
    function registerDomainSeparator(string calldata name, string calldata version) external;
}

// File: src/utils/GsnTypes.sol

// (SPDX)-License-Identifier:MIT
pragma solidity >=0.7.6;


interface GsnTypes {
    /// @notice gasPrice, pctRelayFee and baseRelayFee must be validated inside of the paymaster's preRelayedCall in order not to overpay
    struct RelayData {
        uint256 gasPrice;
        uint256 pctRelayFee;
        uint256 baseRelayFee;
        address relayWorker;
        address paymaster;
        address forwarder;
        bytes paymasterData;
        uint256 clientId;
    }

    //note: must start with the ForwardRequest to be an extension of the generic forwarder
    struct RelayRequest {
        IForwarder.ForwardRequest request;
        RelayData relayData;
    }
}

// File: src/interfaces/IRelayRecipient.sol

// (SPDX)-License-Identifier:MIT
pragma solidity >=0.7.6;

/**
 * a contract must implement this interface in order to support relayed transaction.
 * It is better to inherit the BaseRelayRecipient as its implementation.
 */
abstract contract IRelayRecipient {

    /**
     * return if the forwarder is trusted to forward relayed transactions to us.
     * the forwarder is required to verify the sender's signature, and verify
     * the call is not a replay.
     */
    function isTrustedForwarder(address forwarder) public virtual view returns(bool);

    /**
     * return the sender of this call.
     * if the call came through our trusted forwarder, then the real sender is appended as the last 20 bytes
     * of the msg.data.
     * otherwise, return `msg.sender`
     * should be used in the contract anywhere instead of msg.sender
     */
    function _msgSender() internal virtual view returns (address payable);

    /**
     * return the msg.data of this call.
     * if the call came through our trusted forwarder, then the real sender was appended as the last 20 bytes
     * of the msg.data - so this method will strip those 20 bytes off.
     * otherwise (if the call was made directly and not through the forwarder), return `msg.data`
     * should be used in the contract instead of msg.data, where this difference matters.
     */
    function _msgData() internal virtual view returns (bytes memory);

    function versionRecipient() external virtual view returns (string memory);
}

// File: src/utils/GsnEip712Library.sol

// (SPDX)-License-Identifier:MIT
pragma solidity >=0.7.6;
// pragma abicoder v2;





/**
 * Bridge Library to map GSN RelayRequest into a call of a Forwarder
 */
library GsnEip712Library {
    // maximum length of return value/revert reason for 'execute' method. Will truncate result if exceeded.
    uint256 private constant MAX_RETURN_SIZE = 1024;

    //copied from Forwarder (can't reference string constants even from another library)
    string public constant GENERIC_PARAMS = "address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data,uint256 validUntil";

    bytes public constant RELAYDATA_TYPE = "RelayData(uint256 gasPrice,uint256 pctRelayFee,uint256 baseRelayFee,address relayWorker,address paymaster,address forwarder,bytes paymasterData,uint256 clientId)";

    string public constant RELAY_REQUEST_NAME = "RelayRequest";
    string public constant RELAY_REQUEST_SUFFIX = string(abi.encodePacked("RelayData relayData)", RELAYDATA_TYPE));

    bytes public constant RELAY_REQUEST_TYPE = abi.encodePacked(
        RELAY_REQUEST_NAME,"(",GENERIC_PARAMS,",", RELAY_REQUEST_SUFFIX);

    bytes32 public constant RELAYDATA_TYPEHASH = keccak256(RELAYDATA_TYPE);
    bytes32 public constant RELAY_REQUEST_TYPEHASH = keccak256(RELAY_REQUEST_TYPE);


    struct EIP712Domain {
        string name;
        string version;
        uint256 chainId;
        address verifyingContract;
    }

    bytes32 public constant EIP712DOMAIN_TYPEHASH = keccak256(
        "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
    );

    function splitRequest(
        GsnTypes.RelayRequest calldata req
    )
    internal
    pure
    returns (
        bytes memory suffixData
    ) {
        suffixData = abi.encode(
            hashRelayData(req.relayData));
    }

    //verify that the recipient trusts the given forwarder
    // MUST be called by paymaster
    function verifyForwarderTrusted(GsnTypes.RelayRequest calldata relayRequest) internal view {
        (bool success, bytes memory ret) = relayRequest.request.to.staticcall(
            abi.encodeWithSelector(
                IRelayRecipient.isTrustedForwarder.selector, relayRequest.relayData.forwarder
            )
        );
        require(success, "isTrustedForwarder: reverted");
        require(ret.length == 32, "isTrustedForwarder: bad response");
        require(abi.decode(ret, (bool)), "invalid forwarder for recipient");
    }

    function verifySignature(GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature) internal view {
        (bytes memory suffixData) = splitRequest(relayRequest);
        bytes32 _domainSeparator = domainSeparator(relayRequest.relayData.forwarder);
        IForwarder forwarder = IForwarder(payable(relayRequest.relayData.forwarder));
        forwarder.verify(relayRequest.request, _domainSeparator, RELAY_REQUEST_TYPEHASH, suffixData, signature);
    }

    function verify(GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature) internal view {
        verifyForwarderTrusted(relayRequest);
        verifySignature(relayRequest, signature);
    }

    function execute(GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature) internal returns (bool forwarderSuccess, bool callSuccess, bytes memory ret) {
        (bytes memory suffixData) = splitRequest(relayRequest);
        bytes32 _domainSeparator = domainSeparator(relayRequest.relayData.forwarder);
        /* solhint-disable-next-line avoid-low-level-calls */
        (forwarderSuccess, ret) = relayRequest.relayData.forwarder.call(
            abi.encodeWithSelector(IForwarder.execute.selector,
            relayRequest.request, _domainSeparator, RELAY_REQUEST_TYPEHASH, suffixData, signature
        ));
        if ( forwarderSuccess ) {

          //decode return value of execute:
          (callSuccess, ret) = abi.decode(ret, (bool, bytes));
        }
        truncateInPlace(ret);
    }

    //truncate the given parameter (in-place) if its length is above the given maximum length
    // do nothing otherwise.
    //NOTE: solidity warns unless the method is marked "pure", but it DOES modify its parameter.
    function truncateInPlace(bytes memory data) internal pure {
        MinLibBytes.truncateInPlace(data, MAX_RETURN_SIZE);
    }

    function domainSeparator(address forwarder) internal pure returns (bytes32) {
        return hashDomain(EIP712Domain({
            name : "GSN Relayed Transaction",
            version : "2",
            chainId : getChainID(),
            verifyingContract : forwarder
            }));
    }

    function getChainID() internal pure returns (uint256 id) {
        /* solhint-disable no-inline-assembly */
        assembly {
            id := chainid()
        }
    }

    function hashDomain(EIP712Domain memory req) internal pure returns (bytes32) {
        return keccak256(abi.encode(
                EIP712DOMAIN_TYPEHASH,
                keccak256(bytes(req.name)),
                keccak256(bytes(req.version)),
                req.chainId,
                req.verifyingContract));
    }

    function hashRelayData(GsnTypes.RelayData calldata req) internal pure returns (bytes32) {
        return keccak256(abi.encode(
                RELAYDATA_TYPEHASH,
                req.gasPrice,
                req.pctRelayFee,
                req.baseRelayFee,
                req.relayWorker,
                req.paymaster,
                req.forwarder,
                keccak256(req.paymasterData),
                req.clientId
            ));
    }
}

// File: src/utils/RelayHubValidator.sol

// (SPDX)-License-Identifier:MIT
pragma solidity ^0.7.5;
// pragma abicoder v2;


library RelayHubValidator {

    // validate that encoded relayCall is properly packed without any extra bytes
    function verifyTransactionPacking(
        GsnTypes.RelayRequest calldata relayRequest,
        bytes calldata signature,
        bytes calldata approvalData
    ) internal pure {
        // abicoder v2: https://docs.soliditylang.org/en/latest/abi-spec.html
        // each static param/member is 1 word
        // struct (with dynamic members) has offset to struct which is 1 word
        // dynamic member is 1 word offset to actual value, which is 1-word length and ceil(length/32) words for data
        // relayCall has 5 method params,
        // relayRequest: 2 members
        // relayData 8 members
        // ForwardRequest: 7 members
        // total 22 32-byte words if all dynamic params are zero-length.
        uint expectedMsgDataLen = 4 + 22 * 32 +
            dynamicParamSize(signature) +
            dynamicParamSize(approvalData) +
            dynamicParamSize(relayRequest.request.data) +
            dynamicParamSize(relayRequest.relayData.paymasterData);
        require(signature.length <= 65, "invalid signature length");
        require(expectedMsgDataLen == msg.data.length, "extra msg.data bytes" );
    }

    // helper method for verifyTransactionPacking:
    // size (in bytes) of the given "bytes" parameter. size include the length (32-byte word),
    // and actual data size, rounded up to full 32-byte words
    function dynamicParamSize(bytes calldata buf) internal pure returns (uint) {
        return 32 + ((buf.length + 31) & uint(~31));
    }
}

// File: src/interfaces/IStakeManager.sol

// (SPDX)-License-Identifier:MIT
pragma solidity >=0.7.6;
// pragma abicoder v2;


interface IStakeManager {

    /// Emitted when a stake or unstakeDelay are initialized or increased
    event StakeAdded(
        address indexed relayManager,
        address indexed owner,
        uint256 stake,
        uint256 unstakeDelay
    );

    /// Emitted once a stake is scheduled for withdrawal
    event StakeUnlocked(
        address indexed relayManager,
        address indexed owner,
        uint256 withdrawBlock
    );

    /// Emitted when owner withdraws relayManager funds
    event StakeWithdrawn(
        address indexed relayManager,
        address indexed owner,
        uint256 amount
    );

    /// Emitted when an authorized Relay Hub penalizes a relayManager
    event StakePenalized(
        address indexed relayManager,
        address indexed beneficiary,
        uint256 reward
    );

    event HubAuthorized(
        address indexed relayManager,
        address indexed relayHub
    );

    event HubUnauthorized(
        address indexed relayManager,
        address indexed relayHub,
        uint256 removalBlock
    );

    event OwnerSet(
        address indexed relayManager,
        address indexed owner
    );

    /// @param stake - amount of ether staked for this relay
    /// @param unstakeDelay - number of blocks to elapse before the owner can retrieve the stake after calling 'unlock'
    /// @param withdrawBlock - first block number 'withdraw' will be callable, or zero if the unlock has not been called
    /// @param owner - address that receives revenue and manages relayManager's stake
    struct StakeInfo {
        uint256 stake;
        uint256 unstakeDelay;
        uint256 withdrawBlock;
        address payable owner;
    }

    struct RelayHubInfo {
        uint256 removalBlock;
    }

    /// Set the owner of a Relay Manager. Called only by the RelayManager itself.
    /// Note that owners cannot transfer ownership - if the entry already exists, reverts.
    /// @param owner - owner of the relay (as configured off-chain)
    function setRelayManagerOwner(address payable owner) external;

    /// Only the owner can call this function. If the entry does not exist, reverts.
    /// @param relayManager - address that represents a stake entry and controls relay registrations on relay hubs
    /// @param unstakeDelay - number of blocks to elapse before the owner can retrieve the stake after calling 'unlock'
    function stakeForRelayManager(address relayManager, uint256 unstakeDelay) external payable;

    function unlockStake(address relayManager) external;

    function withdrawStake(address relayManager) external;

    function authorizeHubByOwner(address relayManager, address relayHub) external;

    function authorizeHubByManager(address relayHub) external;

    function unauthorizeHubByOwner(address relayManager, address relayHub) external;

    function unauthorizeHubByManager(address relayHub) external;

    function isRelayManagerStaked(address relayManager, address relayHub, uint256 minAmount, uint256 minUnstakeDelay)
    external
    view
    returns (bool);

    /// Slash the stake of the relay relayManager. In order to prevent stake kidnapping, burns half of stake on the way.
    /// @param relayManager - entry to penalize
    /// @param beneficiary - address that receives half of the penalty amount
    /// @param amount - amount to withdraw from stake
    function penalizeRelayManager(address relayManager, address payable beneficiary, uint256 amount) external;

    function getStakeInfo(address relayManager) external view returns (StakeInfo memory stakeInfo);

    function maxUnstakeDelay() external view returns (uint256);

    function versionSM() external view returns (string memory);
}

// File: src/interfaces/IRelayHub.sol

// (SPDX)-License-Identifier:MIT
pragma solidity >=0.7.6;
// pragma abicoder v2;



interface IRelayHub {
    struct RelayHubConfig {
        // maximum number of worker accounts allowed per manager
        uint256 maxWorkerCount;
        // Gas set aside for all relayCall() instructions to prevent unexpected out-of-gas exceptions
        uint256 gasReserve;
        // Gas overhead to calculate gasUseWithoutPost
        uint256 postOverhead;
        // Gas cost of all relayCall() instructions after actual 'calculateCharge()'
        // Assume that relay has non-zero balance (costs 15'000 more otherwise).
        uint256 gasOverhead;
        // Maximum funds that can be deposited at once. Prevents user error by disallowing large deposits.
        uint256 maximumRecipientDeposit;
        // Minimum unstake delay blocks of a relay manager's stake on the StakeManager
        uint256 minimumUnstakeDelay;
        // Minimum stake a relay can have. An attack on the network will never cost less than half this value.
        uint256 minimumStake;
        // relayCall()'s msg.data upper bound gas cost per byte
        uint256 dataGasCostPerByte;
        // relayCalls() minimal gas overhead when calculating cost of putting tx on chain.
        uint256 externalCallDataCostOverhead;
    }

    event RelayHubConfigured(RelayHubConfig config);

    /// Emitted when a relay server registers or updates its details
    /// Looking at these events lets a client discover relay servers
    event RelayServerRegistered(
        address indexed relayManager,
        uint256 baseRelayFee,
        uint256 pctRelayFee,
        string relayUrl
    );

    /// Emitted when relays are added by a relayManager
    event RelayWorkersAdded(
        address indexed relayManager,
        address[] newRelayWorkers,
        uint256 workersCount
    );

    /// Emitted when an account withdraws funds from RelayHub.
    event Withdrawn(
        address indexed account,
        address indexed dest,
        uint256 amount
    );

    /// Emitted when depositFor is called, including the amount and account that was funded.
    event Deposited(
        address indexed paymaster,
        address indexed from,
        uint256 amount
    );

    /// Emitted when an attempt to relay a call fails and Paymaster does not accept the transaction.
    /// The actual relayed call was not executed, and the recipient not charged.
    /// @param reason contains a revert reason returned from preRelayedCall or forwarder.
    event TransactionRejectedByPaymaster(
        address indexed relayManager,
        address indexed paymaster,
        address indexed from,
        address to,
        address relayWorker,
        bytes4 selector,
        uint256 innerGasUsed,
        bytes reason
    );

    /// Emitted when a transaction is relayed. Note that the actual encoded function might be reverted: this will be
    /// indicated in the status field.
    /// Useful when monitoring a relay's operation and relayed calls to a contract.
    /// Charge is the ether value deducted from the recipient's balance, paid to the relay's manager.
    event TransactionRelayed(
        address indexed relayManager,
        address indexed relayWorker,
        address indexed from,
        address to,
        address paymaster,
        bytes4 selector,
        RelayCallStatus status,
        uint256 charge
    );

    event TransactionResult(
        RelayCallStatus status,
        bytes returnValue
    );

    event HubDeprecated(uint256 fromBlock);

    /// Reason error codes for the TransactionRelayed event
    /// @param OK - the transaction was successfully relayed and execution successful - never included in the event
    /// @param RelayedCallFailed - the transaction was relayed, but the relayed call failed
    /// @param RejectedByPreRelayed - the transaction was not relayed due to preRelatedCall reverting
    /// @param RejectedByForwarder - the transaction was not relayed due to forwarder check (signature,nonce)
    /// @param PostRelayedFailed - the transaction was relayed and reverted due to postRelatedCall reverting
    /// @param PaymasterBalanceChanged - the transaction was relayed and reverted due to the paymaster balance change
    enum RelayCallStatus {
        OK,
        RelayedCallFailed,
        RejectedByPreRelayed,
        RejectedByForwarder,
        RejectedByRecipientRevert,
        PostRelayedFailed,
        PaymasterBalanceChanged
    }

    /// Add new worker addresses controlled by sender who must be a staked Relay Manager address.
    /// Emits a RelayWorkersAdded event.
    /// This function can be called multiple times, emitting new events
    function addRelayWorkers(address[] calldata newRelayWorkers) external;

    function registerRelayServer(uint256 baseRelayFee, uint256 pctRelayFee, string calldata url) external;

    // Balance management

    /// Deposits ether for a contract, so that it can receive (and pay for) relayed transactions. Unused balance can only
    /// be withdrawn by the contract itself, by calling withdraw.
    /// Emits a Deposited event.
    function depositFor(address target) external payable;

    /// Withdraws from an account's balance, sending it back to it. Relay managers call this to retrieve their revenue, and
    /// contracts can also use it to reduce their funding.
    /// Emits a Withdrawn event.
    function withdraw(uint256 amount, address payable dest) external;

    // Relaying


    /// Relays a transaction. For this to succeed, multiple conditions must be met:
    ///  - Paymaster's "preRelayCall" method must succeed and not revert
    ///  - the sender must be a registered Relay Worker that the user signed
    ///  - the transaction's gas price must be equal or larger than the one that was signed by the sender
    ///  - the transaction must have enough gas to run all internal transactions if they use all gas available to them
    ///  - the Paymaster must have enough balance to pay the Relay Worker for the scenario when all gas is spent
    ///
    /// If all conditions are met, the call will be relayed and the recipient charged.
    ///
    /// Arguments:
    /// @param maxAcceptanceBudget - max valid value for paymaster.getGasLimits().acceptanceBudget
    /// @param relayRequest - all details of the requested relayed call
    /// @param signature - client's EIP-712 signature over the relayRequest struct
    /// @param approvalData: dapp-specific data forwarded to preRelayedCall.
    ///        This value is *not* verified by the Hub. For example, it can be used to pass a signature to the Paymaster
    /// @param externalGasLimit - the value passed as gasLimit to the transaction.
    ///
    /// Emits a TransactionRelayed event.
    function relayCall(
        uint maxAcceptanceBudget,
        GsnTypes.RelayRequest calldata relayRequest,
        bytes calldata signature,
        bytes calldata approvalData,
        uint externalGasLimit
    )
    external
    returns (bool paymasterAccepted, bytes memory returnValue);

    function penalize(address relayWorker, address payable beneficiary) external;

    function setConfiguration(RelayHubConfig memory _config) external;

    // Deprecate hub (reverting relayCall()) from block number 'fromBlock'
    // Can only be called by owner
    function deprecateHub(uint256 fromBlock) external;

    /// The fee is expressed as a base fee in wei plus percentage on actual charge.
    /// E.g. a value of 40 stands for a 40% fee, so the recipient will be
    /// charged for 1.4 times the spent amount.
    function calculateCharge(uint256 gasUsed, GsnTypes.RelayData calldata relayData) external view returns (uint256);

    /* getters */

    /// Returns the whole hub configuration
    function getConfiguration() external view returns (RelayHubConfig memory config);

    function calldataGasCost(uint256 length) external view returns (uint256);

    function workerToManager(address worker) external view returns(address);

    function workerCount(address manager) external view returns(uint256);

    /// Returns an account's deposits. It can be either a deposit of a paymaster, or a revenue of a relay manager.
    function balanceOf(address target) external view returns (uint256);

    function stakeManager() external view returns (IStakeManager);

    function penalizer() external view returns (address);

    /// Uses StakeManager info to decide if the Relay Manager can be considered staked
    /// @return true if stake size and delay satisfy all requirements
    function isRelayManagerStaked(address relayManager) external view returns(bool);

    // Checks hubs' deprecation status
    function isDeprecated() external view returns (bool);

    // Returns the block number from which the hub no longer allows relaying calls.
    function deprecationBlock() external view returns (uint256);

    /// @return a SemVer-compliant version of the hub contract
    function versionHub() external view returns (string memory);
}

// File: src/interfaces/IPaymaster.sol

// (SPDX)-License-Identifier:MIT
pragma solidity >=0.7.6;
// pragma abicoder v2;


interface IPaymaster {

    /**
     * @param acceptanceBudget -
     *      Paymaster expected gas budget to accept (or reject) a request
     *      This a gas required by any calculations that might need to reject the
     *      transaction, by preRelayedCall, forwarder and recipient.
     *      See value in BasePaymaster.PAYMASTER_ACCEPTANCE_BUDGET
     *      Transaction that gets rejected above that gas usage is on the paymaster's expense.
     *      As long this value is above preRelayedCallGasLimit (see defaults in BasePaymaster), the
     *      Paymaster is guaranteed it will never pay for rejected transactions.
     *      If this value is below preRelayedCallGasLimt, it might might make Paymaster open to a "griefing" attack.
     *
     *      Specifying value too high might make the call rejected by some relayers.
     *
     *      From a Relay's point of view, this is the highest gas value a paymaster might "grief" the relay,
     *      since the paymaster will pay anything above that (regardless if the tx reverts)
     *
     * @param preRelayedCallGasLimit - the max gas usage of preRelayedCall. any revert (including OOG)
     *      of preRelayedCall is a reject by the paymaster.
     *      as long as acceptanceBudget is above preRelayedCallGasLimit, any such revert (including OOG)
     *      is not payed by the paymaster.
     * @param postRelayedCallGasLimit - the max gas usage of postRelayedCall.
     *      note that an OOG will revert the transaction, but the paymaster already committed to pay,
     *      so the relay will get compensated, at the expense of the paymaster
     */
    struct GasAndDataLimits {
        uint256 acceptanceBudget;
        uint256 preRelayedCallGasLimit;
        uint256 postRelayedCallGasLimit;
        uint256 calldataSizeLimit;
    }

    /**
     * Return the Gas Limits and msg.data max size constants used by the Paymaster.
     */
    function getGasAndDataLimits()
    external
    view
    returns (
        GasAndDataLimits memory limits
    );

    function trustedForwarder() external view returns (IForwarder);

/**
 * return the relayHub of this contract.
 */
    function getHubAddr() external view returns (address);

    /**
     * Can be used to determine if the contract can pay for incoming calls before making any.
     * @return the paymaster's deposit in the RelayHub.
     */
    function getRelayHubDeposit() external view returns (uint256);

    /**
     * Called by Relay (and RelayHub), to validate if the paymaster agrees to pay for this call.
     *
     * MUST be protected with relayHubOnly() in case it modifies state.
     *
     * The Paymaster rejects by the following "revert" operations
     *  - preRelayedCall() method reverts
     *  - the forwarder reverts because of nonce or signature error
     *  - the paymaster returned "rejectOnRecipientRevert", and the recipient contract reverted.
     * In any of the above cases, all paymaster calls (and recipient call) are reverted.
     * In any other case, the paymaster agrees to pay for the gas cost of the transaction (note
     *  that this includes also postRelayedCall revert)
     *
     * The rejectOnRecipientRevert flag means the Paymaster "delegate" the rejection to the recipient
     *  code.  It also means the Paymaster trust the recipient to reject fast: both preRelayedCall,
     *  forwarder check and receipient checks must fit into the GasLimits.acceptanceBudget,
     *  otherwise the TX is paid by the Paymaster.
     *
     *  @param relayRequest - the full relay request structure
     *  @param signature - user's EIP712-compatible signature of the {@link relayRequest}.
     *              Note that in most cases the paymaster shouldn't try use it at all. It is always checked
     *              by the forwarder immediately after preRelayedCall returns.
     *  @param approvalData - extra dapp-specific data (e.g. signature from trusted party)
     *  @param maxPossibleGas - based on values returned from {@link getGasAndDataLimits},
     *         the RelayHub will calculate the maximum possible amount of gas the user may be charged for.
     *         In order to convert this value to wei, the Paymaster has to call "relayHub.calculateCharge()"
     *  return:
     *      a context to be passed to postRelayedCall
     *      rejectOnRecipientRevert - TRUE if paymaster want to reject the TX if the recipient reverts.
     *          FALSE means that rejects by the recipient will be completed on chain, and paid by the paymaster.
     *          (note that in the latter case, the preRelayedCall and postRelayedCall are not reverted).
     */
    function preRelayedCall(
        GsnTypes.RelayRequest calldata relayRequest,
        bytes calldata signature,
        bytes calldata approvalData,
        uint256 maxPossibleGas
    )
    external
    returns (bytes memory context, bool rejectOnRecipientRevert);

    /**
     * This method is called after the actual relayed function call.
     * It may be used to record the transaction (e.g. charge the caller by some contract logic) for this call.
     *
     * MUST be protected with relayHubOnly() in case it modifies state.
     *
     * @param context - the call context, as returned by the preRelayedCall
     * @param success - true if the relayed call succeeded, false if it reverted
     * @param gasUseWithoutPost - the actual amount of gas used by the entire transaction, EXCEPT
     *        the gas used by the postRelayedCall itself.
     * @param relayData - the relay params of the request. can be used by relayHub.calculateCharge()
     *
     * Revert in this functions causes a revert of the client's relayed call (and preRelayedCall(), but the Paymaster
     * is still committed to pay the relay for the entire transaction.
     */
    function postRelayedCall(
        bytes calldata context,
        bool success,
        uint256 gasUseWithoutPost,
        GsnTypes.RelayData calldata relayData
    ) external;

    function versionPaymaster() external view returns (string memory);
}

// File: src/RelayHub.sol

/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable not-rely-on-time */
/* solhint-disable avoid-tx-origin */
/* solhint-disable bracket-align */
// (SPDX)-License-Identifier:MIT
pragma solidity ^0.7.6;
// pragma abicoder v2;












contract RelayHub is IRelayHub, Ownable {
    using SafeMath for uint256;

    string public override versionHub = "2.2.0+opengsn.hub.irelayhub";

    IStakeManager immutable override public stakeManager;
    address immutable override public penalizer;

    RelayHubConfig private config;

    function getConfiguration() public override view returns (RelayHubConfig memory) {
        return config;
    }

    function setConfiguration(RelayHubConfig memory _config) public override onlyOwner {
        config = _config;
        emit RelayHubConfigured(config);
    }

    uint256 public constant G_NONZERO = 16;

    // maps relay worker's address to its manager's address
    mapping(address => address) public override workerToManager;

    // maps relay managers to the number of their workers
    mapping(address => uint256) public override workerCount;

    mapping(address => uint256) private balances;

    uint256 public override deprecationBlock = type(uint).max;

    constructor (
        IStakeManager _stakeManager,
        address _penalizer,
        uint256 _maxWorkerCount,
        uint256 _gasReserve,
        uint256 _postOverhead,
        uint256 _gasOverhead,
        uint256 _maximumRecipientDeposit,
        uint256 _minimumUnstakeDelay,
        uint256 _minimumStake,
        uint256 _dataGasCostPerByte,
        uint256 _externalCallDataCostOverhead
    ) {
        stakeManager = _stakeManager;
        penalizer = _penalizer;
        setConfiguration(RelayHubConfig(
            _maxWorkerCount,
            _gasReserve,
            _postOverhead,
            _gasOverhead,
            _maximumRecipientDeposit,
            _minimumUnstakeDelay,
            _minimumStake,
            _dataGasCostPerByte,
            _externalCallDataCostOverhead
        ));
    }

    function registerRelayServer(uint256 baseRelayFee, uint256 pctRelayFee, string calldata url) external override {
        address relayManager = msg.sender;
        require(
            isRelayManagerStaked(relayManager),
            "relay manager not staked"
        );
        require(workerCount[relayManager] > 0, "no relay workers");
        emit RelayServerRegistered(relayManager, baseRelayFee, pctRelayFee, url);
    }

    function addRelayWorkers(address[] calldata newRelayWorkers) external override {
        address relayManager = msg.sender;
        uint256 newWorkerCount = workerCount[relayManager] + newRelayWorkers.length;
        workerCount[relayManager] = newWorkerCount;
        require(newWorkerCount <= config.maxWorkerCount, "too many workers");

        require(
            isRelayManagerStaked(relayManager),
            "relay manager not staked"
        );

        for (uint256 i = 0; i < newRelayWorkers.length; i++) {
            require(workerToManager[newRelayWorkers[i]] == address(0), "this worker has a manager");
            workerToManager[newRelayWorkers[i]] = relayManager;
        }

        emit RelayWorkersAdded(relayManager, newRelayWorkers, newWorkerCount);
    }

    function depositFor(address target) public override payable {
        uint256 amount = msg.value;
        require(amount <= config.maximumRecipientDeposit, "deposit too big");

        balances[target] = balances[target].add(amount);

        emit Deposited(target, msg.sender, amount);
    }

    function balanceOf(address target) external override view returns (uint256) {
        return balances[target];
    }

    function withdraw(uint256 amount, address payable dest) public override {
        address payable account = msg.sender;
        require(balances[account] >= amount, "insufficient funds");

        balances[account] = balances[account].sub(amount);
        dest.transfer(amount);

        emit Withdrawn(account, dest, amount);
    }

    function calldataGasCost(uint256 length) public override view returns (uint256) {
        return config.dataGasCostPerByte.mul(length);
}

    function verifyGasAndDataLimits(
        uint256 maxAcceptanceBudget,
        GsnTypes.RelayRequest calldata relayRequest,
        uint256 initialGasLeft,
        uint256 externalGasLimit
    )
    private
    view
    returns (IPaymaster.GasAndDataLimits memory gasAndDataLimits, uint256 maxPossibleGas) {
        gasAndDataLimits =
            IPaymaster(relayRequest.relayData.paymaster).getGasAndDataLimits{gas:50000}();
        require(msg.data.length <= gasAndDataLimits.calldataSizeLimit, "msg.data exceeded limit" );
        uint256 dataGasCost = calldataGasCost(msg.data.length);
        uint256 externalCallDataCost = externalGasLimit - initialGasLeft - config.externalCallDataCostOverhead;
        uint256 txDataCostPerByte = externalCallDataCost/msg.data.length;
        require(txDataCostPerByte <= G_NONZERO, "invalid externalGasLimit");

        require(maxAcceptanceBudget >= gasAndDataLimits.acceptanceBudget, "acceptance budget too high");
        require(gasAndDataLimits.acceptanceBudget >= gasAndDataLimits.preRelayedCallGasLimit, "acceptance budget too low");

        maxPossibleGas =
            config.gasOverhead.add(
            gasAndDataLimits.preRelayedCallGasLimit).add(
            gasAndDataLimits.postRelayedCallGasLimit).add(
            relayRequest.request.gas).add(
            dataGasCost).add(
            externalCallDataCost);

        // This transaction must have enough gas to forward the call to the recipient with the requested amount, and not
        // run out of gas later in this function.
        require(
            externalGasLimit >= maxPossibleGas,
            "no gas for innerRelayCall");

        uint256 maxPossibleCharge = calculateCharge(
            maxPossibleGas,
            relayRequest.relayData
        );

        // We don't yet know how much gas will be used by the recipient, so we make sure there are enough funds to pay
        // for the maximum possible charge.
        require(maxPossibleCharge <= balances[relayRequest.relayData.paymaster],
            "Paymaster balance too low");
    }

    struct RelayCallData {
        bool success;
        bytes4 functionSelector;
        uint256 initialGasLeft;
        bytes recipientContext;
        bytes relayedCallReturnValue;
        IPaymaster.GasAndDataLimits gasAndDataLimits;
        RelayCallStatus status;
        uint256 innerGasUsed;
        uint256 maxPossibleGas;
        uint256 gasBeforeInner;
        bytes retData;
        address relayManager;
        uint256 dataGasCost;
    }

    function relayCall(
        uint maxAcceptanceBudget,
        GsnTypes.RelayRequest calldata relayRequest,
        bytes calldata signature,
        bytes calldata approvalData,
        uint externalGasLimit
    )
    external
    override
    returns (bool paymasterAccepted, bytes memory returnValue)
    {
        RelayCallData memory vars;
        vars.initialGasLeft = gasleft();
        require(!isDeprecated(), "hub deprecated");
        vars.functionSelector = relayRequest.request.data.length>=4 ? MinLibBytes.readBytes4(relayRequest.request.data, 0) : bytes4(0);
        require(msg.sender == tx.origin, "relay worker must be EOA");
        vars.relayManager = workerToManager[msg.sender];
        require(vars.relayManager != address(0), "Unknown relay worker");
        require(relayRequest.relayData.relayWorker == msg.sender, "Not a right worker");
        require(
            isRelayManagerStaked(vars.relayManager),
            "relay manager not staked"
        );
        require(relayRequest.relayData.gasPrice <= tx.gasprice, "Invalid gas price");
        require(externalGasLimit <= block.gaslimit, "Impossible gas limit");

        (vars.gasAndDataLimits, vars.maxPossibleGas) =
             verifyGasAndDataLimits(maxAcceptanceBudget, relayRequest, vars.initialGasLeft, externalGasLimit);

        RelayHubValidator.verifyTransactionPacking(relayRequest,signature,approvalData);

    {

        //How much gas to pass down to innerRelayCall. must be lower than the default 63/64
        // actually, min(gasleft*63/64, gasleft-GAS_RESERVE) might be enough.
        uint256 innerGasLimit = gasleft()*63/64- config.gasReserve;
        vars.gasBeforeInner = gasleft();

        uint256 _tmpInitialGas = innerGasLimit + externalGasLimit + config.gasOverhead + config.postOverhead;
        // Calls to the recipient are performed atomically inside an inner transaction which may revert in case of
        // errors in the recipient. In either case (revert or regular execution) the return data encodes the
        // RelayCallStatus value.
        (bool success, bytes memory relayCallStatus) = address(this).call{gas:innerGasLimit}(
            abi.encodeWithSelector(RelayHub.innerRelayCall.selector, relayRequest, signature, approvalData, vars.gasAndDataLimits,
                _tmpInitialGas - gasleft(),
                vars.maxPossibleGas
                )
        );
        vars.success = success;
        vars.innerGasUsed = vars.gasBeforeInner-gasleft();
        (vars.status, vars.relayedCallReturnValue) = abi.decode(relayCallStatus, (RelayCallStatus, bytes));
        if ( vars.relayedCallReturnValue.length>0 ) {
            emit TransactionResult(vars.status, vars.relayedCallReturnValue);
        }
    }
    {
        vars.dataGasCost = calldataGasCost(msg.data.length);
        if (!vars.success) {
            //Failure cases where the PM doesn't pay
            if (vars.status == RelayCallStatus.RejectedByPreRelayed ||
                    (vars.innerGasUsed <= vars.gasAndDataLimits.acceptanceBudget.add(vars.dataGasCost)) && (
                    vars.status == RelayCallStatus.RejectedByForwarder ||
                    vars.status == RelayCallStatus.RejectedByRecipientRevert  //can only be thrown if rejectOnRecipientRevert==true
            )) {
                paymasterAccepted=false;

                emit TransactionRejectedByPaymaster(
                    vars.relayManager,
                    relayRequest.relayData.paymaster,
                    relayRequest.request.from,
                    relayRequest.request.to,
                    msg.sender,
                    vars.functionSelector,
                    vars.innerGasUsed,
                    vars.relayedCallReturnValue);
                return (false, vars.relayedCallReturnValue);
            }
        }
        // We now perform the actual charge calculation, based on the measured gas used
        uint256 gasUsed = (externalGasLimit - gasleft()) + config.gasOverhead;
        uint256 charge = calculateCharge(gasUsed, relayRequest.relayData);

        balances[relayRequest.relayData.paymaster] = balances[relayRequest.relayData.paymaster].sub(charge);
        balances[vars.relayManager] = balances[vars.relayManager].add(charge);

        emit TransactionRelayed(
            vars.relayManager,
            msg.sender,
            relayRequest.request.from,
            relayRequest.request.to,
            relayRequest.relayData.paymaster,
            vars.functionSelector,
            vars.status,
            charge);
        return (true, "");
    }
    }

    struct InnerRelayCallData {
        uint256 balanceBefore;
        bytes32 preReturnValue;
        bool relayedCallSuccess;
        bytes relayedCallReturnValue;
        bytes recipientContext;
        bytes data;
        bool rejectOnRecipientRevert;
    }

    function innerRelayCall(
        GsnTypes.RelayRequest calldata relayRequest,
        bytes calldata signature,
        bytes calldata approvalData,
        IPaymaster.GasAndDataLimits calldata gasAndDataLimits,
        uint256 totalInitialGas,
        uint256 maxPossibleGas
    )
    external
    returns (RelayCallStatus, bytes memory)
    {
        InnerRelayCallData memory vars;
        // A new gas measurement is performed inside innerRelayCall, since
        // due to EIP150 available gas amounts cannot be directly compared across external calls

        // This external function can only be called by RelayHub itself, creating an internal transaction. Calls to the
        // recipient (preRelayedCall, the relayedCall, and postRelayedCall) are called from inside this transaction.
        require(msg.sender == address(this), "Must be called by RelayHub");

        // If either pre or post reverts, the whole internal transaction will be reverted, reverting all side effects on
        // the recipient. The recipient will still be charged for the used gas by the relay.

        // The paymaster is no allowed to withdraw balance from RelayHub during a relayed transaction. We check pre and
        // post state to ensure this doesn't happen.
        vars.balanceBefore = balances[relayRequest.relayData.paymaster];

        // First preRelayedCall is executed.
        // Note: we open a new block to avoid growing the stack too much.
        vars.data = abi.encodeWithSelector(
            IPaymaster.preRelayedCall.selector,
                relayRequest, signature, approvalData, maxPossibleGas
        );
        {
            bool success;
            bytes memory retData;
            (success, retData) = relayRequest.relayData.paymaster.call{gas:gasAndDataLimits.preRelayedCallGasLimit}(vars.data);
            if (!success) {
                GsnEip712Library.truncateInPlace(retData);
                revertWithStatus(RelayCallStatus.RejectedByPreRelayed, retData);
            }
            (vars.recipientContext, vars.rejectOnRecipientRevert) = abi.decode(retData, (bytes,bool));
        }

        // The actual relayed call is now executed. The sender's address is appended at the end of the transaction data

        {
            bool forwarderSuccess;
            (forwarderSuccess, vars.relayedCallSuccess, vars.relayedCallReturnValue) = GsnEip712Library.execute(relayRequest, signature);
            if ( !forwarderSuccess ) {
                revertWithStatus(RelayCallStatus.RejectedByForwarder, vars.relayedCallReturnValue);
            }

            if (vars.rejectOnRecipientRevert && !vars.relayedCallSuccess) {
                // we trusted the recipient, but it reverted...
                revertWithStatus(RelayCallStatus.RejectedByRecipientRevert, vars.relayedCallReturnValue);
            }
        }
        // Finally, postRelayedCall is executed, with the relayedCall execution's status and a charge estimate
        // We now determine how much the recipient will be charged, to pass this value to postRelayedCall for accurate
        // accounting.
        vars.data = abi.encodeWithSelector(
            IPaymaster.postRelayedCall.selector,
            vars.recipientContext,
            vars.relayedCallSuccess,
            totalInitialGas - gasleft(), /*gasUseWithoutPost*/
            relayRequest.relayData
        );

        {
        (bool successPost,bytes memory ret) = relayRequest.relayData.paymaster.call{gas:gasAndDataLimits.postRelayedCallGasLimit}(vars.data);

        if (!successPost) {
            revertWithStatus(RelayCallStatus.PostRelayedFailed, ret);
        }
        }

        if (balances[relayRequest.relayData.paymaster] < vars.balanceBefore) {
            revertWithStatus(RelayCallStatus.PaymasterBalanceChanged, "");
        }

        return (vars.relayedCallSuccess ? RelayCallStatus.OK : RelayCallStatus.RelayedCallFailed, vars.relayedCallReturnValue);
    }

    /**
     * @dev Reverts the transaction with return data set to the ABI encoding of the status argument (and revert reason data)
     */
    function revertWithStatus(RelayCallStatus status, bytes memory ret) private pure {
        bytes memory data = abi.encode(status, ret);
        GsnEip712Library.truncateInPlace(data);

        assembly {
            let dataSize := mload(data)
            let dataPtr := add(data, 32)

            revert(dataPtr, dataSize)
        }
    }

    function calculateCharge(uint256 gasUsed, GsnTypes.RelayData calldata relayData) public override virtual view returns (uint256) {
        return relayData.baseRelayFee.add((gasUsed.mul(relayData.gasPrice).mul(relayData.pctRelayFee.add(100))).div(100));
    }

    function isRelayManagerStaked(address relayManager) public override view returns (bool) {
        return stakeManager.isRelayManagerStaked(relayManager, address(this), config.minimumStake, config.minimumUnstakeDelay);
    }

    function deprecateHub(uint256 fromBlock) public override onlyOwner {
        require(deprecationBlock > block.number, "Already deprecated");
        deprecationBlock = fromBlock;
        emit HubDeprecated(fromBlock);
    }

    function isDeprecated() public override view returns (bool) {
        return block.number >= deprecationBlock;
    }

    modifier penalizerOnly () {
        require(msg.sender == penalizer, "Not penalizer");
        _;
    }

    function penalize(address relayWorker, address payable beneficiary) external override penalizerOnly {
        address relayManager = workerToManager[relayWorker];
        // The worker must be controlled by a manager with a locked stake
        require(relayManager != address(0), "Unknown relay worker");
        require(
            isRelayManagerStaked(relayManager),
            "relay manager not staked"
        );
        IStakeManager.StakeInfo memory stakeInfo = stakeManager.getStakeInfo(relayManager);
        stakeManager.penalizeRelayManager(relayManager, beneficiary, stakeInfo.stake);
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IStakeManager","name":"_stakeManager","type":"address"},{"internalType":"address","name":"_penalizer","type":"address"},{"internalType":"uint256","name":"_maxWorkerCount","type":"uint256"},{"internalType":"uint256","name":"_gasReserve","type":"uint256"},{"internalType":"uint256","name":"_postOverhead","type":"uint256"},{"internalType":"uint256","name":"_gasOverhead","type":"uint256"},{"internalType":"uint256","name":"_maximumRecipientDeposit","type":"uint256"},{"internalType":"uint256","name":"_minimumUnstakeDelay","type":"uint256"},{"internalType":"uint256","name":"_minimumStake","type":"uint256"},{"internalType":"uint256","name":"_dataGasCostPerByte","type":"uint256"},{"internalType":"uint256","name":"_externalCallDataCostOverhead","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"paymaster","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fromBlock","type":"uint256"}],"name":"HubDeprecated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"maxWorkerCount","type":"uint256"},{"internalType":"uint256","name":"gasReserve","type":"uint256"},{"internalType":"uint256","name":"postOverhead","type":"uint256"},{"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"internalType":"uint256","name":"maximumRecipientDeposit","type":"uint256"},{"internalType":"uint256","name":"minimumUnstakeDelay","type":"uint256"},{"internalType":"uint256","name":"minimumStake","type":"uint256"},{"internalType":"uint256","name":"dataGasCostPerByte","type":"uint256"},{"internalType":"uint256","name":"externalCallDataCostOverhead","type":"uint256"}],"indexed":false,"internalType":"struct IRelayHub.RelayHubConfig","name":"config","type":"tuple"}],"name":"RelayHubConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseRelayFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pctRelayFee","type":"uint256"},{"indexed":false,"internalType":"string","name":"relayUrl","type":"string"}],"name":"RelayServerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayManager","type":"address"},{"indexed":false,"internalType":"address[]","name":"newRelayWorkers","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"workersCount","type":"uint256"}],"name":"RelayWorkersAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayManager","type":"address"},{"indexed":true,"internalType":"address","name":"paymaster","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"relayWorker","type":"address"},{"indexed":false,"internalType":"bytes4","name":"selector","type":"bytes4"},{"indexed":false,"internalType":"uint256","name":"innerGasUsed","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TransactionRejectedByPaymaster","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayManager","type":"address"},{"indexed":true,"internalType":"address","name":"relayWorker","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"paymaster","type":"address"},{"indexed":false,"internalType":"bytes4","name":"selector","type":"bytes4"},{"indexed":false,"internalType":"enum IRelayHub.RelayCallStatus","name":"status","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"charge","type":"uint256"}],"name":"TransactionRelayed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IRelayHub.RelayCallStatus","name":"status","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"returnValue","type":"bytes"}],"name":"TransactionResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"dest","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"G_NONZERO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"newRelayWorkers","type":"address[]"}],"name":"addRelayWorkers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"components":[{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"pctRelayFee","type":"uint256"},{"internalType":"uint256","name":"baseRelayFee","type":"uint256"},{"internalType":"address","name":"relayWorker","type":"address"},{"internalType":"address","name":"paymaster","type":"address"},{"internalType":"address","name":"forwarder","type":"address"},{"internalType":"bytes","name":"paymasterData","type":"bytes"},{"internalType":"uint256","name":"clientId","type":"uint256"}],"internalType":"struct GsnTypes.RelayData","name":"relayData","type":"tuple"}],"name":"calculateCharge","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"calldataGasCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"depositFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fromBlock","type":"uint256"}],"name":"deprecateHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deprecationBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConfiguration","outputs":[{"components":[{"internalType":"uint256","name":"maxWorkerCount","type":"uint256"},{"internalType":"uint256","name":"gasReserve","type":"uint256"},{"internalType":"uint256","name":"postOverhead","type":"uint256"},{"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"internalType":"uint256","name":"maximumRecipientDeposit","type":"uint256"},{"internalType":"uint256","name":"minimumUnstakeDelay","type":"uint256"},{"internalType":"uint256","name":"minimumStake","type":"uint256"},{"internalType":"uint256","name":"dataGasCostPerByte","type":"uint256"},{"internalType":"uint256","name":"externalCallDataCostOverhead","type":"uint256"}],"internalType":"struct IRelayHub.RelayHubConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"gas","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"validUntil","type":"uint256"}],"internalType":"struct IForwarder.ForwardRequest","name":"request","type":"tuple"},{"components":[{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"pctRelayFee","type":"uint256"},{"internalType":"uint256","name":"baseRelayFee","type":"uint256"},{"internalType":"address","name":"relayWorker","type":"address"},{"internalType":"address","name":"paymaster","type":"address"},{"internalType":"address","name":"forwarder","type":"address"},{"internalType":"bytes","name":"paymasterData","type":"bytes"},{"internalType":"uint256","name":"clientId","type":"uint256"}],"internalType":"struct GsnTypes.RelayData","name":"relayData","type":"tuple"}],"internalType":"struct GsnTypes.RelayRequest","name":"relayRequest","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"approvalData","type":"bytes"},{"components":[{"internalType":"uint256","name":"acceptanceBudget","type":"uint256"},{"internalType":"uint256","name":"preRelayedCallGasLimit","type":"uint256"},{"internalType":"uint256","name":"postRelayedCallGasLimit","type":"uint256"},{"internalType":"uint256","name":"calldataSizeLimit","type":"uint256"}],"internalType":"struct IPaymaster.GasAndDataLimits","name":"gasAndDataLimits","type":"tuple"},{"internalType":"uint256","name":"totalInitialGas","type":"uint256"},{"internalType":"uint256","name":"maxPossibleGas","type":"uint256"}],"name":"innerRelayCall","outputs":[{"internalType":"enum IRelayHub.RelayCallStatus","name":"","type":"uint8"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isDeprecated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"relayManager","type":"address"}],"name":"isRelayManagerStaked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"relayWorker","type":"address"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"name":"penalize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"penalizer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseRelayFee","type":"uint256"},{"internalType":"uint256","name":"pctRelayFee","type":"uint256"},{"internalType":"string","name":"url","type":"string"}],"name":"registerRelayServer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxAcceptanceBudget","type":"uint256"},{"components":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"gas","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"validUntil","type":"uint256"}],"internalType":"struct IForwarder.ForwardRequest","name":"request","type":"tuple"},{"components":[{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"pctRelayFee","type":"uint256"},{"internalType":"uint256","name":"baseRelayFee","type":"uint256"},{"internalType":"address","name":"relayWorker","type":"address"},{"internalType":"address","name":"paymaster","type":"address"},{"internalType":"address","name":"forwarder","type":"address"},{"internalType":"bytes","name":"paymasterData","type":"bytes"},{"internalType":"uint256","name":"clientId","type":"uint256"}],"internalType":"struct GsnTypes.RelayData","name":"relayData","type":"tuple"}],"internalType":"struct GsnTypes.RelayRequest","name":"relayRequest","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"approvalData","type":"bytes"},{"internalType":"uint256","name":"externalGasLimit","type":"uint256"}],"name":"relayCall","outputs":[{"internalType":"bool","name":"paymasterAccepted","type":"bool"},{"internalType":"bytes","name":"returnValue","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"maxWorkerCount","type":"uint256"},{"internalType":"uint256","name":"gasReserve","type":"uint256"},{"internalType":"uint256","name":"postOverhead","type":"uint256"},{"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"internalType":"uint256","name":"maximumRecipientDeposit","type":"uint256"},{"internalType":"uint256","name":"minimumUnstakeDelay","type":"uint256"},{"internalType":"uint256","name":"minimumStake","type":"uint256"},{"internalType":"uint256","name":"dataGasCostPerByte","type":"uint256"},{"internalType":"uint256","name":"externalCallDataCostOverhead","type":"uint256"}],"internalType":"struct IRelayHub.RelayHubConfig","name":"_config","type":"tuple"}],"name":"setConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeManager","outputs":[{"internalType":"contract IStakeManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"versionHub","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"dest","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"workerCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"workerToManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

610100604052601b60c08190527f322e322e302b6f70656e67736e2e6875622e6972656c6179687562000000000060e09081526200004191600191906200024d565b50600019600e553480156200005557600080fd5b5060405162003c5838038062003c588339810160408190526200007891620002f9565b60006200008462000169565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3508a6001600160a01b03166080816001600160a01b031660601b81525050896001600160a01b031660a0816001600160a01b031660601b81525050620001586040518061012001604052808b81526020018a8152602001898152602001888152602001878152602001868152602001858152602001848152602001838152506200016d60201b60201c565b50505050505050505050506200043c565b3390565b6200017762000169565b6001600160a01b03166200018a6200023e565b6001600160a01b031614620001bc5760405162461bcd60e51b8152600401620001b39062000391565b60405180910390fd5b8051600290815560208201516003556040808301516004556060830151600555608083015160065560a083015160075560c083015160085560e0830151600955610100830151600a55517f918ee002eb112844e457f37ea6b320c5572bc73957ebb0423ffcfb03d7b939d7916200023391620003c6565b60405180910390a150565b6000546001600160a01b031690565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620002855760008555620002d0565b82601f10620002a057805160ff1916838001178555620002d0565b82800160010185558215620002d0579182015b82811115620002d0578251825591602001919060010190620002b3565b50620002de929150620002e2565b5090565b5b80821115620002de5760008155600101620002e3565b60008060008060008060008060008060006101608c8e0312156200031b578687fd5b8b51620003288162000423565b60208d0151909b506200033b8162000423565b809a505060408c0151985060608c0151975060808c0151965060a08c0151955060c08c0151945060e08c015193506101008c015192506101208c015191506101408c015190509295989b509295989b9093969950565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b8154815260018201546020820152600282015460408201526003820154606082015260048201546080820152600582015460a0820152600682015460c0820152600782015460e08201526008909101546101008201526101200190565b6001600160a01b03811681146200043957600080fd5b50565b60805160601c60a05160601c6137de6200047a6000398061155752806116ef525080610c74528061117e52806117a3528061184452506137de6000f3fe6080604052600436106101655760003560e01c80638da5cb5b116100d1578063c651bce81161008a578063d6a71c0d11610064578063d6a71c0d1461040b578063d904c73214610420578063ebcd31ac14610442578063f2fde38b1461046257610165565b8063c651bce8146103b6578063c7178230146103d6578063ca998f56146103eb57610165565b80638da5cb5b146103195780638e53548b1461032e578063aa67c9191461034e578063af595dfc14610361578063c2da078614610381578063c4775a68146103a157610165565b80636bd50cef116101235780636bd50cef1461025257806370a0823114610274578063715018a614610294578063746d300c146102a95780637542ff95146102d757806383b71871146102f957610165565b8062f714ce1461016a5780630dd3eded1461018c57806310c45431146101b7578063194ac307146101e557806326595b9d146102055780632ad311b514610225575b600080fd5b34801561017657600080fd5b5061018a610185366004612728565b610482565b005b34801561019857600080fd5b506101a1610578565b6040516101ae9190612cad565b60405180910390f35b3480156101c357600080fd5b506101d76101d236600461278c565b61057d565b6040516101ae929190612c92565b3480156101f157600080fd5b506101a1610200366004612365565b610c2a565b34801561021157600080fd5b506101a1610220366004612710565b610c3c565b34801561023157600080fd5b50610245610240366004612365565b610c54565b6040516101ae9190612c87565b34801561025e57600080fd5b50610267610cfb565b6040516101ae91906133df565b34801561028057600080fd5b506101a161028f366004612365565b610d5a565b3480156102a057600080fd5b5061018a610d75565b3480156102b557600080fd5b506102c96102c43660046125ee565b610dfe565b6040516101ae929190612d5f565b3480156102e357600080fd5b506102ec61117c565b6040516101ae9190612b3d565b34801561030557600080fd5b5061018a61031436600461282e565b6111a0565b34801561032557600080fd5b506102ec611249565b34801561033a57600080fd5b506101a161034936600461274c565b611258565b61018a61035c366004612365565b61129e565b34801561036d57600080fd5b5061018a61037c366004612710565b611341565b34801561038d57600080fd5b5061018a61039c3660046123b9565b6113e1565b3480156103ad57600080fd5b506102ec611555565b3480156103c257600080fd5b5061018a6103d1366004612570565b611579565b3480156103e257600080fd5b5061024561162d565b3480156103f757600080fd5b506102ec610406366004612365565b611636565b34801561041757600080fd5b506101a1611651565b34801561042c57600080fd5b50610435611657565b6040516101ae9190612d7f565b34801561044e57600080fd5b5061018a61045d366004612381565b6116e4565b34801561046e57600080fd5b5061018a61047d366004612365565b6118b3565b336000818152600d60205260409020548311156104ba5760405162461bcd60e51b81526004016104b190613233565b60405180910390fd5b6001600160a01b0381166000908152600d60205260409020546104dd9084611973565b6001600160a01b038083166000908152600d60205260408082209390935591519084169185156108fc02918691818181858888f19350505050158015610527573d6000803e3d6000fd5b50816001600160a01b0316816001600160a01b03167fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb8560405161056b9190612cad565b60405180910390a3505050565b601081565b60006060610589612163565b5a604082015261059761162d565b156105b45760405162461bcd60e51b81526004016104b190612dc0565b60046105c08a806135d4565b6105ce9060a0810190613590565b905010156105dd576000610632565b6106326105ea8a806135d4565b6105f89060a0810190613590565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250925061199b915050565b6001600160e01b03191660208201523332146106605760405162461bcd60e51b81526004016104b19061302f565b336000908152600b60205260409020546001600160a01b0316610160820181905261069d5760405162461bcd60e51b81526004016104b19061335e565b336106ab60208b018b6135e9565b6106bc906080810190606001612365565b6001600160a01b0316146106e25760405162461bcd60e51b81526004016104b19061328d565b6106f0816101600151610c54565b61070c5760405162461bcd60e51b81526004016104b190612ff8565b3a61071a60208b018b6135e9565b3511156107395760405162461bcd60e51b81526004016104b190612f6d565b458411156107595760405162461bcd60e51b81526004016104b190612d92565b6107698a8a8360400151876119d4565b61010083015260a08201526107818989898989611c08565b60035460009060405a603f028161079457fe5b040390505a826101200181815250506000600280015460026003015487840101019050600080306001600160a01b03168463746d300c60e01b8f8f8f8f8f8c60a001515a8c038e61010001516040516024016107f79897969594939291906134a1565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516108359190612a86565b60006040518083038160008787f1925050503d8060008114610873576040519150601f19603f3d011682016040523d82523d6000602084013e610878565b606091505b50811515875290925090505a6101208601510360e086015280516108a590820160209081019083016124d7565b8660c001876080018290528260068111156108bc57fe5b60068111156108c757fe5b9052505060808501515115610918577fa1478a4242848419db824250a0dddc645dca0d6a9b12ab1fd79b00145a0ba98e8560c00151866080015160405161090f929190612d5f565b60405180910390a15b505050506109296000369050610c3c565b6101808201528051610a6d5760028160c00151600681111561094757fe5b148061099e575061018081015160a08201515161096391611cad565b8160e001511115801561099e575060038160c00151600681111561098357fe5b148061099e575060048160c00151600681111561099c57fe5b145b15610a6d57600092506109b189806135d4565b6109bf906020810190612365565b6001600160a01b03166109d560208b018b6135e9565b6109e69060a0810190608001612365565b6101608301516001600160a01b0391821691167fddb88484d11f800b80fe63aa67488ec56ee001d85896d528912c5d850cbcd06a610a248d806135d4565b610a35906040810190602001612365565b3386602001518760e001518860800151604051610a56959493929190612b51565b60405180910390a460800151600092509050610c1e565b6005546000905a86030190506000610a8c8261034960208e018e6135e9565b9050610ad781600d60008e8060200190610aa691906135e9565b610ab79060a0810190608001612365565b6001600160a01b0316815260208101919091526040016000205490611973565b600d6000610ae860208f018f6135e9565b610af99060a0810190608001612365565b6001600160a01b0390811682526020808301939093526040918201600090812094909455610160870151168352600d909152902054610b389082611cad565b6101608401516001600160a01b03166000908152600d6020526040902055610b608b806135d4565b610b6e906020810190612365565b6101608401516001600160a01b03918216913391167fc9aa709786a3d5fe2cc947abc1ba8cbb0f6decb57aa74b84eb7f558125fee454610bae8f806135d4565b610bbf906040810190602001612365565b8f8060200190610bcf91906135e9565b610be09060a0810190608001612365565b88602001518960c0015188604051610bfc959493929190612bc3565b60405180910390a4600160405180602001604052806000815250945094505050505b97509795505050505050565b600c6020526000908152604090205481565b600954600090610c4c9083611cd2565b90505b919050565b600854600754604051636de8dd4160e01b81526000926001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001692636de8dd4192610cab9287923092600401612c08565b60206040518083038186803b158015610cc357600080fd5b505afa158015610cd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4c9190612427565b610d036121e6565b50604080516101208101825260025481526003546020820152600454918101919091526005546060820152600654608082015260075460a082015260085460c082015260095460e0820152600a5461010082015290565b6001600160a01b03166000908152600d602052604090205490565b610d7d611d0c565b6001600160a01b0316610d8e611249565b6001600160a01b031614610db45760405162461bcd60e51b81526004016104b190613166565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60006060610e0a612232565b333014610e295760405162461bcd60e51b81526004016104b190612f36565b600d6000610e3a60208e018e6135e9565b610e4b9060a0810190608001612365565b6001600160a01b031681526020810191909152604090810160002054825251622f977560e21b90610e8a908d908d908d908d908d908b90602401613521565b60408051601f19818403018152919052602080820180516001600160e01b03166001600160e01b03199094169390931790925260a0830152600090606090610ed4908e018e6135e9565b610ee59060a0810190608001612365565b6001600160a01b031688602001358460a00151604051610f059190612a86565b60006040518083038160008787f1925050503d8060008114610f43576040519150601f19603f3d011682016040523d82523d6000602084013e610f48565b606091505b50909250905081610f6757610f5c81611d10565b610f67600282611d1f565b80806020019051810190610f7b919061248c565b151560c085015260808401525060009050610f978c8c8c611d58565b606085015215156040840152905080610fb957610fb960038360600151611d1f565b8160c001518015610fcc57508160400151155b15610fe057610fe060048360600151611d1f565b506376fa01c360e01b816080015182604001515a88038e806020019061100691906135e9565b6040516024016110199493929190612d2c565b60408051601f19818403018152919052602080820180516001600160e01b03166001600160e01b03199094169390931790925260a08301526000908190611062908e018e6135e9565b6110739060a0810190608001612365565b6001600160a01b031688604001358460a001516040516110939190612a86565b60006040518083038160008787f1925050503d80600081146110d1576040519150601f19603f3d011682016040523d82523d6000602084013e6110d6565b606091505b5091509150816110eb576110eb600582611d1f565b50508051600d600061110060208f018f6135e9565b6111119060a0810190608001612365565b6001600160a01b03166001600160a01b0316815260200190815260200160002054101561115257611152600660405180602001604052806000815250611d1f565b8060400151611162576001611165565b60005b816060015192509250509850989650505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b336111aa81610c54565b6111c65760405162461bcd60e51b81526004016104b190612ff8565b6001600160a01b0381166000908152600c60205260409020546111fb5760405162461bcd60e51b81526004016104b19061309d565b806001600160a01b03167f77f2d8afec4b9d82ffa0dea525320620292bd1067f575964994d5c4501479aed8686868660405161123a9493929190613570565b60405180910390a25050505050565b6000546001600160a01b031690565b600061129561128a6064611284611273602087013583611cad565b61127e888835611cd2565b90611cd2565b90611f3c565b604084013590611cad565b90505b92915050565b60065434908111156112c25760405162461bcd60e51b81526004016104b190612f98565b6001600160a01b0382166000908152600d60205260409020546112e59082611cad565b6001600160a01b0383166000818152600d6020526040908190209290925590513391907f8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a790611335908590612cad565b60405180910390a35050565b611349611d0c565b6001600160a01b031661135a611249565b6001600160a01b0316146113805760405162461bcd60e51b81526004016104b190613166565b43600e54116113a15760405162461bcd60e51b81526004016104b190612e1f565b600e8190556040517f1c0aa0c666483fbf0cf795d9d646ea3552d1e3008162ba9ab1d6d6dfd8c6ec6b906113d6908390612cad565b60405180910390a150565b336000818152600c60205260409020805483019081905560025481111561141a5760405162461bcd60e51b81526004016104b1906131d2565b61142382610c54565b61143f5760405162461bcd60e51b81526004016104b190612ff8565b60005b83811015611509576000600b8187878581811061145b57fe5b90506020020160208101906114709190612365565b6001600160a01b03908116825260208201929092526040016000205416146114aa5760405162461bcd60e51b81526004016104b190612e4b565b82600b60008787858181106114bb57fe5b90506020020160208101906114d09190612365565b6001600160a01b039081168252602082019290925260400160002080546001600160a01b03191692909116919091179055600101611442565b50816001600160a01b03167febf4a9bffb39f7c5dbf3f65540183b9381ae226ac3d0a45b4cad484713bd4a2885858460405161154793929190612c31565b60405180910390a250505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b611581611d0c565b6001600160a01b0316611592611249565b6001600160a01b0316146115b85760405162461bcd60e51b81526004016104b190613166565b8051600290815560208201516003556040808301516004556060830151600555608083015160065560a083015160075560c083015160085560e0830151600955610100830151600a55517f918ee002eb112844e457f37ea6b320c5572bc73957ebb0423ffcfb03d7b939d7916113d691613444565b600e5443101590565b600b602052600090815260409020546001600160a01b031681565b600e5481565b60018054604080516020600284861615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156116dc5780601f106116b1576101008083540402835291602001916116dc565b820191906000526020600020905b8154815290600101906020018083116116bf57829003601f168201915b505050505081565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461172c5760405162461bcd60e51b81526004016104b1906130fe565b6001600160a01b038083166000908152600b602052604090205416806117645760405162461bcd60e51b81526004016104b19061335e565b61176d81610c54565b6117895760405162461bcd60e51b81526004016104b190612ff8565b60405163c345315360e01b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c3453153906117d8908590600401612b3d565b60806040518083038186803b1580156117f057600080fd5b505afa158015611804573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182891906126ac565b805160405163026822bd60e21b81529192506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916309a08af49161187b9186918891600401612b9f565b600060405180830381600087803b15801561189557600080fd5b505af11580156118a9573d6000803e3d6000fd5b5050505050505050565b6118bb611d0c565b6001600160a01b03166118cc611249565b6001600160a01b0316146118f25760405162461bcd60e51b81526004016104b190613166565b6001600160a01b0381166119185760405162461bcd60e51b81526004016104b190612e82565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000828211156119955760405162461bcd60e51b81526004016104b190612fc1565b50900390565b600081600401835110156119c15760405162461bcd60e51b81526004016104b190613327565b5001602001516001600160e01b03191690565b6119dc61226d565b60006119eb60208601866135e9565b6119fc9060a0810190608001612365565b6001600160a01b031663b039a88f61c3506040518263ffffffff1660e01b815260040160806040518083038187803b158015611a3757600080fd5b5086fa158015611a4b573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611a709190612512565b6060810151909250361115611a975760405162461bcd60e51b81526004016104b1906130c7565b6000611aa236610c3c565b600a54909150858503036000368281611ab757fe5b0490506010811115611adb5760405162461bcd60e51b81526004016104b190612de8565b8451891015611afc5760405162461bcd60e51b81526004016104b1906131fc565b602085015185511015611b215760405162461bcd60e51b81526004016104b190612ec8565b611b6282611b5c8581611b348d806135d4565b60600135611b5c8b60400151611b5c8d60200151600260030154611cad90919063ffffffff16565b90611cad565b935083861015611b845760405162461bcd60e51b81526004016104b19061319b565b6000611b978561034960208c018c6135e9565b9050600d6000611baa60208c018c6135e9565b611bbb9060a0810190608001612365565b6001600160a01b03166001600160a01b0316815260200190815260200160002054811115611bfb5760405162461bcd60e51b81526004016104b1906132b9565b5050505094509492505050565b6000611c2d611c1a60208801886135e9565b611c289060c0810190613590565b611f6e565b611c48611c3a88806135d4565b611c289060a0810190613590565b611c528585611f6e565b611c5c8888611f6e565b0101016102c40190506041841115611c865760405162461bcd60e51b81526004016104b1906132f0565b368114611ca55760405162461bcd60e51b81526004016104b19061325f565b505050505050565b6000828201838110156112955760405162461bcd60e51b81526004016104b190612eff565b600082611ce157506000611298565b82820282848281611cee57fe5b04146112955760405162461bcd60e51b81526004016104b190613125565b3390565b611d1c81610400611f7f565b50565b60008282604051602001611d34929190612d5f565b6040516020818303038152906040529050611d4e81611d10565b8051602082018181fd5b60008060606000611d6887611f90565b90506000611d92611d7c60208a018a6135e9565b611d8d9060c081019060a001612365565b611fcd565b9050611da160208901896135e9565b611db29060c081019060a001612365565b6001600160a01b031663e024dc7f60e01b611dcd8a806135d4565b836040518060400160405280600c81526020016b14995b185e54995c5d595cdd60a21b8152506040518060800160405280605d81526020016136ab605d91396040518060e0016040528060a1815260200161370860a19139604051602001611e359190612b01565b60408051601f1981840301815290829052611e54939291602001612aa2565b60405160208183030381529060405280519060200120868c8c604051602401611e829695949392919061338c565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611ec09190612a86565b6000604051808303816000865af19150503d8060008114611efd576040519150601f19603f3d011682016040523d82523d6000602084013e611f02565b606091505b5090955092508415611f285782806020019051810190611f229190612441565b90945092505b611f3183611d10565b505093509350939050565b6000808211611f5d5760405162461bcd60e51b81526004016104b190613066565b818381611f6657fe5b049392505050565b6020601f8201601f19160192915050565b8082511115611f8c578082525b5050565b6060611fa7611fa260208401846135e9565b61204d565b604051602001611fb79190612cad565b6040516020818303038152906040529050919050565b6040805160c0810182526017608082019081527f47534e2052656c61796564205472616e73616374696f6e00000000000000000060a083015281528151808301835260018152601960f91b602082810191909152820152600091610c4c91908101612036612106565b8152602001846001600160a01b031681525061210a565b60006040518060e0016040528060a1815260200161370860a19139805160209182012090833590840135604085013561208c6080870160608801612365565b61209c60a0880160808901612365565b6120ac60c0890160a08a01612365565b6120b960c08a018a613590565b6040516120c7929190612a76565b6040519081900381206120e9989796959493929160e08c013590602001612ce2565b604051602081830303815290604052805190602001209050919050565b4690565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f826000015180519060200120836020015180519060200120846040015185606001516040516020016120e9959493929190612cb6565b604051806101a0016040528060001515815260200160006001600160e01b03191681526020016000815260200160608152602001606081526020016121a661226d565b8152602001600081526020016000815260200160008152602001600081526020016060815260200160006001600160a01b03168152602001600081525090565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160e08101825260008082526020820181905291810182905260608082018190526080820181905260a082015260c081019190915290565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b80518015158114610c4f57600080fd5b60008083601f8401126122b6578182fd5b5081356001600160401b038111156122cc578182fd5b6020830191508360208285010111156122e457600080fd5b9250929050565b600082601f8301126122fb578081fd5b81516001600160401b0381111561230e57fe5b612321601f8201601f19166020016135fe565b818152846020838601011115612335578283fd5b612346826020830160208701613665565b949350505050565b60006040828403121561235f578081fd5b50919050565b600060208284031215612376578081fd5b813561129581613695565b60008060408385031215612393578081fd5b823561239e81613695565b915060208301356123ae81613695565b809150509250929050565b600080602083850312156123cb578182fd5b82356001600160401b03808211156123e1578384fd5b818501915085601f8301126123f4578384fd5b813581811115612402578485fd5b8660208083028501011115612415578485fd5b60209290920196919550909350505050565b600060208284031215612438578081fd5b61129582612295565b60008060408385031215612453578182fd5b61245c83612295565b915060208301516001600160401b03811115612476578182fd5b612482858286016122eb565b9150509250929050565b6000806040838503121561249e578182fd5b82516001600160401b038111156124b3578283fd5b6124bf858286016122eb565b9250506124ce60208401612295565b90509250929050565b600080604083850312156124e9578081fd5b8251600781106124f7578182fd5b60208401519092506001600160401b03811115612476578182fd5b600060808284031215612523578081fd5b604051608081018181106001600160401b038211171561253f57fe5b8060405250825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b6000610120808385031215612583578182fd5b61258c816135fe565b9050823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e08201526101008084013581830152508091505092915050565b600080600080600080600080888a0361012081121561260b578485fd5b89356001600160401b0380821115612621578687fd5b61262d8d838e0161234e565b9a5060208c0135915080821115612642578687fd5b61264e8d838e016122a5565b909a50985060408c0135915080821115612666578687fd5b506126738c828d016122a5565b9097509550506080605f198201121561268a578384fd5b5096999598509396929550909360608301935060e08301359261010001359150565b6000608082840312156126bd578081fd5b604051608081018181106001600160401b03821117156126d957fe5b8060405250825181526020830151602082015260408301516040820152606083015161270481613695565b60608201529392505050565b600060208284031215612721578081fd5b5035919050565b6000806040838503121561273a578182fd5b8235915060208301356123ae81613695565b6000806040838503121561275e578182fd5b8235915060208301356001600160401b0381111561277a578182fd5b830161010081860312156123ae578182fd5b600080600080600080600060a0888a0312156127a6578081fd5b8735965060208801356001600160401b03808211156127c3578283fd5b6127cf8b838c0161234e565b975060408a01359150808211156127e4578283fd5b6127f08b838c016122a5565b909750955060608a0135915080821115612808578283fd5b506128158a828b016122a5565b989b979a50959894979596608090950135949350505050565b60008060008060608587031215612843578182fd5b843593506020850135925060408501356001600160401b03811115612866578283fd5b612872878288016122a5565b95989497509550505050565b6001600160a01b03169052565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b600081518084526128cd816020860160208601613665565b601f01601f19169290920160200192915050565b600781106128eb57fe5b9052565b600081356128fc81613695565b6001600160a01b03908116845260208301359061291882613695565b16602084015260408281013590840152606080830135908401526080808301359084015261294960a0830183613621565b60e060a086015261295e60e08601828461288b565b91505060c083013560c08501528091505092915050565b600061010082358452602083013560208501526040830135604085015260608301356129a081613695565b6001600160a01b0390811660608601526080840135906129bf82613695565b16608085015260a08301356129d381613695565b6129e060a086018261287e565b506129ee60c0840184613621565b8260c0870152612a01838701828461288b565b9250505060e083013560e08501528091505092915050565b6000813582360360de1981018212612a2f578283fd5b60408552612a42604086018584016128ef565b9150602084013560fe1982018112612a58578384fd5b8583036020870152612a6c83868301612975565b9695505050505050565b6000828483379101908152919050565b60008251612a98818460208701613665565b9190910192915050565b60008451612ab4818460208901613665565b600560fb1b9083019081528451612ad2816001840160208901613665565b600b60fa1b600192909101918201528351612af4816002840160208801613665565b0160020195945050505050565b60007352656c6179446174612072656c6179446174612960601b82528251612b30816014850160208701613665565b9190910160140192915050565b6001600160a01b0391909116815260200190565b6001600160a01b038681168252851660208201526001600160e01b0319841660408201526060810183905260a060808201819052600090612b94908301846128b5565b979650505050505050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b038681168252851660208201526001600160e01b03198416604082015260a08101612bf860608301856128e1565b8260808301529695505050505050565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6040808252810183905260008460608301825b86811015612c74578235612c5781613695565b6001600160a01b0316825260209283019290910190600101612c44565b5060209390930193909352509392505050565b901515815260200190565b600083151582526040602083015261234660408301846128b5565b90815260200190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b9889526020890197909752604088019590955260608701939093526001600160a01b039182166080870152811660a08601521660c084015260e08301526101008201526101200190565b600060808252612d3f60808301876128b5565b85151560208401528460408401528281036060840152612b948185612975565b6000612d6b82856128e1565b6040602083015261234660408301846128b5565b60006020825261129560208301846128b5565b602080825260149082015273125b5c1bdcdcda589b194819d85cc81b1a5b5a5d60621b604082015260600190565b6020808252600e908201526d1a1d588819195c1c9958d85d195960921b604082015260600190565b60208082526018908201527f696e76616c69642065787465726e616c4761734c696d69740000000000000000604082015260600190565b602080825260129082015271105b1c9958591e4819195c1c9958d85d195960721b604082015260600190565b60208082526019908201527f7468697320776f726b6572206861732061206d616e6167657200000000000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526019908201527f616363657074616e63652062756467657420746f6f206c6f7700000000000000604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252601a908201527f4d7573742062652063616c6c65642062792052656c6179487562000000000000604082015260600190565b602080825260119082015270496e76616c69642067617320707269636560781b604082015260600190565b6020808252600f908201526e6465706f73697420746f6f2062696760881b604082015260600190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b60208082526018908201527f72656c6179206d616e61676572206e6f74207374616b65640000000000000000604082015260600190565b60208082526018908201527f72656c617920776f726b6572206d75737420626520454f410000000000000000604082015260600190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b60208082526010908201526f6e6f2072656c617920776f726b65727360801b604082015260600190565b60208082526017908201527f6d73672e64617461206578636565646564206c696d6974000000000000000000604082015260600190565b6020808252600d908201526c2737ba103832b730b634bd32b960991b604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526019908201527f6e6f2067617320666f7220696e6e657252656c617943616c6c00000000000000604082015260600190565b60208082526010908201526f746f6f206d616e7920776f726b65727360801b604082015260600190565b6020808252601a908201527f616363657074616e63652062756467657420746f6f2068696768000000000000604082015260600190565b602080825260129082015271696e73756666696369656e742066756e647360701b604082015260600190565b6020808252601490820152736578747261206d73672e6461746120627974657360601b604082015260600190565b6020808252601290820152712737ba1030903934b3b43a103bb7b935b2b960711b604082015260600190565b60208082526019908201527f5061796d61737465722062616c616e636520746f6f206c6f7700000000000000604082015260600190565b60208082526018908201527f696e76616c6964207369676e6174757265206c656e6774680000000000000000604082015260600190565b6020808252601a908201527f726561644279746573343a206461746120746f6f2073686f7274000000000000604082015260600190565b6020808252601490820152732ab735b737bbb7103932b630bc903bb7b935b2b960611b604082015260600190565b600060a0825261339f60a08301896128ef565b87602084015286604084015282810360608401526133bd81876128b5565b905082810360808401526133d281858761288b565b9998505050505050505050565b600061012082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525092915050565b8154815260018201546020820152600282015460408201526003820154606082015260048201546080820152600582015460a0820152600682015460c0820152600782015460e08201526008909101546101008201526101200190565b60006101208083526134b58184018c612a19565b905082810360208401526134ca818a8c61288b565b905082810360408401526134df81888a61288b565b9150508451606083015260208501516080830152604085015160a0830152606085015160c08301528360e0830152826101008301529998505050505050505050565b6000608082526135346080830189612a19565b828103602084015261354781888a61288b565b9050828103604084015261355c81868861288b565b915050826060830152979650505050505050565b600085825284602083015260606040830152612a6c60608301848661288b565b6000808335601e198436030181126135a6578283fd5b8301803591506001600160401b038211156135bf578283fd5b6020019150368190038213156122e457600080fd5b6000823560de19833603018112612a98578182fd5b6000823560fe19833603018112612a98578182fd5b6040518181016001600160401b038111828210171561361957fe5b604052919050565b6000808335601e19843603018112613637578283fd5b83016020810192503590506001600160401b0381111561365657600080fd5b8036038313156122e457600080fd5b60005b83811015613680578181015183820152602001613668565b8381111561368f576000848401525b50505050565b6001600160a01b0381168114611d1c57600080fdfe616464726573732066726f6d2c6164647265737320746f2c75696e743235362076616c75652c75696e74323536206761732c75696e74323536206e6f6e63652c627974657320646174612c75696e743235362076616c6964556e74696c52656c6179446174612875696e743235362067617350726963652c75696e743235362070637452656c61794665652c75696e74323536206261736552656c61794665652c616464726573732072656c6179576f726b65722c61646472657373207061796d61737465722c6164647265737320666f727761726465722c6279746573207061796d6173746572446174612c75696e7432353620636c69656e74496429a2646970667358221220f3aa7edd3470b3c75e5cd253b2b645a290b4c98d15b18abe5d24ca659075d9a264736f6c6343000706003300000000000000000000000015c7b7ce10f3a9ae63554bce7c54d0a818e967c70000000000000000000000000adf62f267206ff6ead3d93f4d421f86b51c6b7d000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000003ada000000000000000000000000000000000000000000000000000000000000816f0000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000578e

Deployed Bytecode

0x6080604052600436106101655760003560e01c80638da5cb5b116100d1578063c651bce81161008a578063d6a71c0d11610064578063d6a71c0d1461040b578063d904c73214610420578063ebcd31ac14610442578063f2fde38b1461046257610165565b8063c651bce8146103b6578063c7178230146103d6578063ca998f56146103eb57610165565b80638da5cb5b146103195780638e53548b1461032e578063aa67c9191461034e578063af595dfc14610361578063c2da078614610381578063c4775a68146103a157610165565b80636bd50cef116101235780636bd50cef1461025257806370a0823114610274578063715018a614610294578063746d300c146102a95780637542ff95146102d757806383b71871146102f957610165565b8062f714ce1461016a5780630dd3eded1461018c57806310c45431146101b7578063194ac307146101e557806326595b9d146102055780632ad311b514610225575b600080fd5b34801561017657600080fd5b5061018a610185366004612728565b610482565b005b34801561019857600080fd5b506101a1610578565b6040516101ae9190612cad565b60405180910390f35b3480156101c357600080fd5b506101d76101d236600461278c565b61057d565b6040516101ae929190612c92565b3480156101f157600080fd5b506101a1610200366004612365565b610c2a565b34801561021157600080fd5b506101a1610220366004612710565b610c3c565b34801561023157600080fd5b50610245610240366004612365565b610c54565b6040516101ae9190612c87565b34801561025e57600080fd5b50610267610cfb565b6040516101ae91906133df565b34801561028057600080fd5b506101a161028f366004612365565b610d5a565b3480156102a057600080fd5b5061018a610d75565b3480156102b557600080fd5b506102c96102c43660046125ee565b610dfe565b6040516101ae929190612d5f565b3480156102e357600080fd5b506102ec61117c565b6040516101ae9190612b3d565b34801561030557600080fd5b5061018a61031436600461282e565b6111a0565b34801561032557600080fd5b506102ec611249565b34801561033a57600080fd5b506101a161034936600461274c565b611258565b61018a61035c366004612365565b61129e565b34801561036d57600080fd5b5061018a61037c366004612710565b611341565b34801561038d57600080fd5b5061018a61039c3660046123b9565b6113e1565b3480156103ad57600080fd5b506102ec611555565b3480156103c257600080fd5b5061018a6103d1366004612570565b611579565b3480156103e257600080fd5b5061024561162d565b3480156103f757600080fd5b506102ec610406366004612365565b611636565b34801561041757600080fd5b506101a1611651565b34801561042c57600080fd5b50610435611657565b6040516101ae9190612d7f565b34801561044e57600080fd5b5061018a61045d366004612381565b6116e4565b34801561046e57600080fd5b5061018a61047d366004612365565b6118b3565b336000818152600d60205260409020548311156104ba5760405162461bcd60e51b81526004016104b190613233565b60405180910390fd5b6001600160a01b0381166000908152600d60205260409020546104dd9084611973565b6001600160a01b038083166000908152600d60205260408082209390935591519084169185156108fc02918691818181858888f19350505050158015610527573d6000803e3d6000fd5b50816001600160a01b0316816001600160a01b03167fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb8560405161056b9190612cad565b60405180910390a3505050565b601081565b60006060610589612163565b5a604082015261059761162d565b156105b45760405162461bcd60e51b81526004016104b190612dc0565b60046105c08a806135d4565b6105ce9060a0810190613590565b905010156105dd576000610632565b6106326105ea8a806135d4565b6105f89060a0810190613590565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250925061199b915050565b6001600160e01b03191660208201523332146106605760405162461bcd60e51b81526004016104b19061302f565b336000908152600b60205260409020546001600160a01b0316610160820181905261069d5760405162461bcd60e51b81526004016104b19061335e565b336106ab60208b018b6135e9565b6106bc906080810190606001612365565b6001600160a01b0316146106e25760405162461bcd60e51b81526004016104b19061328d565b6106f0816101600151610c54565b61070c5760405162461bcd60e51b81526004016104b190612ff8565b3a61071a60208b018b6135e9565b3511156107395760405162461bcd60e51b81526004016104b190612f6d565b458411156107595760405162461bcd60e51b81526004016104b190612d92565b6107698a8a8360400151876119d4565b61010083015260a08201526107818989898989611c08565b60035460009060405a603f028161079457fe5b040390505a826101200181815250506000600280015460026003015487840101019050600080306001600160a01b03168463746d300c60e01b8f8f8f8f8f8c60a001515a8c038e61010001516040516024016107f79897969594939291906134a1565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516108359190612a86565b60006040518083038160008787f1925050503d8060008114610873576040519150601f19603f3d011682016040523d82523d6000602084013e610878565b606091505b50811515875290925090505a6101208601510360e086015280516108a590820160209081019083016124d7565b8660c001876080018290528260068111156108bc57fe5b60068111156108c757fe5b9052505060808501515115610918577fa1478a4242848419db824250a0dddc645dca0d6a9b12ab1fd79b00145a0ba98e8560c00151866080015160405161090f929190612d5f565b60405180910390a15b505050506109296000369050610c3c565b6101808201528051610a6d5760028160c00151600681111561094757fe5b148061099e575061018081015160a08201515161096391611cad565b8160e001511115801561099e575060038160c00151600681111561098357fe5b148061099e575060048160c00151600681111561099c57fe5b145b15610a6d57600092506109b189806135d4565b6109bf906020810190612365565b6001600160a01b03166109d560208b018b6135e9565b6109e69060a0810190608001612365565b6101608301516001600160a01b0391821691167fddb88484d11f800b80fe63aa67488ec56ee001d85896d528912c5d850cbcd06a610a248d806135d4565b610a35906040810190602001612365565b3386602001518760e001518860800151604051610a56959493929190612b51565b60405180910390a460800151600092509050610c1e565b6005546000905a86030190506000610a8c8261034960208e018e6135e9565b9050610ad781600d60008e8060200190610aa691906135e9565b610ab79060a0810190608001612365565b6001600160a01b0316815260208101919091526040016000205490611973565b600d6000610ae860208f018f6135e9565b610af99060a0810190608001612365565b6001600160a01b0390811682526020808301939093526040918201600090812094909455610160870151168352600d909152902054610b389082611cad565b6101608401516001600160a01b03166000908152600d6020526040902055610b608b806135d4565b610b6e906020810190612365565b6101608401516001600160a01b03918216913391167fc9aa709786a3d5fe2cc947abc1ba8cbb0f6decb57aa74b84eb7f558125fee454610bae8f806135d4565b610bbf906040810190602001612365565b8f8060200190610bcf91906135e9565b610be09060a0810190608001612365565b88602001518960c0015188604051610bfc959493929190612bc3565b60405180910390a4600160405180602001604052806000815250945094505050505b97509795505050505050565b600c6020526000908152604090205481565b600954600090610c4c9083611cd2565b90505b919050565b600854600754604051636de8dd4160e01b81526000926001600160a01b037f00000000000000000000000015c7b7ce10f3a9ae63554bce7c54d0a818e967c71692636de8dd4192610cab9287923092600401612c08565b60206040518083038186803b158015610cc357600080fd5b505afa158015610cd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4c9190612427565b610d036121e6565b50604080516101208101825260025481526003546020820152600454918101919091526005546060820152600654608082015260075460a082015260085460c082015260095460e0820152600a5461010082015290565b6001600160a01b03166000908152600d602052604090205490565b610d7d611d0c565b6001600160a01b0316610d8e611249565b6001600160a01b031614610db45760405162461bcd60e51b81526004016104b190613166565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60006060610e0a612232565b333014610e295760405162461bcd60e51b81526004016104b190612f36565b600d6000610e3a60208e018e6135e9565b610e4b9060a0810190608001612365565b6001600160a01b031681526020810191909152604090810160002054825251622f977560e21b90610e8a908d908d908d908d908d908b90602401613521565b60408051601f19818403018152919052602080820180516001600160e01b03166001600160e01b03199094169390931790925260a0830152600090606090610ed4908e018e6135e9565b610ee59060a0810190608001612365565b6001600160a01b031688602001358460a00151604051610f059190612a86565b60006040518083038160008787f1925050503d8060008114610f43576040519150601f19603f3d011682016040523d82523d6000602084013e610f48565b606091505b50909250905081610f6757610f5c81611d10565b610f67600282611d1f565b80806020019051810190610f7b919061248c565b151560c085015260808401525060009050610f978c8c8c611d58565b606085015215156040840152905080610fb957610fb960038360600151611d1f565b8160c001518015610fcc57508160400151155b15610fe057610fe060048360600151611d1f565b506376fa01c360e01b816080015182604001515a88038e806020019061100691906135e9565b6040516024016110199493929190612d2c565b60408051601f19818403018152919052602080820180516001600160e01b03166001600160e01b03199094169390931790925260a08301526000908190611062908e018e6135e9565b6110739060a0810190608001612365565b6001600160a01b031688604001358460a001516040516110939190612a86565b60006040518083038160008787f1925050503d80600081146110d1576040519150601f19603f3d011682016040523d82523d6000602084013e6110d6565b606091505b5091509150816110eb576110eb600582611d1f565b50508051600d600061110060208f018f6135e9565b6111119060a0810190608001612365565b6001600160a01b03166001600160a01b0316815260200190815260200160002054101561115257611152600660405180602001604052806000815250611d1f565b8060400151611162576001611165565b60005b816060015192509250509850989650505050505050565b7f00000000000000000000000015c7b7ce10f3a9ae63554bce7c54d0a818e967c781565b336111aa81610c54565b6111c65760405162461bcd60e51b81526004016104b190612ff8565b6001600160a01b0381166000908152600c60205260409020546111fb5760405162461bcd60e51b81526004016104b19061309d565b806001600160a01b03167f77f2d8afec4b9d82ffa0dea525320620292bd1067f575964994d5c4501479aed8686868660405161123a9493929190613570565b60405180910390a25050505050565b6000546001600160a01b031690565b600061129561128a6064611284611273602087013583611cad565b61127e888835611cd2565b90611cd2565b90611f3c565b604084013590611cad565b90505b92915050565b60065434908111156112c25760405162461bcd60e51b81526004016104b190612f98565b6001600160a01b0382166000908152600d60205260409020546112e59082611cad565b6001600160a01b0383166000818152600d6020526040908190209290925590513391907f8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a790611335908590612cad565b60405180910390a35050565b611349611d0c565b6001600160a01b031661135a611249565b6001600160a01b0316146113805760405162461bcd60e51b81526004016104b190613166565b43600e54116113a15760405162461bcd60e51b81526004016104b190612e1f565b600e8190556040517f1c0aa0c666483fbf0cf795d9d646ea3552d1e3008162ba9ab1d6d6dfd8c6ec6b906113d6908390612cad565b60405180910390a150565b336000818152600c60205260409020805483019081905560025481111561141a5760405162461bcd60e51b81526004016104b1906131d2565b61142382610c54565b61143f5760405162461bcd60e51b81526004016104b190612ff8565b60005b83811015611509576000600b8187878581811061145b57fe5b90506020020160208101906114709190612365565b6001600160a01b03908116825260208201929092526040016000205416146114aa5760405162461bcd60e51b81526004016104b190612e4b565b82600b60008787858181106114bb57fe5b90506020020160208101906114d09190612365565b6001600160a01b039081168252602082019290925260400160002080546001600160a01b03191692909116919091179055600101611442565b50816001600160a01b03167febf4a9bffb39f7c5dbf3f65540183b9381ae226ac3d0a45b4cad484713bd4a2885858460405161154793929190612c31565b60405180910390a250505050565b7f0000000000000000000000000adf62f267206ff6ead3d93f4d421f86b51c6b7d81565b611581611d0c565b6001600160a01b0316611592611249565b6001600160a01b0316146115b85760405162461bcd60e51b81526004016104b190613166565b8051600290815560208201516003556040808301516004556060830151600555608083015160065560a083015160075560c083015160085560e0830151600955610100830151600a55517f918ee002eb112844e457f37ea6b320c5572bc73957ebb0423ffcfb03d7b939d7916113d691613444565b600e5443101590565b600b602052600090815260409020546001600160a01b031681565b600e5481565b60018054604080516020600284861615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156116dc5780601f106116b1576101008083540402835291602001916116dc565b820191906000526020600020905b8154815290600101906020018083116116bf57829003601f168201915b505050505081565b336001600160a01b037f0000000000000000000000000adf62f267206ff6ead3d93f4d421f86b51c6b7d161461172c5760405162461bcd60e51b81526004016104b1906130fe565b6001600160a01b038083166000908152600b602052604090205416806117645760405162461bcd60e51b81526004016104b19061335e565b61176d81610c54565b6117895760405162461bcd60e51b81526004016104b190612ff8565b60405163c345315360e01b81526000906001600160a01b037f00000000000000000000000015c7b7ce10f3a9ae63554bce7c54d0a818e967c7169063c3453153906117d8908590600401612b3d565b60806040518083038186803b1580156117f057600080fd5b505afa158015611804573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182891906126ac565b805160405163026822bd60e21b81529192506001600160a01b037f00000000000000000000000015c7b7ce10f3a9ae63554bce7c54d0a818e967c716916309a08af49161187b9186918891600401612b9f565b600060405180830381600087803b15801561189557600080fd5b505af11580156118a9573d6000803e3d6000fd5b5050505050505050565b6118bb611d0c565b6001600160a01b03166118cc611249565b6001600160a01b0316146118f25760405162461bcd60e51b81526004016104b190613166565b6001600160a01b0381166119185760405162461bcd60e51b81526004016104b190612e82565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000828211156119955760405162461bcd60e51b81526004016104b190612fc1565b50900390565b600081600401835110156119c15760405162461bcd60e51b81526004016104b190613327565b5001602001516001600160e01b03191690565b6119dc61226d565b60006119eb60208601866135e9565b6119fc9060a0810190608001612365565b6001600160a01b031663b039a88f61c3506040518263ffffffff1660e01b815260040160806040518083038187803b158015611a3757600080fd5b5086fa158015611a4b573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611a709190612512565b6060810151909250361115611a975760405162461bcd60e51b81526004016104b1906130c7565b6000611aa236610c3c565b600a54909150858503036000368281611ab757fe5b0490506010811115611adb5760405162461bcd60e51b81526004016104b190612de8565b8451891015611afc5760405162461bcd60e51b81526004016104b1906131fc565b602085015185511015611b215760405162461bcd60e51b81526004016104b190612ec8565b611b6282611b5c8581611b348d806135d4565b60600135611b5c8b60400151611b5c8d60200151600260030154611cad90919063ffffffff16565b90611cad565b935083861015611b845760405162461bcd60e51b81526004016104b19061319b565b6000611b978561034960208c018c6135e9565b9050600d6000611baa60208c018c6135e9565b611bbb9060a0810190608001612365565b6001600160a01b03166001600160a01b0316815260200190815260200160002054811115611bfb5760405162461bcd60e51b81526004016104b1906132b9565b5050505094509492505050565b6000611c2d611c1a60208801886135e9565b611c289060c0810190613590565b611f6e565b611c48611c3a88806135d4565b611c289060a0810190613590565b611c528585611f6e565b611c5c8888611f6e565b0101016102c40190506041841115611c865760405162461bcd60e51b81526004016104b1906132f0565b368114611ca55760405162461bcd60e51b81526004016104b19061325f565b505050505050565b6000828201838110156112955760405162461bcd60e51b81526004016104b190612eff565b600082611ce157506000611298565b82820282848281611cee57fe5b04146112955760405162461bcd60e51b81526004016104b190613125565b3390565b611d1c81610400611f7f565b50565b60008282604051602001611d34929190612d5f565b6040516020818303038152906040529050611d4e81611d10565b8051602082018181fd5b60008060606000611d6887611f90565b90506000611d92611d7c60208a018a6135e9565b611d8d9060c081019060a001612365565b611fcd565b9050611da160208901896135e9565b611db29060c081019060a001612365565b6001600160a01b031663e024dc7f60e01b611dcd8a806135d4565b836040518060400160405280600c81526020016b14995b185e54995c5d595cdd60a21b8152506040518060800160405280605d81526020016136ab605d91396040518060e0016040528060a1815260200161370860a19139604051602001611e359190612b01565b60408051601f1981840301815290829052611e54939291602001612aa2565b60405160208183030381529060405280519060200120868c8c604051602401611e829695949392919061338c565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611ec09190612a86565b6000604051808303816000865af19150503d8060008114611efd576040519150601f19603f3d011682016040523d82523d6000602084013e611f02565b606091505b5090955092508415611f285782806020019051810190611f229190612441565b90945092505b611f3183611d10565b505093509350939050565b6000808211611f5d5760405162461bcd60e51b81526004016104b190613066565b818381611f6657fe5b049392505050565b6020601f8201601f19160192915050565b8082511115611f8c578082525b5050565b6060611fa7611fa260208401846135e9565b61204d565b604051602001611fb79190612cad565b6040516020818303038152906040529050919050565b6040805160c0810182526017608082019081527f47534e2052656c61796564205472616e73616374696f6e00000000000000000060a083015281528151808301835260018152601960f91b602082810191909152820152600091610c4c91908101612036612106565b8152602001846001600160a01b031681525061210a565b60006040518060e0016040528060a1815260200161370860a19139805160209182012090833590840135604085013561208c6080870160608801612365565b61209c60a0880160808901612365565b6120ac60c0890160a08a01612365565b6120b960c08a018a613590565b6040516120c7929190612a76565b6040519081900381206120e9989796959493929160e08c013590602001612ce2565b604051602081830303815290604052805190602001209050919050565b4690565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f826000015180519060200120836020015180519060200120846040015185606001516040516020016120e9959493929190612cb6565b604051806101a0016040528060001515815260200160006001600160e01b03191681526020016000815260200160608152602001606081526020016121a661226d565b8152602001600081526020016000815260200160008152602001600081526020016060815260200160006001600160a01b03168152602001600081525090565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160e08101825260008082526020820181905291810182905260608082018190526080820181905260a082015260c081019190915290565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b80518015158114610c4f57600080fd5b60008083601f8401126122b6578182fd5b5081356001600160401b038111156122cc578182fd5b6020830191508360208285010111156122e457600080fd5b9250929050565b600082601f8301126122fb578081fd5b81516001600160401b0381111561230e57fe5b612321601f8201601f19166020016135fe565b818152846020838601011115612335578283fd5b612346826020830160208701613665565b949350505050565b60006040828403121561235f578081fd5b50919050565b600060208284031215612376578081fd5b813561129581613695565b60008060408385031215612393578081fd5b823561239e81613695565b915060208301356123ae81613695565b809150509250929050565b600080602083850312156123cb578182fd5b82356001600160401b03808211156123e1578384fd5b818501915085601f8301126123f4578384fd5b813581811115612402578485fd5b8660208083028501011115612415578485fd5b60209290920196919550909350505050565b600060208284031215612438578081fd5b61129582612295565b60008060408385031215612453578182fd5b61245c83612295565b915060208301516001600160401b03811115612476578182fd5b612482858286016122eb565b9150509250929050565b6000806040838503121561249e578182fd5b82516001600160401b038111156124b3578283fd5b6124bf858286016122eb565b9250506124ce60208401612295565b90509250929050565b600080604083850312156124e9578081fd5b8251600781106124f7578182fd5b60208401519092506001600160401b03811115612476578182fd5b600060808284031215612523578081fd5b604051608081018181106001600160401b038211171561253f57fe5b8060405250825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b6000610120808385031215612583578182fd5b61258c816135fe565b9050823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e08201526101008084013581830152508091505092915050565b600080600080600080600080888a0361012081121561260b578485fd5b89356001600160401b0380821115612621578687fd5b61262d8d838e0161234e565b9a5060208c0135915080821115612642578687fd5b61264e8d838e016122a5565b909a50985060408c0135915080821115612666578687fd5b506126738c828d016122a5565b9097509550506080605f198201121561268a578384fd5b5096999598509396929550909360608301935060e08301359261010001359150565b6000608082840312156126bd578081fd5b604051608081018181106001600160401b03821117156126d957fe5b8060405250825181526020830151602082015260408301516040820152606083015161270481613695565b60608201529392505050565b600060208284031215612721578081fd5b5035919050565b6000806040838503121561273a578182fd5b8235915060208301356123ae81613695565b6000806040838503121561275e578182fd5b8235915060208301356001600160401b0381111561277a578182fd5b830161010081860312156123ae578182fd5b600080600080600080600060a0888a0312156127a6578081fd5b8735965060208801356001600160401b03808211156127c3578283fd5b6127cf8b838c0161234e565b975060408a01359150808211156127e4578283fd5b6127f08b838c016122a5565b909750955060608a0135915080821115612808578283fd5b506128158a828b016122a5565b989b979a50959894979596608090950135949350505050565b60008060008060608587031215612843578182fd5b843593506020850135925060408501356001600160401b03811115612866578283fd5b612872878288016122a5565b95989497509550505050565b6001600160a01b03169052565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b600081518084526128cd816020860160208601613665565b601f01601f19169290920160200192915050565b600781106128eb57fe5b9052565b600081356128fc81613695565b6001600160a01b03908116845260208301359061291882613695565b16602084015260408281013590840152606080830135908401526080808301359084015261294960a0830183613621565b60e060a086015261295e60e08601828461288b565b91505060c083013560c08501528091505092915050565b600061010082358452602083013560208501526040830135604085015260608301356129a081613695565b6001600160a01b0390811660608601526080840135906129bf82613695565b16608085015260a08301356129d381613695565b6129e060a086018261287e565b506129ee60c0840184613621565b8260c0870152612a01838701828461288b565b9250505060e083013560e08501528091505092915050565b6000813582360360de1981018212612a2f578283fd5b60408552612a42604086018584016128ef565b9150602084013560fe1982018112612a58578384fd5b8583036020870152612a6c83868301612975565b9695505050505050565b6000828483379101908152919050565b60008251612a98818460208701613665565b9190910192915050565b60008451612ab4818460208901613665565b600560fb1b9083019081528451612ad2816001840160208901613665565b600b60fa1b600192909101918201528351612af4816002840160208801613665565b0160020195945050505050565b60007352656c6179446174612072656c6179446174612960601b82528251612b30816014850160208701613665565b9190910160140192915050565b6001600160a01b0391909116815260200190565b6001600160a01b038681168252851660208201526001600160e01b0319841660408201526060810183905260a060808201819052600090612b94908301846128b5565b979650505050505050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b038681168252851660208201526001600160e01b03198416604082015260a08101612bf860608301856128e1565b8260808301529695505050505050565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6040808252810183905260008460608301825b86811015612c74578235612c5781613695565b6001600160a01b0316825260209283019290910190600101612c44565b5060209390930193909352509392505050565b901515815260200190565b600083151582526040602083015261234660408301846128b5565b90815260200190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b9889526020890197909752604088019590955260608701939093526001600160a01b039182166080870152811660a08601521660c084015260e08301526101008201526101200190565b600060808252612d3f60808301876128b5565b85151560208401528460408401528281036060840152612b948185612975565b6000612d6b82856128e1565b6040602083015261234660408301846128b5565b60006020825261129560208301846128b5565b602080825260149082015273125b5c1bdcdcda589b194819d85cc81b1a5b5a5d60621b604082015260600190565b6020808252600e908201526d1a1d588819195c1c9958d85d195960921b604082015260600190565b60208082526018908201527f696e76616c69642065787465726e616c4761734c696d69740000000000000000604082015260600190565b602080825260129082015271105b1c9958591e4819195c1c9958d85d195960721b604082015260600190565b60208082526019908201527f7468697320776f726b6572206861732061206d616e6167657200000000000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526019908201527f616363657074616e63652062756467657420746f6f206c6f7700000000000000604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252601a908201527f4d7573742062652063616c6c65642062792052656c6179487562000000000000604082015260600190565b602080825260119082015270496e76616c69642067617320707269636560781b604082015260600190565b6020808252600f908201526e6465706f73697420746f6f2062696760881b604082015260600190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b60208082526018908201527f72656c6179206d616e61676572206e6f74207374616b65640000000000000000604082015260600190565b60208082526018908201527f72656c617920776f726b6572206d75737420626520454f410000000000000000604082015260600190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b60208082526010908201526f6e6f2072656c617920776f726b65727360801b604082015260600190565b60208082526017908201527f6d73672e64617461206578636565646564206c696d6974000000000000000000604082015260600190565b6020808252600d908201526c2737ba103832b730b634bd32b960991b604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526019908201527f6e6f2067617320666f7220696e6e657252656c617943616c6c00000000000000604082015260600190565b60208082526010908201526f746f6f206d616e7920776f726b65727360801b604082015260600190565b6020808252601a908201527f616363657074616e63652062756467657420746f6f2068696768000000000000604082015260600190565b602080825260129082015271696e73756666696369656e742066756e647360701b604082015260600190565b6020808252601490820152736578747261206d73672e6461746120627974657360601b604082015260600190565b6020808252601290820152712737ba1030903934b3b43a103bb7b935b2b960711b604082015260600190565b60208082526019908201527f5061796d61737465722062616c616e636520746f6f206c6f7700000000000000604082015260600190565b60208082526018908201527f696e76616c6964207369676e6174757265206c656e6774680000000000000000604082015260600190565b6020808252601a908201527f726561644279746573343a206461746120746f6f2073686f7274000000000000604082015260600190565b6020808252601490820152732ab735b737bbb7103932b630bc903bb7b935b2b960611b604082015260600190565b600060a0825261339f60a08301896128ef565b87602084015286604084015282810360608401526133bd81876128b5565b905082810360808401526133d281858761288b565b9998505050505050505050565b600061012082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525092915050565b8154815260018201546020820152600282015460408201526003820154606082015260048201546080820152600582015460a0820152600682015460c0820152600782015460e08201526008909101546101008201526101200190565b60006101208083526134b58184018c612a19565b905082810360208401526134ca818a8c61288b565b905082810360408401526134df81888a61288b565b9150508451606083015260208501516080830152604085015160a0830152606085015160c08301528360e0830152826101008301529998505050505050505050565b6000608082526135346080830189612a19565b828103602084015261354781888a61288b565b9050828103604084015261355c81868861288b565b915050826060830152979650505050505050565b600085825284602083015260606040830152612a6c60608301848661288b565b6000808335601e198436030181126135a6578283fd5b8301803591506001600160401b038211156135bf578283fd5b6020019150368190038213156122e457600080fd5b6000823560de19833603018112612a98578182fd5b6000823560fe19833603018112612a98578182fd5b6040518181016001600160401b038111828210171561361957fe5b604052919050565b6000808335601e19843603018112613637578283fd5b83016020810192503590506001600160401b0381111561365657600080fd5b8036038313156122e457600080fd5b60005b83811015613680578181015183820152602001613668565b8381111561368f576000848401525b50505050565b6001600160a01b0381168114611d1c57600080fdfe616464726573732066726f6d2c6164647265737320746f2c75696e743235362076616c75652c75696e74323536206761732c75696e74323536206e6f6e63652c627974657320646174612c75696e743235362076616c6964556e74696c52656c6179446174612875696e743235362067617350726963652c75696e743235362070637452656c61794665652c75696e74323536206261736552656c61794665652c616464726573732072656c6179576f726b65722c61646472657373207061796d61737465722c6164647265737320666f727761726465722c6279746573207061796d6173746572446174612c75696e7432353620636c69656e74496429a2646970667358221220f3aa7edd3470b3c75e5cd253b2b645a290b4c98d15b18abe5d24ca659075d9a264736f6c63430007060033

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

00000000000000000000000015c7b7ce10f3a9ae63554bce7c54d0a818e967c70000000000000000000000000adf62f267206ff6ead3d93f4d421f86b51c6b7d000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000003ada000000000000000000000000000000000000000000000000000000000000816f0000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000578e

-----Decoded View---------------
Arg [0] : _stakeManager (address): 0x15C7B7CE10f3A9AE63554bCE7C54d0a818E967C7
Arg [1] : _penalizer (address): 0x0aDF62f267206ff6EAD3d93f4d421f86b51C6B7D
Arg [2] : _maxWorkerCount (uint256): 10
Arg [3] : _gasReserve (uint256): 100000
Arg [4] : _postOverhead (uint256): 15066
Arg [5] : _gasOverhead (uint256): 33135
Arg [6] : _maximumRecipientDeposit (uint256): 2000000000000000000
Arg [7] : _minimumUnstakeDelay (uint256): 1000
Arg [8] : _minimumStake (uint256): 1000000000000000000
Arg [9] : _dataGasCostPerByte (uint256): 13
Arg [10] : _externalCallDataCostOverhead (uint256): 22414

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 00000000000000000000000015c7b7ce10f3a9ae63554bce7c54d0a818e967c7
Arg [1] : 0000000000000000000000000adf62f267206ff6ead3d93f4d421f86b51c6b7d
Arg [2] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [3] : 00000000000000000000000000000000000000000000000000000000000186a0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000003ada
Arg [5] : 000000000000000000000000000000000000000000000000000000000000816f
Arg [6] : 0000000000000000000000000000000000000000000000001bc16d674ec80000
Arg [7] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [8] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [9] : 000000000000000000000000000000000000000000000000000000000000000d
Arg [10] : 000000000000000000000000000000000000000000000000000000000000578e


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

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