Contract 0x6C28AfC105e65782D9Ea6F2cA68df84C9e7d750d 2

 
 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xd8011b3ed33707f2555faa248b723d111a695ff9d7c65d0e6b75b98a7666e74cRegister Relay S...339721612022-10-05 18:35:155 mins ago0x555bbf8d0289e48e9aeb7603d45829c6349920c8 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00138220204630.673339992
0x90410790b16a6786399f7dacd0bb12f17b105b95cffb0e2d6cad0c5699e96401Register Relay S...339720262022-10-05 18:30:3310 mins ago0x120b8b4268f942aa8ef4b61916f8748ede87a2e3 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00138183396630.673339992
0xabf24dd12662c5ff09eea7c7fcd9ce48767e77f065b638f89905f26c8acd2b30Register Relay S...339717182022-10-05 18:20:0121 mins ago0x3084eb5126c6ad691d59f4007b36c50eaf43b1dd IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00134898000130.000000023
0x1a59ad2295abeb5ea843dc4d98250340c650537073b169d773ee74ee159877fcRelay Call339716432022-10-05 18:17:2323 mins ago0x042e6ba26ebcc0e4592fd2a69ea89c0b9ac2a362 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00985575600336.000000014
0x71a3c7d88d1ca7e7b84f54eb3807b207fe23c4b548842f6804be8cdb23e93ffcRegister Relay S...339716372022-10-05 18:17:1123 mins ago0x05c883c417b0e09925318e070de220422665ca86 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.0013493430.000000016
0xcd931dd341c9aec504b30f2e77d1250dc6b7de53311298e437c4204a91e737f8Register Relay S...339715192022-10-05 18:13:0727 mins ago0x4fd765ea4d6504d6a45a0b2c99f2a2dce1ae8d8d IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.001354741630.000035447
0x52daa651922443809f2f8a105d1a00295e8bd358e0e008cd669b2bc3ee999b55Register Relay S...339714692022-10-05 18:11:2729 mins ago0xc17cb17c4c6673a9480f612ecd9a7cf170d9455e IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.0013365630.000000015
0xc39baf885cf2bea899dd94e9b58d1821ed916af06387ab62747713df41e745caRelay Call339713392022-10-05 18:06:5934 mins ago0x0f04812cb6b03398422fe70c0dcee5c4821cac62 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00966510000436.000000017
0x3ed8f742aac70ea4964cf98f7794bc3663df854a17942f45ee37eb564217be09Register Relay S...339711552022-10-05 18:00:3940 mins ago0x555bbf8d0289e48e9aeb7603d45829c6349920c8 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00139692352731.000033893
0x41febed6178ae7935674ae9bd497837da3c5c0daedfa71ed48dc0e7a0eb1f94dRegister Relay S...339710202022-10-05 17:56:0145 mins ago0x120b8b4268f942aa8ef4b61916f8748ede87a2e3 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00135150189530.000042082
0x10136950dc947e1740b263804f98c8bbdc5c93e711e37f189c8d39240d7576dfRegister Relay S...339707142022-10-05 17:45:2855 mins ago0x3084eb5126c6ad691d59f4007b36c50eaf43b1dd IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.0013489830.000000013
0x74a1e0cd41eed9f469e0c8e8c8d40cf02a2e96b42ac6e81e6a4ea50558b1f368Register Relay S...339706302022-10-05 17:42:3658 mins ago0x05c883c417b0e09925318e070de220422665ca86 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00134934189230.000042082
0x6d89f1bc720cee962726dbb48779fd9ca5921ee18e1275e2e4b9a8d3f1711a82Register Relay S...339705142022-10-05 17:38:361 hr 2 mins ago0x4fd765ea4d6504d6a45a0b2c99f2a2dce1ae8d8d IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.001354741930.000042082
0x49bbd055cf791266f27528a18e435b23d591981e091d7af50d0f5a227c2ef711Register Relay S...339704632022-10-05 17:36:501 hr 4 mins ago0xc17cb17c4c6673a9480f612ecd9a7cf170d9455e IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.0013365630.000000013
0x867fb5199c87607943b4735e04e7b32d6c629f059162bf442f92842ea5bf33caRegister Relay S...339701512022-10-05 17:26:051 hr 14 mins ago0x555bbf8d0289e48e9aeb7603d45829c6349920c8 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00135186189630.000042082
0xebb9ca01a88fc2f4c1da7154f22d8ea66b7f54501e66784df68ddeab9b5777f8Relay Call339700342022-10-05 17:22:031 hr 19 mins ago0x0f04812cb6b03398422fe70c0dcee5c4821cac62 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00818938800336.000000016
0xdcf8234131a4984a9f4a9b12d4c8a7b6b56e6c184178622e4559591ddb7e3a6eRegister Relay S...339700172022-10-05 17:21:291 hr 19 mins ago0x120b8b4268f942aa8ef4b61916f8748ede87a2e3 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00135150189530.000042082
0x55914f183cccf33bacbeff4d6fcb6f3c1763dec76f3d4998c3de0a89d626ce23Relay Call339700022022-10-05 17:20:591 hr 20 mins ago0x0f04812cb6b03398422fe70c0dcee5c4821cac62 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00752270400336.000000019
0x5ceb568dc531dcea19ef8d84dae9bc56c77ab40a6b57eefbca774e883889e7c9Register Relay S...339697112022-10-05 17:10:571 hr 30 mins ago0x3084eb5126c6ad691d59f4007b36c50eaf43b1dd IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00134898000130.000000033
0x21fdbc9620f68654b0f2c0f4dce49574f5ec4d248c41b8fb6fc7294f3bda1141Register Relay S...339696262022-10-05 17:08:031 hr 33 mins ago0x05c883c417b0e09925318e070de220422665ca86 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00134934189230.000042082
0xa753b6fb9fe8f8ce3d221cfaa2f956e5e82b333108dcd8ad576c5762dfea77f9Register Relay S...339695102022-10-05 17:04:031 hr 37 mins ago0x4fd765ea4d6504d6a45a0b2c99f2a2dce1ae8d8d IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.001354741930.000042082
0x263ca270ac418fd8095ce3db86de1c73893f52634f60027159ecac1f92578946Register Relay S...339694262022-10-05 17:01:111 hr 39 mins ago0xc17cb17c4c6673a9480f612ecd9a7cf170d9455e IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.0013365630.000000017
0xfaed1ba198544e03b84c84743e79d14677d0c2b127a5836136a7e5729867647fRelay Call339692802022-10-05 16:56:111 hr 44 mins ago0x41a9db7fead35cfc17b334bc112401ffe6427656 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00794516400636.00000003
0xa463a7f19dbe172239beaaf185f381830ac34f6128dc8dba61c4633f45dceb6dRegister Relay S...339691482022-10-05 16:51:351 hr 49 mins ago0x555bbf8d0289e48e9aeb7603d45829c6349920c8 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00135186189630.000042082
0x68bbe355c55fb384accbc6e691468c000f81dbcfbd7aa935d72baa8da3fbd18cRegister Relay S...339690132022-10-05 16:46:571 hr 54 mins ago0x120b8b4268f942aa8ef4b61916f8748ede87a2e3 IN  0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0 MATIC0.00135150189530.000042082
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xbe98e9bd60843149e50dbe0103425aa971fdb69318e021cd6169c6cba5520913339519012022-10-05 6:51:3411 hrs 49 mins ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.031248237912309911 MATIC
0xaab2ec565aed5ea6f5b05cd0f2884efe29a26da23ce69b50da8b2d3a04443177339512212022-10-05 6:28:1012 hrs 12 mins ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.025406188511548267 MATIC
0x2e9d88fe4287d86a756ab8d59805611ade6744285a49efc833c1e657d1b46230339508522022-10-05 6:15:3212 hrs 25 mins ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.067128864028478912 MATIC
0x26f1ef9c5cc42a590c429dc9c70bb2a739a3493fb2d4013cea886dd4c7794bb8339500642022-10-05 5:46:2212 hrs 54 mins ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.025437261310791565 MATIC
0xb10abb43c73097b6ade70832750724fac9fd972fb105ab4b9af308be2755feb8339492652022-10-05 5:16:4813 hrs 24 mins ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.021962117109317261 MATIC
0xc8b2b4d87d973df73edab36c85fd2bda6b5993eb213a028a203426f84eb80fdd339485602022-10-05 4:52:3413 hrs 48 mins ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.025425935718491589 MATIC
0xb87d7aea24ac0ea6064f6ec3ebb85a9b6e7630cf6c566ac3b9974efad0294c31339389672022-10-04 23:22:3119 hrs 18 mins ago 0xc9bdcd4e59a14f390b48f4afabc4ee68f071dae7 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d1 MATIC
0xddc1d1e90ddb40bc3310b66f132c1b4942152d65c95a6cf98d3a3a4e883ffbc3339159122022-10-04 10:06:501 day 8 hrs ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.029185225070576949 MATIC
0xd4a4901b616b7b5a30ff62d80102fe52cf2ec738a1386209cdd9bd1b60e3d1c3339125802022-10-04 8:11:381 day 10 hrs ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.021912712815936518 MATIC
0x7b383ea2d063a48d8d647264488ae7d93395893d73409da81d2c76463ae69d4c339119372022-10-04 7:49:191 day 10 hrs ago 0x8f9314936e23f0a7dabb0a43950d6455e4cf1df8 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.021961004211395749 MATIC
0xcca3d5a8655640ca45de90ac714db9f968e1dc4af61bad9cf188b4b58e2397fa338723332022-10-03 9:01:392 days 9 hrs ago 0x2d357877e55697cf30404ae835e0702648e75df6 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d1 MATIC
0xb3a16c67b5a813bc28bbba2c5a1eec6cacea86a488010204d8e060dc6f9a875c338704082022-10-03 7:53:212 days 10 hrs ago 0x39252bf0941bc29e9e39644952a0744939b1e1e1 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.05166495512348407 MATIC
0xae146bd485ff887b030ddd3049da25d8504771b8a27450d2666a99dd39f10655338697722022-10-03 7:31:292 days 11 hrs ago 0x39252bf0941bc29e9e39644952a0744939b1e1e1 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.028625272515613785 MATIC
0xff552f4243f74475d0f7e3806a612e384e0f91323f2cd2163bb08318d61ac169338697202022-10-03 7:29:412 days 11 hrs ago 0x39252bf0941bc29e9e39644952a0744939b1e1e1 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.021270856210313142 MATIC
0xacdc8791e954c2e822f55a22e045bb5490f385764f02836e3ca730fe8c7a52d2338696782022-10-03 7:28:132 days 11 hrs ago 0x39252bf0941bc29e9e39644952a0744939b1e1e1 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.012845372108952835 MATIC
0x5094bc5bd1f51aebd07fa48f0a1e7af1fafc222fbe5c539b28470d30e3296691338678402022-10-03 6:22:532 days 12 hrs ago 0x39252bf0941bc29e9e39644952a0744939b1e1e1 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.008321943556267449 MATIC
0xdd99c98416ad5fab2d3207eb1f13893aa811d68804f4b3532449ddfe11157a6e338615532022-10-03 2:42:252 days 15 hrs ago 0x2d357877e55697cf30404ae835e0702648e75df6 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d1 MATIC
0x36d28b2767f1eb3c1c65c6b7f31a82bb89c428ba3f2910192b57d3bc2917fbf4337723372022-09-30 23:12:034 days 19 hrs ago 0xe109473ae1be88830e49ca1b31414e9f6738a9ac 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.022957217196993981 MATIC
0xd7188b9b049e00e6570e532cae271bc7aadada8f8a1c095230275743a34264e1337722622022-09-30 23:09:294 days 19 hrs ago 0xe109473ae1be88830e49ca1b31414e9f6738a9ac 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.021964014571675133 MATIC
0xda09101a3458b6e1efefd129b20a0a9c487816cc29507ea6b1eb82926f88f368337721822022-09-30 23:06:454 days 19 hrs ago 0xe109473ae1be88830e49ca1b31414e9f6738a9ac 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.023278856068671904 MATIC
0x58ffd8177e2134aa0be78dd42ee2802da93efe0b9fd41e8a7c806c807b539f5d337721402022-09-30 23:05:174 days 19 hrs ago 0xe109473ae1be88830e49ca1b31414e9f6738a9ac 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.023789329660695909 MATIC
0x89c324f94dc29a76618faec3a9e5f749496dfaf4d2ea4c2e5c1429f51a112818337720902022-09-30 23:03:334 days 19 hrs ago 0xe109473ae1be88830e49ca1b31414e9f6738a9ac 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.034300305612472838 MATIC
0x40ba175a37715e4a763d9370bdf0608009af4982cf1c40590b4a2877d7539ca6337717862022-09-30 22:51:014 days 19 hrs ago 0xe109473ae1be88830e49ca1b31414e9f6738a9ac 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.020318380509235627 MATIC
0x77c0d3a56144a8618fa18bd74eebec568507c5dd9251d5c61fec1ce2077e04a9337716482022-09-30 22:46:174 days 19 hrs ago 0xe109473ae1be88830e49ca1b31414e9f6738a9ac 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.017641509607484276 MATIC
0xe71bc19ff10bfb9bc853faf1f9cfdaca2dfe6754fdd446ba8b30e85b9a3c8bc5337716052022-09-30 22:44:474 days 19 hrs ago 0xe109473ae1be88830e49ca1b31414e9f6738a9ac 0x6c28afc105e65782d9ea6f2ca68df84c9e7d750d0.017835279006485556 MATIC
[ Download CSV Export 
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

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 Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.