Contract Overview
Balance:
0.810510384314752619 ETH
Token:
My Name Tag:
Not Available
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
RelayHub
Compiler Version
v0.8.7+commit.e28d00a7
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract 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() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.2) (utils/introspection/ERC165Checker.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Library used to query support of an interface declared via {IERC165}. * * Note that these functions return the actual result of the query: they do not * `revert` if an interface is not supported. It is up to the caller to decide * what to do in these cases. */ library ERC165Checker { // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; /** * @dev Returns true if `account` supports the {IERC165} interface, */ function supportsERC165(address account) internal view returns (bool) { // Any contract that implements ERC165 must explicitly indicate support of // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid return _supportsERC165Interface(account, type(IERC165).interfaceId) && !_supportsERC165Interface(account, _INTERFACE_ID_INVALID); } /** * @dev Returns true if `account` supports the interface defined by * `interfaceId`. Support for {IERC165} itself is queried automatically. * * See {IERC165-supportsInterface}. */ function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { // query support of both ERC165 as per the spec and support of _interfaceId return supportsERC165(account) && _supportsERC165Interface(account, interfaceId); } /** * @dev Returns a boolean array where each value corresponds to the * interfaces passed in and whether they're supported or not. This allows * you to batch check interfaces for a contract where your expectation * is that some interfaces may not be supported. * * See {IERC165-supportsInterface}. * * _Available since v3.4._ */ function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) { // an array of booleans corresponding to interfaceIds and whether they're supported or not bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); // query support of ERC165 itself if (supportsERC165(account)) { // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]); } } return interfaceIdsSupported; } /** * @dev Returns true if `account` supports all the interfaces defined in * `interfaceIds`. Support for {IERC165} itself is queried automatically. * * Batch-querying can lead to gas savings by skipping repeated checks for * {IERC165} support. * * See {IERC165-supportsInterface}. */ function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { // query support of ERC165 itself if (!supportsERC165(account)) { return false; } // query support of each interface in _interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { if (!_supportsERC165Interface(account, interfaceIds[i])) { return false; } } // all interfaces supported return true; } /** * @notice Query if a contract implements an interface, does not check ERC165 support * @param account The address of the contract to query for support of an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract at account indicates support of the interface with * identifier interfaceId, false otherwise * @dev Assumes that account contains a contract that supports ERC165, otherwise * the behavior of this method is undefined. This precondition can be checked * with {supportsERC165}. * Interface identification is specified in ERC-165. */ function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) { // prepare call bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); // perform static call bool success; uint256 returnSize; uint256 returnValue; assembly { success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20) returnSize := returndatasize() returnValue := mload(0x00) } return success && returnSize >= 0x20 && returnValue > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`. // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`. // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a // good first aproximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1; uint256 x = a; if (x >> 128 > 0) { x >>= 128; result <<= 64; } if (x >> 64 > 0) { x >>= 64; result <<= 32; } if (x >> 32 > 0) { x >>= 32; result <<= 16; } if (x >> 16 > 0) { x >>= 16; result <<= 8; } if (x >> 8 > 0) { x >>= 8; result <<= 4; } if (x >> 4 > 0) { x >>= 4; result <<= 2; } if (x >> 2 > 0) { result <<= 1; } // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { uint256 result = sqrt(a); if (rounding == Rounding.Up && result * result < a) { result += 1; } return result; } }
pragma solidity ^0.8.0; pragma abicoder v2; /* 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: GPL-3.0-only import "./utils/MinLibBytes.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./utils/GsnUtils.sol"; import "./utils/GsnEip712Library.sol"; import "./utils/RelayHubValidator.sol"; import "./utils/GsnTypes.sol"; import "./interfaces/IRelayHub.sol"; import "./interfaces/IPaymaster.sol"; import "./forwarder/IForwarder.sol"; import "./interfaces/IStakeManager.sol"; import "./interfaces/IRelayRegistrar.sol"; import "./interfaces/IStakeManager.sol"; /** * @title The RelayHub Implementation * @notice This contract implements the `IRelayHub` interface for the EVM-compatible networks. */ contract RelayHub is IRelayHub, Ownable, ERC165 { using ERC165Checker for address; using Address for address; address private constant DRY_RUN_ADDRESS = 0x0000000000000000000000000000000000000000; /// @inheritdoc IRelayHub function versionHub() override virtual public pure returns (string memory){ return "3.0.0-beta.3+opengsn.hub.irelayhub"; } IStakeManager internal immutable stakeManager; address internal immutable penalizer; address internal immutable batchGateway; address internal immutable relayRegistrar; RelayHubConfig internal config; /// @inheritdoc IRelayHub function getConfiguration() public override view returns (RelayHubConfig memory) { return config; } /// @inheritdoc IRelayHub function setConfiguration(RelayHubConfig memory _config) public override onlyOwner { require(_config.devFee < 100, "dev fee too high"); config = _config; emit RelayHubConfigured(config); } // maps ERC-20 token address to a minimum stake for it mapping(IERC20 => uint256) internal minimumStakePerToken; /// @inheritdoc IRelayHub function setMinimumStakes(IERC20[] memory token, uint256[] memory minimumStake) public override onlyOwner { require(token.length == minimumStake.length, "setMinimumStakes: wrong length"); for (uint256 i = 0; i < token.length; i++) { minimumStakePerToken[token[i]] = minimumStake[i]; emit StakingTokenDataChanged(address(token[i]), minimumStake[i]); } } // maps relay worker's address to its manager's address mapping(address => address) internal workerToManager; // maps relay managers to the number of their workers mapping(address => uint256) internal workerCount; mapping(address => uint256) internal balances; uint256 internal immutable creationBlock; uint256 internal deprecationTime = type(uint256).max; constructor ( IStakeManager _stakeManager, address _penalizer, address _batchGateway, address _relayRegistrar, RelayHubConfig memory _config ) { creationBlock = block.number; stakeManager = _stakeManager; penalizer = _penalizer; batchGateway = _batchGateway; relayRegistrar = _relayRegistrar; setConfiguration(_config); } /// @inheritdoc IRelayHub function getCreationBlock() external override virtual view returns (uint256){ return creationBlock; } /// @inheritdoc IRelayHub function getDeprecationTime() external override view returns (uint256) { return deprecationTime; } /// @inheritdoc IRelayHub function getStakeManager() external override view returns (IStakeManager) { return stakeManager; } /// @inheritdoc IRelayHub function getPenalizer() external override view returns (address) { return penalizer; } /// @inheritdoc IRelayHub function getBatchGateway() external override view returns (address) { return batchGateway; } /// @inheritdoc IRelayHub function getRelayRegistrar() external override view returns (address) { return relayRegistrar; } /// @inheritdoc IRelayHub function getMinimumStakePerToken(IERC20 token) external override view returns (uint256) { return minimumStakePerToken[token]; } /// @inheritdoc IRelayHub function getWorkerManager(address worker) external override view returns (address) { return workerToManager[worker]; } /// @inheritdoc IRelayHub function getWorkerCount(address manager) external override view returns (uint256) { return workerCount[manager]; } /// @inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { return interfaceId == type(IRelayHub).interfaceId || interfaceId == type(Ownable).interfaceId || super.supportsInterface(interfaceId); } /// @inheritdoc IRelayHub function onRelayServerRegistered(address relayManager) external override { require(msg.sender == relayRegistrar, "caller is not relay registrar"); verifyRelayManagerStaked(relayManager); require(workerCount[relayManager] > 0, "no relay workers"); stakeManager.updateRelayKeepaliveTime(relayManager); } /// @inheritdoc IRelayHub 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"); verifyRelayManagerStaked(relayManager); 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); } /// @inheritdoc IRelayHub function depositFor(address target) public virtual override payable { require(target.supportsInterface(type(IPaymaster).interfaceId), "target is not a valid IPaymaster"); uint256 amount = msg.value; balances[target] = balances[target] + amount; emit Deposited(target, msg.sender, amount); } /// @inheritdoc IRelayHub function balanceOf(address target) external override view returns (uint256) { return balances[target]; } /// @inheritdoc IRelayHub function withdraw(address payable dest, uint256 amount) public override { uint256[] memory amounts = new uint256[](1); address payable[] memory destinations = new address payable[](1); amounts[0] = amount; destinations[0] = dest; withdrawMultiple(destinations, amounts); } /// @inheritdoc IRelayHub function withdrawMultiple(address payable[] memory dest, uint256[] memory amount) public override { address payable account = payable(msg.sender); for (uint256 i = 0; i < amount.length; i++) { uint256 balance = balances[account]; require(balance >= amount[i], "insufficient funds"); balances[account] = balance - amount[i]; (bool success, ) = dest[i].call{value: amount[i]}(""); require(success, "Transfer failed."); emit Withdrawn(account, dest[i], amount[i]); } } function verifyGasAndDataLimits( uint256 maxAcceptanceBudget, GsnTypes.RelayRequest calldata relayRequest, uint256 initialGasLeft ) 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" ); require(maxAcceptanceBudget >= gasAndDataLimits.acceptanceBudget, "acceptance budget too high"); require(gasAndDataLimits.acceptanceBudget >= gasAndDataLimits.preRelayedCallGasLimit, "acceptance budget too low"); maxPossibleGas = relayRequest.relayData.transactionCalldataGasUsed + initialGasLeft; 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 innerGasLimit; uint256 gasBeforeInner; uint256 gasUsed; uint256 devCharge; bytes retData; address relayManager; bytes32 relayRequestId; uint256 tmpInitialGas; bytes relayCallStatus; } /// @inheritdoc IRelayHub function relayCall( string calldata domainSeparatorName, uint256 maxAcceptanceBudget, GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature, bytes calldata approvalData ) external override returns ( bool paymasterAccepted, uint256 charge, IRelayHub.RelayCallStatus status, bytes memory returnValue) { RelayCallData memory vars; vars.initialGasLeft = aggregateGasleft(); vars.relayRequestId = GsnUtils.getRelayRequestID(relayRequest, signature); require(!isDeprecated(), "hub deprecated"); vars.functionSelector = relayRequest.request.data.length>=4 ? MinLibBytes.readBytes4(relayRequest.request.data, 0) : bytes4(0); if (msg.sender != batchGateway && tx.origin != DRY_RUN_ADDRESS) { require(signature.length != 0, "missing signature or bad gateway"); require(msg.sender == tx.origin, "relay worker must be EOA"); require(msg.sender == relayRequest.relayData.relayWorker, "Not a right worker"); } if (tx.origin != DRY_RUN_ADDRESS) { vars.relayManager = workerToManager[relayRequest.relayData.relayWorker]; require(vars.relayManager != address(0), "Unknown relay worker"); verifyRelayManagerStaked(vars.relayManager); } (vars.gasAndDataLimits, vars.maxPossibleGas) = verifyGasAndDataLimits(maxAcceptanceBudget, relayRequest, vars.initialGasLeft); RelayHubValidator.verifyTransactionPacking(domainSeparatorName,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. vars.innerGasLimit = gasleft()*63/64- config.gasReserve; vars.gasBeforeInner = aggregateGasleft(); /* Preparing to calculate "gasUseWithoutPost": MPG = calldataGasUsage + vars.initialGasLeft :: max possible gas, an approximate gas limit for the current transaction GU1 = MPG - gasleft(called right before innerRelayCall) :: gas actually used by current transaction until that point GU2 = innerGasLimit - gasleft(called inside the innerRelayCall just before preRelayedCall) :: gas actually used by innerRelayCall before calling postRelayCall GWP1 = GU1 + GU2 :: gas actually used by the entire transaction before calling postRelayCall TGO = config.gasOverhead + config.postOverhead :: extra that will be added to the charge to cover hidden costs GWP = GWP1 + TGO :: transaction "gas used without postRelayCall" */ vars.tmpInitialGas = relayRequest.relayData.transactionCalldataGasUsed + vars.initialGasLeft + vars.innerGasLimit + 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. (vars.success, vars.relayCallStatus) = address(this).call{gas:vars.innerGasLimit}( abi.encodeWithSelector(RelayHub.innerRelayCall.selector, domainSeparatorName, relayRequest, signature, approvalData, vars.gasAndDataLimits, vars.tmpInitialGas - aggregateGasleft(), /* totalInitialGas */ vars.maxPossibleGas ) ); vars.innerGasUsed = vars.gasBeforeInner-aggregateGasleft(); (vars.status, vars.relayedCallReturnValue) = abi.decode(vars.relayCallStatus, (RelayCallStatus, bytes)); if ( vars.relayedCallReturnValue.length>0 ) { emit TransactionResult(vars.status, vars.relayedCallReturnValue); } } { if (!vars.success) { //Failure cases where the PM doesn't pay if (vars.status == RelayCallStatus.RejectedByPreRelayed || (vars.innerGasUsed <= vars.gasAndDataLimits.acceptanceBudget + relayRequest.relayData.transactionCalldataGasUsed) && ( vars.status == RelayCallStatus.RejectedByForwarder || vars.status == RelayCallStatus.RejectedByRecipientRevert //can only be thrown if rejectOnRecipientRevert==true )) { emit TransactionRejectedByPaymaster( vars.relayManager, relayRequest.relayData.paymaster, vars.relayRequestId, relayRequest.request.from, relayRequest.request.to, msg.sender, vars.functionSelector, vars.innerGasUsed, vars.relayedCallReturnValue); return (false, 0, vars.status, vars.relayedCallReturnValue); } } // We now perform the actual charge calculation, based on the measured gas used vars.gasUsed = relayRequest.relayData.transactionCalldataGasUsed + (vars.initialGasLeft - aggregateGasleft()) + config.gasOverhead; charge = calculateCharge(vars.gasUsed, relayRequest.relayData); vars.devCharge = calculateDevCharge(charge); balances[relayRequest.relayData.paymaster] = balances[relayRequest.relayData.paymaster] - charge; balances[vars.relayManager] = balances[vars.relayManager] + (charge - vars.devCharge); if (vars.devCharge > 0) { // save some gas in case of zero dev charge balances[config.devAddress] = balances[config.devAddress] + vars.devCharge; } { address from = relayRequest.request.from; address to = relayRequest.request.to; address paymaster = relayRequest.relayData.paymaster; emit TransactionRelayed( vars.relayManager, msg.sender, vars.relayRequestId, from, to, paymaster, vars.functionSelector, vars.status, charge); } // avoid variable size memory copying after gas calculation completed on-chain if (tx.origin == DRY_RUN_ADDRESS) { return (true, charge, vars.status, vars.relayedCallReturnValue); } return (true, charge, vars.status, ""); } } struct InnerRelayCallData { uint256 initialGasLeft; uint256 gasUsedToCallInner; uint256 balanceBefore; bytes32 preReturnValue; bool relayedCallSuccess; bytes relayedCallReturnValue; bytes recipientContext; bytes data; bool rejectOnRecipientRevert; } /** * @notice This method can only by called by this `RelayHub`. * It wraps the execution of the `RelayRequest` in a revertable frame context. */ function innerRelayCall( string calldata domainSeparatorName, 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; vars.initialGasLeft = aggregateGasleft(); vars.gasUsedToCallInner = totalInitialGas - gasleft(); // 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(domainSeparatorName, 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, vars.gasUsedToCallInner + (vars.initialGasLeft - aggregateGasleft()), /*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) } } /// @inheritdoc IRelayHub function calculateDevCharge(uint256 charge) public override virtual view returns (uint256){ if (config.devFee == 0){ // save some gas in case of zero dev charge return 0; } unchecked { return charge * config.devFee / 100; } } /// @inheritdoc IRelayHub function calculateCharge(uint256 gasUsed, GsnTypes.RelayData calldata relayData) public override virtual view returns (uint256) { uint256 basefee; if (relayData.maxFeePerGas == relayData.maxPriorityFeePerGas) { basefee = 0; } else { basefee = block.basefee; } uint256 chargeableGasPrice = Math.min(relayData.maxFeePerGas, Math.min(tx.gasprice, basefee + relayData.maxPriorityFeePerGas)); return config.baseRelayFee + (gasUsed * chargeableGasPrice * (config.pctRelayFee + 100)) / 100; } /// @inheritdoc IRelayHub function verifyRelayManagerStaked(address relayManager) public override view { (IStakeManager.StakeInfo memory info, bool isHubAuthorized) = stakeManager.getStakeInfo(relayManager); uint256 minimumStake = minimumStakePerToken[info.token]; require(info.token != IERC20(address(0)), "relay manager not staked"); require(info.stake >= minimumStake, "stake amount is too small"); require(minimumStake != 0, "staking this token is forbidden"); require(info.unstakeDelay >= config.minimumUnstakeDelay, "unstake delay is too small"); require(info.withdrawTime == 0, "stake has been withdrawn"); require(isHubAuthorized, "this hub is not authorized by SM"); } /// @inheritdoc IRelayHub function deprecateHub(uint256 _deprecationTime) public override onlyOwner { require(!isDeprecated(), "Already deprecated"); deprecationTime = _deprecationTime; emit HubDeprecated(deprecationTime); } /// @inheritdoc IRelayHub function isDeprecated() public override view returns (bool) { return block.timestamp >= deprecationTime; } /// @notice Prevents any address other than the `Penalizer` from calling this method. modifier penalizerOnly () { require(msg.sender == penalizer, "Not penalizer"); _; } /// @inheritdoc IRelayHub 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"); (IStakeManager.StakeInfo memory stakeInfo,) = stakeManager.getStakeInfo(relayManager); require(stakeInfo.stake > 0, "relay manager not staked"); stakeManager.penalizeRelayManager(relayManager, beneficiary, stakeInfo.stake); } /// @inheritdoc IRelayHub function isRelayEscheatable(address relayManager) public view override returns (bool){ return stakeManager.isRelayEscheatable(relayManager); } /// @inheritdoc IRelayHub function escheatAbandonedRelayBalance(address relayManager) external override onlyOwner { require(stakeManager.isRelayEscheatable(relayManager), "relay server not escheatable yet"); uint256 balance = balances[relayManager]; balances[relayManager] = 0; balances[config.devAddress] = balances[config.devAddress] + balance; emit AbandonedRelayManagerBalanceEscheated(relayManager, balance); } /// @inheritdoc IRelayHub function aggregateGasleft() public override virtual view returns (uint256){ return gasleft(); } }
pragma solidity >=0.7.6; pragma abicoder v2; // SPDX-License-Identifier: GPL-3.0-only import "@openzeppelin/contracts/interfaces/IERC165.sol"; /** * @title The Forwarder Interface * @notice The contracts implementing this interface take a role of authorization, authentication and replay protection * for contracts that choose to trust a `Forwarder`, instead of relying on a mechanism built into the Ethereum protocol. * * @notice if the `Forwarder` contract decides that an incoming `ForwardRequest` is valid, it must append 20 bytes that * represent the caller to the `data` field of the request and send this new data to the target address (the `to` field) * * :warning: **Warning** :warning: The Forwarder can have a full control over a `Recipient` contract. * Any vulnerability in a `Forwarder` implementation can make all of its `Recipient` contracts susceptible! * Recipient contracts should only trust forwarders that passed through security audit, * otherwise they are susceptible to identity theft. */ interface IForwarder is IERC165 { /** * @notice A representation of a request for a `Forwarder` to send `data` on behalf of a `from` to a target (`to`). */ struct ForwardRequest { address from; address to; uint256 value; uint256 gas; uint256 nonce; bytes data; uint256 validUntilTime; } event DomainRegistered(bytes32 indexed domainSeparator, bytes domainValue); event RequestTypeRegistered(bytes32 indexed typeHash, string typeStr); /** * @param from The address of a sender. * @return The nonce for this address. */ function getNonce(address from) external view returns(uint256); /** * @notice Verify the transaction is valid and can be executed. * Implementations must validate the signature and the nonce of the request are correct. * Does not revert and returns successfully if the input is valid. * Reverts if any validation has failed. For instance, if either signature or nonce are incorrect. * Reverts if `domainSeparator` or `requestTypeHash` are not registered as well. */ function verify( ForwardRequest calldata forwardRequest, bytes32 domainSeparator, bytes32 requestTypeHash, bytes calldata suffixData, bytes calldata signature ) external view; /** * @notice Executes a transaction specified by the `ForwardRequest`. * The transaction is first verified and then executed. * The success flag and returned bytes array of the `CALL` are returned as-is. * * This method would revert only in case of a verification error. * * All the target errors are reported using the returned success flag and returned bytes array. * * @param forwardRequest All requested transaction parameters. * @param domainSeparator The domain used when signing this request. * @param requestTypeHash The request type used when signing this request. * @param suffixData The ABI-encoded extension data for the current `RequestType` used when signing this request. * @param signature The client signature to be validated. * * @return success The success flag of the underlying `CALL` to the target address. * @return ret The byte array returned by the underlying `CALL` to the target address. */ function execute( ForwardRequest calldata forwardRequest, bytes32 domainSeparator, bytes32 requestTypeHash, bytes calldata suffixData, bytes calldata signature ) external payable returns (bool success, bytes memory ret); /** * @notice Register a new Request typehash. * * @notice This is necessary for the Forwarder to be able to verify the signatures conforming to the ERC-712. * * @param typeName The name of the request type. * @param typeSuffix Any extra data after the generic params. Must contain add at least one param. * The generic ForwardRequest type is always registered by the constructor. */ function registerRequestType(string calldata typeName, string calldata typeSuffix) external; /** * @notice Register a new domain separator. * * @notice This is necessary for the Forwarder to be able to verify the signatures conforming to the ERC-712. * * @notice 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's address. * This method accepts 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; }
pragma solidity >=0.6.0; // SPDX-License-Identifier: MIT /** * @title The ERC-2771 Recipient Base Abstract Class - Declarations * * @notice A contract must implement this interface in order to support relayed transaction. * * @notice It is recommended that your contract inherits from the ERC2771Recipient contract. */ abstract contract IERC2771Recipient { /** * :warning: **Warning** :warning: The Forwarder can have a full control over your Recipient. Only trust verified Forwarder. * @param forwarder The address of the Forwarder contract that is being used. * @return isTrustedForwarder `true` if the Forwarder is trusted to forward relayed transactions by this Recipient. */ function isTrustedForwarder(address forwarder) public virtual view returns(bool); /** * @notice Use this method the contract anywhere instead of msg.sender to support relayed transactions. * @return sender The real sender of this call. * For a call that came through the Forwarder the real sender is extracted from the last 20 bytes of the `msg.data`. * Otherwise simply returns `msg.sender`. */ function _msgSender() internal virtual view returns (address); /** * @notice Use this method in the contract instead of `msg.data` when difference matters (hashing, signature, etc.) * @return data The real `msg.data` of this call. * For a call that came through the Forwarder, the real sender address 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) simply returns `msg.data`. */ function _msgData() internal virtual view returns (bytes calldata); }
pragma solidity >=0.7.6; pragma abicoder v2; // SPDX-License-Identifier: GPL-3.0-only import "@openzeppelin/contracts/interfaces/IERC165.sol"; import "../utils/GsnTypes.sol"; /** * @title The Paymaster Interface * @notice Contracts implementing this interface exist to make decision about paying the transaction fee to the relay. * * @notice There are two callbacks here that are executed by the RelayHub: `preRelayedCall` and `postRelayedCall`. * * @notice It is recommended that your implementation inherits from the abstract BasePaymaster contract. */ interface IPaymaster is IERC165 { /** * @notice The limits this Paymaster wants to be imposed by the RelayHub on user input. See `getGasAndDataLimits`. */ struct GasAndDataLimits { uint256 acceptanceBudget; uint256 preRelayedCallGasLimit; uint256 postRelayedCallGasLimit; uint256 calldataSizeLimit; } /** * @notice Return the Gas Limits for Paymaster's functions and maximum msg.data length values for this Paymaster. * This function allows different paymasters to have different properties without changes to the RelayHub. * @return limits An instance of the `GasAndDataLimits` struct * * ##### `acceptanceBudget` * If the transactions consumes more than `acceptanceBudget` this Paymaster will be charged for gas no matter what. * Transaction that gets rejected after consuming more than `acceptanceBudget` gas is on this Paymaster's expense. * * Should be set to an amount gas this Paymaster expects to spend deciding whether to accept or reject a request. * This includes gas consumed by calculations in the `preRelayedCall`, `Forwarder` and the recipient contract. * * :warning: **Warning** :warning: 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 `preRelayedCallGasLimit`, it might might make Paymaster open to a "griefing" attack. * * The relayers should prefer lower `acceptanceBudget`, as it improves their chances of being compensated. * From a Relay's point of view, this is the highest gas value a bad Paymaster may cost the relay, * since the paymaster will pay anything above that value regardless of whether the transaction succeeds or reverts. * Specifying value too high might make the call rejected by relayers (see `maxAcceptanceBudget` in server config). * * ##### `preRelayedCallGasLimit` * The max gas usage of preRelayedCall. Any revert of the `preRelayedCall` is a request rejection by the paymaster. * As long as `acceptanceBudget` is above `preRelayedCallGasLimit`, any such revert is not payed by the paymaster. * * ##### `postRelayedCallGasLimit` * The max gas usage of postRelayedCall. The Paymaster is not charged for the maximum, only for actually used gas. * Note that an OOG will revert the inner transaction, but the paymaster will be charged for it anyway. */ function getGasAndDataLimits() external view returns ( GasAndDataLimits memory limits ); /** * @notice :warning: **Warning** :warning: using incorrect Forwarder may cause the Paymaster to agreeing to pay for invalid transactions. * @return trustedForwarder The address of the `Forwarder` that is trusted by this Paymaster to execute the requests. */ function getTrustedForwarder() external view returns (address trustedForwarder); /** * @return relayHub The address of the `RelayHub` that is trusted by this Paymaster to execute the requests. */ function getRelayHub() external view returns (address relayHub); /** * @notice Called by the Relay in view mode and later by the `RelayHub` on-chain to validate that * the Paymaster agrees to pay for this call. * * The request is considered to be rejected by the Paymaster in one of the following conditions: * - `preRelayedCall()` method reverts * - the `Forwarder` reverts because of nonce or signature error * - the `Paymaster` returned `rejectOnRecipientRevert: true` and the recipient contract reverted * (and all that did not consume more than `acceptanceBudget` gas). * * In any of the above cases, all Paymaster calls and the recipient call are reverted. * In any other case the Paymaster will pay for the gas cost of the transaction. * Note that even if `postRelayedCall` is reverted the Paymaster will be charged. * * @param relayRequest - the full relay request structure * @param signature - user's EIP712-compatible signature of the `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 `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 context * A byte array to be passed to postRelayedCall. * Can contain any data needed by this Paymaster in any form or be empty if no extra data is needed. * @return rejectOnRecipientRevert * The flag that allows a Paymaster to "delegate" the rejection to the recipient code. * It also means the Paymaster trust the recipient to reject fast: both preRelayedCall, * forwarder check and recipient checks must fit into the GasLimits.acceptanceBudget, * otherwise the TX is paid by the Paymaster. * `true` if the Paymaster wants to reject the TX if the recipient reverts. * `false` if the Paymaster wants rejects by the recipient to be completed on chain and paid by the Paymaster. */ function preRelayedCall( GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature, bytes calldata approvalData, uint256 maxPossibleGas ) external returns (bytes memory context, bool rejectOnRecipientRevert); /** * @notice 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. * * 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. * * @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() * */ function postRelayedCall( bytes calldata context, bool success, uint256 gasUseWithoutPost, GsnTypes.RelayData calldata relayData ) external; /** * @return version The SemVer string of this Paymaster's version. */ function versionPaymaster() external view returns (string memory); }
pragma solidity >=0.7.6; pragma abicoder v2; // SPDX-License-Identifier: GPL-3.0-only import "@openzeppelin/contracts/interfaces/IERC165.sol"; import "../utils/GsnTypes.sol"; import "./IStakeManager.sol"; /** * @title The RelayHub interface * @notice The implementation of this interface provides all the information the GSN client needs to * create a valid `RelayRequest` and also serves as an entry point for such requests. * * @notice The RelayHub also handles all the related financial records and hold the balances of participants. * The Paymasters keep their Ether deposited in the `RelayHub` in order to pay for the `RelayRequest`s that thay choose * to pay for, and Relay Servers keep their earned Ether in the `RelayHub` until they choose to `withdraw()` * * @notice The RelayHub on each supported network only needs a single instance and there is usually no need for dApp * developers or Relay Server operators to redeploy, reimplement, modify or override the `RelayHub`. */ interface IRelayHub is IERC165 { /** * @notice A struct that contains all the parameters of the `RelayHub` that can be modified after the deployment. */ 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; // Minimum unstake delay seconds of a relay manager's stake on the StakeManager uint256 minimumUnstakeDelay; // Developers address address devAddress; // 0 < fee < 100, as percentage of total charge from paymaster to relayer uint8 devFee; // baseRelayFee The base fee the Relay Server charges for a single transaction in Ether, in wei. uint80 baseRelayFee; // pctRelayFee The percent of the total charge to add as a Relay Server fee to the total charge. uint16 pctRelayFee; } /// @notice Emitted when a configuration of the `RelayHub` is changed event RelayHubConfigured(RelayHubConfig config); /// @notice Emitted when relays are added by a relayManager event RelayWorkersAdded( address indexed relayManager, address[] newRelayWorkers, uint256 workersCount ); /// @notice Emitted when an account withdraws funds from the `RelayHub`. event Withdrawn( address indexed account, address indexed dest, uint256 amount ); /// @notice Emitted when `depositFor` is called, including the amount and account that was funded. event Deposited( address indexed paymaster, address indexed from, uint256 amount ); /// @notice Emitted for each token configured for staking in setMinimumStakes event StakingTokenDataChanged( address token, uint256 minimumStake ); /** * @notice Emitted when an attempt to relay a call fails and the `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, bytes32 indexed relayRequestID, address from, address to, address relayWorker, bytes4 selector, uint256 innerGasUsed, bytes reason ); /** * @notice Emitted when a transaction is relayed. Note that the actual internal function call might be reverted. * The reason for a revert will be indicated in the `status` field of a corresponding `RelayCallStatus` value. * @notice `charge` is the Ether value deducted from the `Paymaster` balance. * The amount added to the `relayManager` balance will be lower if there is an activated `devFee` in the `config`. */ event TransactionRelayed( address indexed relayManager, address indexed relayWorker, bytes32 indexed relayRequestID, address from, address to, address paymaster, bytes4 selector, RelayCallStatus status, uint256 charge ); /// @notice This event is emitted in case the internal function returns a value or reverts with a revert string. event TransactionResult( RelayCallStatus status, bytes returnValue ); /// @notice This event is emitted in case this `RelayHub` is deprecated and will stop serving transactions soon. event HubDeprecated(uint256 deprecationTime); /** * @notice This event is emitted in case a `relayManager` has been deemed "abandoned" for being * unresponsive for a prolonged period of time. * @notice This event means the entire balance of the relay has been transferred to the `devAddress`. */ event AbandonedRelayManagerBalanceEscheated( address indexed relayManager, uint256 balance ); /** * Error codes that describe all possible failure reasons reported in the `TransactionRelayed` event `status` field. * @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 } /** * @notice Add new worker addresses controlled by the 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; /** * @notice The `RelayRegistrar` callback to notify the `RelayHub` that this `relayManager` has updated registration. */ function onRelayServerRegistered(address relayManager) external; // Balance management /** * @notice Deposits ether for a `Paymaster`, so that it can and pay for relayed transactions. * :warning: **Warning** :warning: Unused balance can only be withdrawn by the holder itself, by calling `withdraw`. * Emits a `Deposited` event. */ function depositFor(address target) external payable; /** * @notice Withdraws from an account's balance, sending it back to the caller. * Relay Managers call this to retrieve their revenue, and `Paymasters` can also use it to reduce their funding. * Emits a `Withdrawn` event. */ function withdraw(address payable dest, uint256 amount) external; /** * @notice Withdraws from an account's balance, sending funds to multiple provided addresses. * Relay Managers call this to retrieve their revenue, and `Paymasters` can also use it to reduce their funding. * Emits a `Withdrawn` event for each destination. */ function withdrawMultiple(address payable[] memory dest, uint256[] memory amount) external; // Relaying /** * @notice Relays a transaction. For this to succeed, multiple conditions must be met: * - `Paymaster`'s `preRelayCall` method must succeed and not revert. * - the `msg.sender` must be a registered Relay Worker that the user signed to use. * - the transaction's gas fees must be equal or larger than the ones that were 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 if all gas is spent. * * @notice If all conditions are met, the call will be relayed and the `Paymaster` charged. * * @param domainSeparatorName The name of the Domain Separator used to verify the EIP-712 signature * @param maxAcceptanceBudget The maximum valid value for `paymaster.getGasLimits().acceptanceBudget` to return. * @param relayRequest All details of the requested relayed call. * @param signature The client's EIP-712 signature over the `relayRequest` struct. * @param approvalData The dapp-specific data forwarded to the `Paymaster`'s `preRelayedCall` method. * This value is **not** verified by the `RelayHub` in any way. * As an example, it can be used to pass some kind of a third-party signature to the `Paymaster` for verification. * * Emits a `TransactionRelayed` event regardless of whether the transaction succeeded or failed. */ function relayCall( string calldata domainSeparatorName, uint256 maxAcceptanceBudget, GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature, bytes calldata approvalData ) external returns ( bool paymasterAccepted, uint256 charge, IRelayHub.RelayCallStatus status, bytes memory returnValue ); /** * @notice In case the Relay Worker has been found to be in violation of some rules by the `Penalizer` contract, * the `Penalizer` will call this method to execute a penalization. * The `RelayHub` will look up the Relay Manager of the given Relay Worker and will forward the call to * the `StakeManager` contract. The `RelayHub` does not perform the actual penalization either. * @param relayWorker The address of the Relay Worker that committed a penalizable offense. * @param beneficiary The address that called the `Penalizer` and will receive a reward for it. */ function penalize(address relayWorker, address payable beneficiary) external; /** * @notice Sets or changes the configuration of this `RelayHub`. * @param _config The new configuration. */ function setConfiguration(RelayHubConfig memory _config) external; /** * @notice Sets or changes the minimum amount of a given `token` that needs to be staked so that the Relay Manager * is considered to be 'staked' by this `RelayHub`. Zero value means this token is not allowed for staking. * @param token An array of addresses of ERC-20 compatible tokens. * @param minimumStake An array of minimal amounts necessary for a corresponding token, in wei. */ function setMinimumStakes(IERC20[] memory token, uint256[] memory minimumStake) external; /** * @notice Deprecate hub by reverting all incoming `relayCall()` calls starting from a given timestamp * @param _deprecationTime The timestamp in seconds after which the `RelayHub` stops serving transactions. */ function deprecateHub(uint256 _deprecationTime) external; /** * @notice * @param relayManager */ function escheatAbandonedRelayBalance(address relayManager) external; /** * @notice The fee is expressed as a base fee in wei plus percentage of the actual charge. * For example, a value '40' stands for a 40% fee, so the recipient will be charged for 1.4 times the spent amount. * @param gasUsed An amount of gas used by the transaction. * @param relayData The details of a transaction signed by the sender. * @return The calculated charge, in wei. */ function calculateCharge(uint256 gasUsed, GsnTypes.RelayData calldata relayData) external view returns (uint256); /** * @notice The fee is expressed as a percentage of the actual charge. * For example, a value '40' stands for a 40% fee, so the Relay Manager will only get 60% of the `charge`. * @param charge The amount of Ether in wei the Paymaster will be charged for this transaction. * @return The calculated devFee, in wei. */ function calculateDevCharge(uint256 charge) external view returns (uint256); /* getters */ /// @return config The configuration of the `RelayHub`. function getConfiguration() external view returns (RelayHubConfig memory config); /** * @param token An address of an ERC-20 compatible tokens. * @return The minimum amount of a given `token` that needs to be staked so that the Relay Manager * is considered to be 'staked' by this `RelayHub`. Zero value means this token is not allowed for staking. */ function getMinimumStakePerToken(IERC20 token) external view returns (uint256); /** * @param worker An address of the Relay Worker. * @return The address of its Relay Manager. */ function getWorkerManager(address worker) external view returns (address); /** * @param manager An address of the Relay Manager. * @return The count of Relay Workers associated with this Relay Manager. */ function getWorkerCount(address manager) external view returns (uint256); /// @return An account's balance. It can be either a deposit of a `Paymaster`, or a revenue of a Relay Manager. function balanceOf(address target) external view returns (uint256); /// @return The `StakeManager` address for this `RelayHub`. function getStakeManager() external view returns (IStakeManager); /// @return The `Penalizer` address for this `RelayHub`. function getPenalizer() external view returns (address); /// @return The `RelayRegistrar` address for this `RelayHub`. function getRelayRegistrar() external view returns (address); /// @return The `BatchGateway` address for this `RelayHub`. function getBatchGateway() external view returns (address); /** * @notice Uses `StakeManager` to decide if the Relay Manager can be considered staked or not. * Returns if the stake's token, amount and delay satisfy all requirements, reverts otherwise. */ function verifyRelayManagerStaked(address relayManager) external view; /** * @notice Uses `StakeManager` to check if the Relay Manager can be considered abandoned or not. * Returns true if the stake's abandonment time is in the past including the escheatment delay, false otherwise. */ function isRelayEscheatable(address relayManager) external view returns (bool); /// @return `true` if the `RelayHub` is deprecated, `false` it it is not deprecated and can serve transactions. function isDeprecated() external view returns (bool); /// @return The timestamp from which the hub no longer allows relaying calls. function getDeprecationTime() external view returns (uint256); /// @return The block number in which the contract has been deployed. function getCreationBlock() external view returns (uint256); /// @return a SemVer-compliant version of the `RelayHub` contract. function versionHub() external view returns (string memory); /// @return A total measurable amount of gas left to current execution. Same as 'gasleft()' for pure EVMs. function aggregateGasleft() external view returns (uint256); }
pragma solidity ^0.8.6; //SPDX-License-Identifier: GPL-3.0-only import "@openzeppelin/contracts/interfaces/IERC165.sol"; /** * @title The RelayRegistrar Interface * @notice The on-chain registrar for all registered Relay Managers. * * @notice The client can use an implementation of a `RelayRegistrar` to find relay registration info. * */ interface IRelayRegistrar is IERC165 { /** * @notice A struct containing all the information necessary to client to interact with the Relay Server. */ struct RelayInfo { //last registration block number uint32 lastSeenBlockNumber; //last registration block timestamp uint40 lastSeenTimestamp; //stake (first registration) block number uint32 firstSeenBlockNumber; //stake (first registration) block timestamp uint40 firstSeenTimestamp; bytes32[3] urlParts; address relayManager; } /** * @notice Emitted when a relay server registers or updates its details. * Looking up these events allows a client to discover registered Relay Servers. */ event RelayServerRegistered( address indexed relayManager, address indexed relayHub, bytes32[3] relayUrl ); /** * @notice This function is called by Relay Servers in order to register or to update their registration. * @param relayHub The address of the `RelayHub` contract for which this action is performed. * @param url The URL of the Relay Server that is listening to the clients' requests. */ function registerRelayServer( address relayHub, bytes32[3] calldata url ) external; /** * @return The block number in which the contract has been deployed. */ function getCreationBlock() external view returns (uint256); /** * @return The maximum age the relay is considered registered by default by this `RelayRegistrar`, in seconds. */ function getRelayRegistrationMaxAge() external view returns (uint256); /** * @notice Change the maximum relay registration age. */ function setRelayRegistrationMaxAge(uint256) external; /** * @param relayManager An address of a Relay Manager. * @param relayHub The address of the `RelayHub` contract for which this action is performed. * @return info All the details of the given Relay Manager's registration. Throws if relay not found for `RelayHub`. */ function getRelayInfo(address relayHub, address relayManager) external view returns (RelayInfo memory info); /** * @notice Read relay info of registered Relay Server from an on-chain storage. * @param relayHub The address of the `RelayHub` contract for which this action is performed. * @return info The list of `RelayInfo`s of registered Relay Servers */ function readRelayInfos( address relayHub ) external view returns ( RelayInfo[] memory info ); /** * @notice Read relay info of registered Relay Server from an on-chain storage. * @param relayHub The address of the `RelayHub` contract for which this action is performed. * @param maxCount The maximum amount of relays to be returned by this function. * @param oldestBlockNumber The latest block number in which a Relay Server may be registered. * @param oldestBlockTimestamp The latest block timestamp in which a Relay Server may be registered. * @return info The list of `RelayInfo`s of registered Relay Servers */ function readRelayInfosInRange( address relayHub, uint256 oldestBlockNumber, uint256 oldestBlockTimestamp, uint256 maxCount ) external view returns ( RelayInfo[] memory info ); }
pragma solidity >=0.7.6; pragma abicoder v2; // SPDX-License-Identifier: GPL-3.0-only import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; /** * @title The StakeManager Interface * @notice In order to prevent an attacker from registering a large number of unresponsive relays, the GSN requires * the Relay Server to maintain a permanently locked stake in the system before being able to register. * * @notice Also, in some cases the behavior of a Relay Server may be found to be illegal by a `Penalizer` contract. * In such case, the stake will never be returned to the Relay Server operator and will be slashed. * * @notice An implementation of this interface is tasked with keeping Relay Servers' stakes, made in any ERC-20 token. * Note that the `RelayHub` chooses which ERC-20 tokens to support and how much stake is needed. */ interface IStakeManager is IERC165 { /// @notice Emitted when a `stake` or `unstakeDelay` are initialized or increased. event StakeAdded( address indexed relayManager, address indexed owner, IERC20 token, uint256 stake, uint256 unstakeDelay ); /// @notice Emitted once a stake is scheduled for withdrawal. event StakeUnlocked( address indexed relayManager, address indexed owner, uint256 withdrawTime ); /// @notice Emitted when owner withdraws `relayManager` funds. event StakeWithdrawn( address indexed relayManager, address indexed owner, IERC20 token, uint256 amount ); /// @notice Emitted when an authorized `RelayHub` penalizes a `relayManager`. event StakePenalized( address indexed relayManager, address indexed beneficiary, IERC20 token, uint256 reward ); /// @notice Emitted when a `relayManager` adds a new `RelayHub` to a list of authorized. event HubAuthorized( address indexed relayManager, address indexed relayHub ); /// @notice Emitted when a `relayManager` removes a `RelayHub` from a list of authorized. event HubUnauthorized( address indexed relayManager, address indexed relayHub, uint256 removalTime ); /// @notice Emitted when a `relayManager` sets its `owner`. This is necessary to prevent stake hijacking. event OwnerSet( address indexed relayManager, address indexed owner ); /// @notice Emitted when a `burnAddress` is changed. event BurnAddressSet( address indexed burnAddress ); /// @notice Emitted when a `devAddress` is changed. event DevAddressSet( address indexed devAddress ); /// @notice Emitted if Relay Server is inactive for an `abandonmentDelay` and contract owner initiates its removal. event RelayServerAbandoned( address indexed relayManager, uint256 abandonedTime ); /// @notice Emitted to indicate an action performed by a relay server to prevent it from being marked as abandoned. event RelayServerKeepalive( address indexed relayManager, uint256 keepaliveTime ); /// @notice Emitted when the stake of an abandoned relayer has been confiscated and transferred to the `devAddress`. event AbandonedRelayManagerStakeEscheated( address indexed relayManager, address indexed owner, IERC20 token, uint256 amount ); /** * @param stake - amount of ether staked for this relay * @param unstakeDelay - number of seconds to elapse before the owner can retrieve the stake after calling 'unlock' * @param withdrawTime - timestamp in seconds when '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 withdrawTime; uint256 abandonedTime; uint256 keepaliveTime; IERC20 token; address owner; } struct RelayHubInfo { uint256 removalTime; } /** * @param devAddress - the address that will receive the 'abandoned' stake * @param abandonmentDelay - the amount of time after which the relay can be marked as 'abandoned' * @param escheatmentDelay - the amount of time after which the abandoned relay's stake and balance may be withdrawn to the `devAddress` */ struct AbandonedRelayServerConfig { address devAddress; uint256 abandonmentDelay; uint256 escheatmentDelay; } /** * @notice 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 owner) external; /** * @notice Put a stake for a relayManager and set its unstake delay. * Only the owner can call this function. If the entry does not exist, reverts. * The owner must give allowance of the ERC-20 token to the StakeManager before calling this method. * It is the RelayHub who has a configurable list of minimum stakes per token. StakeManager accepts all tokens. * @param token The address of an ERC-20 token that is used by the relayManager as a stake * @param relayManager The address that represents a stake entry and controls relay registrations on relay hubs * @param unstakeDelay The number of seconds to elapse before an owner can retrieve the stake after calling `unlock` * @param amount The amount of tokens to be taken from the relayOwner and locked in the StakeManager as a stake */ function stakeForRelayManager(IERC20 token, address relayManager, uint256 unstakeDelay, uint256 amount) external; /** * @notice Schedule the unlocking of the stake. The `unstakeDelay` must pass before owner can call `withdrawStake`. * @param relayManager The address of a Relay Manager whose stake is to be unlocked. */ function unlockStake(address relayManager) external; /** * @notice Withdraw the unlocked stake. * @param relayManager The address of a Relay Manager whose stake is to be withdrawn. */ function withdrawStake(address relayManager) external; /** * @notice Add the `RelayHub` to a list of authorized by this Relay Manager. * This allows the RelayHub to penalize this Relay Manager. The `RelayHub` cannot trust a Relay it cannot penalize. * @param relayManager The address of a Relay Manager whose stake is to be authorized for the new `RelayHub`. * @param relayHub The address of a `RelayHub` to be authorized. */ function authorizeHubByOwner(address relayManager, address relayHub) external; /** * @notice Same as `authorizeHubByOwner` but can be called by the RelayManager itself. */ function authorizeHubByManager(address relayHub) external; /** * @notice Remove the `RelayHub` from a list of authorized by this Relay Manager. * @param relayManager The address of a Relay Manager. * @param relayHub The address of a `RelayHub` to be unauthorized. */ function unauthorizeHubByOwner(address relayManager, address relayHub) external; /** * @notice Same as `unauthorizeHubByOwner` but can be called by the RelayManager itself. */ function unauthorizeHubByManager(address relayHub) external; /** * Slash the stake of the relay relayManager. In order to prevent stake kidnapping, burns part of stake on the way. * @param relayManager The address of a Relay Manager to be penalized. * @param beneficiary The address that receives part of the penalty amount. * @param amount A total amount of penalty to be withdrawn from stake. */ function penalizeRelayManager(address relayManager, address beneficiary, uint256 amount) external; /** * @notice Allows the contract owner to set the given `relayManager` as abandoned after a configurable delay. * Its entire stake and balance will be taken from a relay if it does not respond to being marked as abandoned. */ function markRelayAbandoned(address relayManager) external; /** * @notice If more than `abandonmentDelay` has passed since the last Keepalive transaction, and relay manager * has been marked as abandoned, and after that more that `escheatmentDelay` have passed, entire stake and * balance will be taken from this relay. */ function escheatAbandonedRelayStake(address relayManager) external; /** * @notice Sets a new `keepaliveTime` for the given `relayManager`, preventing it from being marked as abandoned. * Can be called by an authorized `RelayHub` or by the `relayOwner` address. */ function updateRelayKeepaliveTime(address relayManager) external; /** * @notice Check if the Relay Manager can be considered abandoned or not. * Returns true if the stake's abandonment time is in the past including the escheatment delay, false otherwise. */ function isRelayEscheatable(address relayManager) external view returns(bool); /** * @notice Get the stake details information for the given Relay Manager. * @param relayManager The address of a Relay Manager. * @return stakeInfo The `StakeInfo` structure. * @return isSenderAuthorizedHub `true` if the `msg.sender` for this call was a `RelayHub` that is authorized now. * `false` if the `msg.sender` for this call is not authorized. */ function getStakeInfo(address relayManager) external view returns (StakeInfo memory stakeInfo, bool isSenderAuthorizedHub); /** * @return The maximum unstake delay this `StakeManger` allows. This is to prevent locking money forever by mistake. */ function getMaxUnstakeDelay() external view returns (uint256); /** * @notice Change the address that will receive the 'burned' part of the penalized stake. * This is done to prevent malicious Relay Server from penalizing itself and breaking even. */ function setBurnAddress(address _burnAddress) external; /** * @return The address that will receive the 'burned' part of the penalized stake. */ function getBurnAddress() external view returns (address); /** * @notice Change the address that will receive the 'abandoned' stake. * This is done to prevent Relay Servers that lost their keys from losing access to funds. */ function setDevAddress(address _burnAddress) external; /** * @return The structure that contains all configuration values for the 'abandoned' stake. */ function getAbandonedRelayServerConfig() external view returns (AbandonedRelayServerConfig memory); /** * @return the block number in which the contract has been deployed. */ function getCreationBlock() external view returns (uint256); /** * @return a SemVer-compliant version of the `StakeManager` contract. */ function versionSM() external view returns (string memory); }
pragma solidity ^0.8.0; pragma abicoder v2; // SPDX-License-Identifier: GPL-3.0-only import "../utils/GsnTypes.sol"; import "../interfaces/IERC2771Recipient.sol"; import "../forwarder/IForwarder.sol"; import "./GsnUtils.sol"; /** * @title The ERC-712 Library for GSN * @notice Bridge Library to convert a GSN RelayRequest into a valid `ForwardRequest` for 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 validUntilTime"; bytes public constant RELAYDATA_TYPE = "RelayData(uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,uint256 transactionCalldataGasUsed,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( IERC2771Recipient.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( string memory domainSeparatorName, GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature ) internal view { (bytes memory suffixData) = splitRequest(relayRequest); bytes32 _domainSeparator = domainSeparator(domainSeparatorName, relayRequest.relayData.forwarder); IForwarder forwarder = IForwarder(payable(relayRequest.relayData.forwarder)); forwarder.verify(relayRequest.request, _domainSeparator, RELAY_REQUEST_TYPEHASH, suffixData, signature); } function verify( string memory domainSeparatorName, GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature ) internal view { verifyForwarderTrusted(relayRequest); verifySignature(domainSeparatorName, relayRequest, signature); } function execute( string memory domainSeparatorName, GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature ) internal returns ( bool forwarderSuccess, bool callSuccess, bytes memory ret ) { (bytes memory suffixData) = splitRequest(relayRequest); bytes32 _domainSeparator = domainSeparator(domainSeparatorName, 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(string memory name, address forwarder) internal view returns (bytes32) { return hashDomain(EIP712Domain({ name : name, version : "3", chainId : getChainID(), verifyingContract : forwarder })); } function getChainID() internal view 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.maxFeePerGas, req.maxPriorityFeePerGas, req.transactionCalldataGasUsed, req.relayWorker, req.paymaster, req.forwarder, keccak256(req.paymasterData), req.clientId )); } }
pragma solidity ^0.8.0; // SPDX-License-Identifier: GPL-3.0-only import "../forwarder/IForwarder.sol"; interface GsnTypes { /// @notice maxFeePerGas, maxPriorityFeePerGas, pctRelayFee and baseRelayFee must be validated inside of the paymaster's preRelayedCall in order not to overpay struct RelayData { uint256 maxFeePerGas; uint256 maxPriorityFeePerGas; uint256 transactionCalldataGasUsed; 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; } }
pragma solidity ^0.8.0; /* solhint-disable no-inline-assembly */ // SPDX-License-Identifier: GPL-3.0-only import "../utils/MinLibBytes.sol"; import "./GsnTypes.sol"; /** * @title The GSN Solidity Utils Library * @notice Some library functions used throughout the GSN Solidity codebase. */ library GsnUtils { bytes32 constant private RELAY_REQUEST_ID_MASK = 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; /** * @notice Calculate an identifier for the meta-transaction in a format similar to a transaction hash. * Note that uniqueness relies on signature and may not be enforced if meta-transactions are verified * with a different algorithm, e.g. when batching. * @param relayRequest The `RelayRequest` for which an ID is being calculated. * @param signature The signature for the `RelayRequest`. It is not validated here and may even remain empty. */ function getRelayRequestID(GsnTypes.RelayRequest calldata relayRequest, bytes calldata signature) internal pure returns (bytes32) { return keccak256(abi.encode(relayRequest.request.from, relayRequest.request.nonce, signature)) & RELAY_REQUEST_ID_MASK; } /** * @notice Extract the method identifier signature from the encoded function call. */ function getMethodSig(bytes memory msgData) internal pure returns (bytes4) { return MinLibBytes.readBytes4(msgData, 0); } /** * @notice Extract a 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`). * @param msgData Byte array containing a uint256 value. * @param index Index in byte array of uint256 value. * @return result uint256 value from byte array. */ function getParam(bytes memory msgData, uint256 index) internal pure returns (uint256 result) { return MinLibBytes.readUint256(msgData, 4 + index * 32); } /// @notice Re-throw revert with the same revert data. function revertWithData(bytes memory data) internal pure { assembly { revert(add(data,32), mload(data)) } } }
pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT // minimal bytes manipulation required by GSN // a minimal subset from 0x/LibBytes /* solhint-disable no-inline-assembly */ 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; } }
pragma solidity ^0.8.0; pragma abicoder v2; // SPDX-License-Identifier: GPL-3.0-only import "../utils/GsnTypes.sol"; /** * @title The RelayHub Validator Library * @notice Validates the `msg.data` received by the `RelayHub` does not contain unnecessary bytes. * Including these extra bytes would allow the Relay Server to inflate transaction costs and overcharge the client. */ library RelayHubValidator { /// @notice Validate that encoded `relayCall` is properly packed without any extra bytes function verifyTransactionPacking( string calldata domainSeparatorName, 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 21 32-byte words if all dynamic params are zero-length. uint256 expectedMsgDataLen = 4 + 22 * 32 + dynamicParamSize(bytes(domainSeparatorName)) + dynamicParamSize(signature) + dynamicParamSize(approvalData) + dynamicParamSize(relayRequest.request.data) + dynamicParamSize(relayRequest.relayData.paymasterData); // zero-length signature is allowed in a batch relay transaction 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 (uint256) { return 32 + ((buf.length + 31) & (type(uint256).max - 31)); } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
[{"inputs":[{"internalType":"contract IStakeManager","name":"_stakeManager","type":"address"},{"internalType":"address","name":"_penalizer","type":"address"},{"internalType":"address","name":"_batchGateway","type":"address"},{"internalType":"address","name":"_relayRegistrar","type":"address"},{"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":"minimumUnstakeDelay","type":"uint256"},{"internalType":"address","name":"devAddress","type":"address"},{"internalType":"uint8","name":"devFee","type":"uint8"},{"internalType":"uint80","name":"baseRelayFee","type":"uint80"},{"internalType":"uint16","name":"pctRelayFee","type":"uint16"}],"internalType":"struct IRelayHub.RelayHubConfig","name":"_config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"}],"name":"AbandonedRelayManagerBalanceEscheated","type":"event"},{"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":"deprecationTime","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":"minimumUnstakeDelay","type":"uint256"},{"internalType":"address","name":"devAddress","type":"address"},{"internalType":"uint8","name":"devFee","type":"uint8"},{"internalType":"uint80","name":"baseRelayFee","type":"uint80"},{"internalType":"uint16","name":"pctRelayFee","type":"uint16"}],"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":"address[]","name":"newRelayWorkers","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"workersCount","type":"uint256"}],"name":"RelayWorkersAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"minimumStake","type":"uint256"}],"name":"StakingTokenDataChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"relayManager","type":"address"},{"indexed":true,"internalType":"address","name":"paymaster","type":"address"},{"indexed":true,"internalType":"bytes32","name":"relayRequestID","type":"bytes32"},{"indexed":false,"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":"bytes32","name":"relayRequestID","type":"bytes32"},{"indexed":false,"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":[{"internalType":"address[]","name":"newRelayWorkers","type":"address[]"}],"name":"addRelayWorkers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"aggregateGasleft","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"uint256","name":"transactionCalldataGasUsed","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":"charge","type":"uint256"}],"name":"calculateDevCharge","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":"_deprecationTime","type":"uint256"}],"name":"deprecateHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"relayManager","type":"address"}],"name":"escheatAbandonedRelayBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBatchGateway","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"minimumUnstakeDelay","type":"uint256"},{"internalType":"address","name":"devAddress","type":"address"},{"internalType":"uint8","name":"devFee","type":"uint8"},{"internalType":"uint80","name":"baseRelayFee","type":"uint80"},{"internalType":"uint16","name":"pctRelayFee","type":"uint16"}],"internalType":"struct IRelayHub.RelayHubConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCreationBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDeprecationTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getMinimumStakePerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPenalizer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRelayRegistrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakeManager","outputs":[{"internalType":"contract IStakeManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"manager","type":"address"}],"name":"getWorkerCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"worker","type":"address"}],"name":"getWorkerManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"domainSeparatorName","type":"string"},{"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":"validUntilTime","type":"uint256"}],"internalType":"struct IForwarder.ForwardRequest","name":"request","type":"tuple"},{"components":[{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"uint256","name":"transactionCalldataGasUsed","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":"isRelayEscheatable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"relayManager","type":"address"}],"name":"onRelayServerRegistered","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"string","name":"domainSeparatorName","type":"string"},{"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":"validUntilTime","type":"uint256"}],"internalType":"struct IForwarder.ForwardRequest","name":"request","type":"tuple"},{"components":[{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"uint256","name":"transactionCalldataGasUsed","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"}],"name":"relayCall","outputs":[{"internalType":"bool","name":"paymasterAccepted","type":"bool"},{"internalType":"uint256","name":"charge","type":"uint256"},{"internalType":"enum IRelayHub.RelayCallStatus","name":"status","type":"uint8"},{"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":"minimumUnstakeDelay","type":"uint256"},{"internalType":"address","name":"devAddress","type":"address"},{"internalType":"uint8","name":"devFee","type":"uint8"},{"internalType":"uint80","name":"baseRelayFee","type":"uint80"},{"internalType":"uint16","name":"pctRelayFee","type":"uint16"}],"internalType":"struct IRelayHub.RelayHubConfig","name":"_config","type":"tuple"}],"name":"setConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"token","type":"address[]"},{"internalType":"uint256[]","name":"minimumStake","type":"uint256[]"}],"name":"setMinimumStakes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"relayManager","type":"address"}],"name":"verifyRelayManagerStaked","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"versionHub","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address payable","name":"dest","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable[]","name":"dest","type":"address[]"},{"internalType":"uint256[]","name":"amount","type":"uint256[]"}],"name":"withdrawMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
610120604052600019600c553480156200001857600080fd5b5060405162004c3138038062004c318339810160408190526200003b9162000331565b62000046336200008b565b43610100526001600160601b0319606086811b821660805285811b821660a05284811b821660c05283901b1660e0526200008081620000db565b505050505062000493565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b620000e562000284565b60648160c0015160ff1610620001355760405162461bcd60e51b815260206004820152601060248201526f0c8caec40cccaca40e8dede40d0d2ced60831b60448201526064015b60405180910390fd5b8051600190815560208201516002556040808301516003556060830151600455608083015160055560a08301516006805460c086015160e08701516001600160501b0316600160a81b02600160a81b600160f81b031960ff909216600160a01b026001600160a81b03199093166001600160a01b039095169490941791909117169190911790556101008301516007805461ffff90921661ffff19909216919091179055517f4812ada68f7c2cdc9f4a4a09e157ea6b924e0ef40a4fa7aa074fa8f70b1e7247916200027991815481526001820154602082015260028201546040820152600382015460608201526004820154608082015260058201546001600160a01b03811660a08084019190915260ff9082901c1660c083015260a81c6001600160501b031660e082015260069091015461ffff166101008201526101200190565b60405180910390a150565b6000546001600160a01b03163314620002e05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016200012c565b565b8051620002ef816200047a565b919050565b805161ffff81168114620002ef57600080fd5b80516001600160501b0381168114620002ef57600080fd5b805160ff81168114620002ef57600080fd5b60008060008060008587036101a08112156200034c57600080fd5b865162000359816200047a565b60208801519096506200036c816200047a565b60408801519095506200037f816200047a565b606088015190945062000392816200047a565b9250610120607f198201811315620003a957600080fd5b620003b362000442565b91506080880151825260a0880151602083015260c0880151604083015260e08801516060830152610100808901516080840152620003f3828a01620002e2565b60a0840152620004076101408a016200031f565b60c08401526200041b6101608a0162000307565b60e08401526200042f6101808a01620002f4565b9083015250949793965091945092919050565b60405161012081016001600160401b03811182821017156200047457634e487b7160e01b600052604160045260246000fd5b60405290565b6001600160a01b03811681146200049057600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005161470e6200052360003960006102420152600081816102eb015261081a0152600081816106f30152610b5301526000818161042501526125b80152600081816105c80152818161078b0152818161090c015281816121930152818161232e015281816126960152612791015261470e6000f3fe6080604052600436106101f95760003560e01c80638e53548b1161010d578063ca64f9e7116100a0578063e6e22e121161006f578063e6e22e1214610664578063ebcd31ac14610684578063f2fde38b146106a4578063f3fef3a3146106c4578063f7908a74146106e457600080fd5b8063ca64f9e7146105b9578063d26152f9146105ec578063d904c7321461060c578063ddf737891461062e57600080fd5b8063af595dfc116100dc578063af595dfc14610528578063be7c76ad14610548578063c2da078614610581578063c7178230146105a157600080fd5b80638e53548b146104a75780639bcde3f3146104c7578063a6e61b60146104e7578063aa67c9191461051557600080fd5b80636bd50cef1161019057806372c1ab0c1161015f57806372c1ab0c146103f657806375dc7b4e146104165780638a09fb56146104495780638ac3b845146104695780638da5cb5b1461048957600080fd5b80636bd50cef146103595780636ca862e21461037b57806370a08231146103ab578063715018a6146103e157600080fd5b806345bdade7116101cc57806345bdade7146102a55780634f7de031146102ba57806351d85156146102dc57806359c4afc01461032357600080fd5b806301ffc9a7146101fe57806320ffd56d1461023357806339622167146102705780633c16e59a14610290575b600080fd5b34801561020a57600080fd5b5061021e610219366004613881565b610717565b60405190151581526020015b60405180910390f35b34801561023f57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405190815260200161022a565b34801561027c57600080fd5b5061021e61028b3660046135cb565b610769565b34801561029c57600080fd5b50600c54610262565b3480156102b157600080fd5b50610262610807565b3480156102c657600080fd5b506102da6102d53660046135cb565b61080f565b005b3480156102e857600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040516001600160a01b03909116815260200161022a565b34801561032f57600080fd5b5061026261033e3660046135cb565b6001600160a01b03166000908152600a602052604090205490565b34801561036557600080fd5b5061036e61096b565b60405161022a9190614229565b34801561038757600080fd5b5061039b610396366004613a1b565b610a4c565b60405161022a94939291906140ac565b3480156103b757600080fd5b506102626103c63660046135cb565b6001600160a01b03166000908152600b602052604090205490565b3480156103ed57600080fd5b506102da611394565b34801561040257600080fd5b506102da6104113660046136c1565b6113a8565b34801561042257600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061030b565b34801561045557600080fd5b506102da610464366004613789565b6115d1565b34801561047557600080fd5b50610262610484366004613c66565b611728565b34801561049557600080fd5b506000546001600160a01b031661030b565b3480156104b357600080fd5b506102626104c2366004613c7f565b61175d565b3480156104d357600080fd5b506102da6104e2366004613b47565b6117f3565b3480156104f357600080fd5b50610507610502366004613936565b611999565b60405161022a92919061410d565b6102da6105233660046135cb565b611e25565b34801561053457600080fd5b506102da610543366004613c66565b611f06565b34801561055457600080fd5b5061030b6105633660046135cb565b6001600160a01b039081166000908152600960205260409020541690565b34801561058d57600080fd5b506102da61059c36600461364d565b611f89565b3480156105ad57600080fd5b50600c5442101561021e565b3480156105c557600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061030b565b3480156105f857600080fd5b506102da6106073660046135cb565b61216c565b34801561061857600080fd5b506106216122ea565b60405161022a91906141c3565b34801561063a57600080fd5b506102626106493660046135cb565b6001600160a01b031660009081526008602052604090205490565b34801561067057600080fd5b506102da61067f3660046135cb565b61230a565b34801561069057600080fd5b506102da61069f366004613614565b6125ad565b3480156106b057600080fd5b506102da6106bf3660046135cb565b6127f5565b3480156106d057600080fd5b506102da6106df3660046135e8565b61286e565b3480156106f057600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061030b565b60006001600160e01b0319821663e9fb30f760e01b148061074857506001600160e01b03198216630704183b60e11b145b8061076357506301ffc9a760e01b6001600160e01b03198316145b92915050565b604051633962216760e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063396221679060240160206040518083038186803b1580156107cf57600080fd5b505afa1580156107e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107639190613823565b60005a905090565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461088c5760405162461bcd60e51b815260206004820152601d60248201527f63616c6c6572206973206e6f742072656c61792072656769737472617200000060448201526064015b60405180910390fd5b6108958161230a565b6001600160a01b0381166000908152600a60205260409020546108ed5760405162461bcd60e51b815260206004820152601060248201526f6e6f2072656c617920776f726b65727360801b6044820152606401610883565b6040516346dcbf0b60e01b81526001600160a01b0382811660048301527f000000000000000000000000000000000000000000000000000000000000000016906346dcbf0b90602401600060405180830381600087803b15801561095057600080fd5b505af1158015610964573d6000803e3d6000fd5b5050505050565b6109d3604051806101200160405280600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600060ff16815260200160006001600160501b03168152602001600061ffff1681525090565b5060408051610120810182526001548152600254602082015260035491810191909152600454606082015260055460808201526006546001600160a01b03811660a0830152600160a01b810460ff1660c0830152600160a81b90046001600160501b031660e082015260075461ffff1661010082015290565b60008060006060610a5b613356565b610a63610807565b6040820152610a738a8a8a612911565b6101e0820152600c544210610abb5760405162461bcd60e51b815260206004820152600e60248201526d1a1d588819195c1c9958d85d195960921b6044820152606401610883565b6004610ac78b80614340565b610ad59060a08101906142fa565b90501015610ae4576000610b39565b610b39610af18b80614340565b610aff9060a08101906142fa565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509250612976915050565b6001600160e01b0319166020820152336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801590610b8057503215155b15610c945787610bd25760405162461bcd60e51b815260206004820181905260248201527f6d697373696e67207369676e6174757265206f722062616420676174657761796044820152606401610883565b333214610c215760405162461bcd60e51b815260206004820152601860248201527f72656c617920776f726b6572206d75737420626520454f4100000000000000006044820152606401610883565b610c2e60208b018b614356565b610c3f9060808101906060016135cb565b6001600160a01b0316336001600160a01b031614610c945760405162461bcd60e51b81526020600482015260126024820152712737ba1030903934b3b43a103bb7b935b2b960711b6044820152606401610883565b3215610d325760096000610cab60208d018d614356565b610cbc9060808101906060016135cb565b6001600160a01b039081168252602082019290925260400160002054166101c08201819052610d245760405162461bcd60e51b81526020600482015260146024820152732ab735b737bbb7103932b630bc903bb7b935b2b960611b6044820152606401610883565b610d32816101c0015161230a565b610d418b8b83604001516129e6565b61010083015260a0820152610d5b8d8d8c8c8c8c8c612c70565b60025460405a610d6c90603f6144af565b610d76919061448d565b610d8091906144ce565b610120820152610d8e610807565b6101408201526003546004546101208301516040840151610db260208f018f614356565b60400135610dc09190614475565b610dca9190614475565b610dd49190614475565b610dde9190614475565b81610200018181525050306001600160a01b031681610120015163a6e61b6060e01b8f8f8e8e8e8e8e8a60a00151610e14610807565b8c6102000151610e2491906144ce565b8c6101000151604051602401610e439a9998979695949392919061412d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051610e819190613ed2565b60006040518083038160008787f1925050503d8060008114610ebf576040519150601f19603f3d011682016040523d82523d6000602084013e610ec4565b606091505b5061022083015215158152610ed7610807565b816101400151610ee791906144ce565b60e08201526102208101518051610f06916020918101820191016138f8565b8260c00183608001829052826006811115610f2357610f23614542565b6006811115610f3457610f34614542565b9052505060808101515115610f85577fa1478a4242848419db824250a0dddc645dca0d6a9b12ab1fd79b00145a0ba98e8160c001518260800151604051610f7c92919061410d565b60405180910390a15b80516110e75760028160c001516006811115610fa357610fa3614542565b14806110125750610fb760208b018b614356565b60a082015151610fcb916040013590614475565b8160e0015111158015611012575060038160c001516006811115610ff157610ff1614542565b1480611012575060048160c00151600681111561101057611010614542565b145b156110e7576101e081015161102a60208c018c614356565b61103b9060a08101906080016135cb565b6101c08301516001600160a01b0391821691167f0c47510cb900901afca7c9e926892582a36fd3bb7f908142ccf0fb4382a2b6846110798e80614340565b6110879060208101906135cb565b6110918f80614340565b6110a29060408101906020016135cb565b3387602001518860e0015189608001516040516110c496959493929190613fd7565b60405180910390a46000808260c001518360800151945094509450945050611385565b6004546110f2610807565b826040015161110191906144ce565b61110e60208d018d614356565b6040013561111c9190614475565b6111269190614475565b610160820181905261113f906104c260208d018d614356565b935061114a84611728565b61018082015283600b600061116260208e018e614356565b6111739060a08101906080016135cb565b6001600160a01b03166001600160a01b031681526020019081526020016000205461119e91906144ce565b600b60006111af60208e018e614356565b6111c09060a08101906080016135cb565b6001600160a01b031681526020810191909152604001600020556101808101516111ea90856144ce565b6101c08201516001600160a01b03166000908152600b60205260409020546112129190614475565b6101c08201516001600160a01b03166000908152600b602052604090205561018081015115611282576101808101516006546001600160a01b03166000908152600b60205260409020546112669190614475565b6006546001600160a01b03166000908152600b60205260409020555b600061128e8b80614340565b61129c9060208101906135cb565b905060006112aa8c80614340565b6112bb9060408101906020016135cb565b905060006112cc60208e018e614356565b6112dd9060a08101906080016135cb565b9050836101e00151336001600160a01b0316856101c001516001600160a01b03167fd51ac07012398c2059ec53c6005fa8639657917bd2dbd72bc489a3c0eaaa7f168686868a602001518b60c001518f60405161133f96959493929190613f89565b60405180910390a4505050326113685760c0810151608090910151600195509092509050611385565b60c001516040805160208101909152600081526001955090925090505b98509850985098945050505050565b61139c612d4a565b6113a66000612da4565b565b3360005b82518110156115cb576001600160a01b0382166000908152600b602052604090205483518490839081106113e2576113e2614558565b602002602001015181101561142e5760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742066756e647360701b6044820152606401610883565b83828151811061144057611440614558565b60200260200101518161145391906144ce565b6001600160a01b0384166000908152600b6020526040812091909155855186908490811061148357611483614558565b60200260200101516001600160a01b03168584815181106114a6576114a6614558565b602002602001015160405160006040518083038185875af1925050503d80600081146114ee576040519150601f19603f3d011682016040523d82523d6000602084013e6114f3565b606091505b50509050806115375760405162461bcd60e51b815260206004820152601060248201526f2a3930b739b332b9103330b4b632b21760811b6044820152606401610883565b85838151811061154957611549614558565b60200260200101516001600160a01b0316846001600160a01b03167fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb87868151811061159757611597614558565b60200260200101516040516115ae91815260200190565b60405180910390a3505080806115c390614511565b9150506113ac565b50505050565b6115d9612d4a565b805182511461162a5760405162461bcd60e51b815260206004820152601e60248201527f7365744d696e696d756d5374616b65733a2077726f6e67206c656e67746800006044820152606401610883565b60005b82518110156117235781818151811061164857611648614558565b60200260200101516008600085848151811061166657611666614558565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020819055507ffb7510fe755f485459ee8ed619d4a0addc092c230ff47e760a62aeba78ebb9e38382815181106116c5576116c5614558565b60200260200101518383815181106116df576116df614558565b60200260200101516040516117099291906001600160a01b03929092168252602082015260400190565b60405180910390a18061171b81614511565b91505061162d565b505050565b600654600090600160a01b900460ff1661174457506000919050565b600654606490600160a01b900460ff1683020492915050565b60008082356020840135141561177557506000611778565b50485b6000611794843561178f3a81602089013587614475565b612df4565b6007549091506064906117ab9061ffff168261444f565b61ffff166117b983886144af565b6117c391906144af565b6117cd919061448d565b6006546117ea9190600160a81b90046001600160501b0316614475565b95945050505050565b6117fb612d4a565b60648160c0015160ff16106118455760405162461bcd60e51b815260206004820152601060248201526f0c8caec40cccaca40e8dede40d0d2ced60831b6044820152606401610883565b8051600190815560208201516002556040808301516003556060830151600455608083015160055560a08301516006805460c086015160e08701516001600160501b0316600160a81b0269ffffffffffffffffffff60a81b1960ff909216600160a01b026001600160a81b03199093166001600160a01b039095169490941791909117169190911790556101008301516007805461ffff90921661ffff19909216919091179055517f4812ada68f7c2cdc9f4a4a09e157ea6b924e0ef40a4fa7aa074fa8f70b1e72479161198e91815481526001820154602082015260028201546040820152600382015460608201526004820154608082015260058201546001600160a01b03811660a08084019190915260ff9082901c1660c08301526001600160501b0360a89190911c1660e082015260069091015461ffff166101008201526101200190565b60405180910390a150565b600060606119f3604051806101200160405280600081526020016000815260200160008152602001600080191681526020016000151581526020016060815260200160608152602001606081526020016000151581525090565b6119fb610807565b81525a611a0890866144ce565b6020820152333014611a5c5760405162461bcd60e51b815260206004820152601a60248201527f4d7573742062652063616c6c65642062792052656c61794875620000000000006044820152606401610883565b600b6000611a6d60208e018e614356565b611a7e9060a08101906080016135cb565b6001600160a01b0316815260208101919091526040908101600020548282015251622f977560e21b90611abf908d908d908d908d908d908b906024016142ab565b60408051601f19818403018152919052602080820180516001600160e01b03166001600160e01b03199094169390931790925260e0830152600090606090611b09908e018e614356565b611b1a9060a08101906080016135cb565b6001600160a01b031688602001358460e00151604051611b3a9190613ed2565b60006040518083038160008787f1925050503d8060008114611b78576040519150601f19603f3d011682016040523d82523d6000602084013e611b7d565b606091505b50909250905081611b9c57611b9181612e0c565b611b9c600282612e18565b80806020019051810190611bb091906138ab565b8460c00185610100018215151515815250829052505050506000611c1a8e8e8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508d8d8d612e51565b60a085015215156080840152905080611c3c57611c3c60038360a00151612e18565b8161010001518015611c5057508160800151155b15611c6457611c6460048360a00151612e18565b506376fa01c360e01b8160c001518260800151611c7f610807565b8451611c8b91906144ce565b8460200151611c9a9190614475565b8e8060200190611caa9190614356565b604051602401611cbd94939291906140da565b60408051601f19818403018152919052602080820180516001600160e01b03166001600160e01b03199094169390931790925260e08301526000908190611d06908e018e614356565b611d179060a08101906080016135cb565b6001600160a01b031688604001358460e00151604051611d379190613ed2565b60006040518083038160008787f1925050503d8060008114611d75576040519150601f19603f3d011682016040523d82523d6000602084013e611d7a565b606091505b509150915081611d8f57611d8f600582612e18565b50506040810151600b6000611da760208f018f614356565b611db89060a08101906080016135cb565b6001600160a01b03166001600160a01b03168152602001908152602001600020541015611df957611df9600660405180602001604052806000815250612e18565b8060800151611e09576001611e0c565b60005b8160a0015192509250509a509a98505050505050505050565b611e3f6001600160a01b0382166370d596f560e11b613037565b611e8b5760405162461bcd60e51b815260206004820181905260248201527f746172676574206973206e6f7420612076616c696420495061796d61737465726044820152606401610883565b6001600160a01b0381166000908152600b60205260409020543490611eb1908290614475565b6001600160a01b0383166000818152600b6020908152604091829020939093555183815233927f8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a7910160405180910390a35050565b611f0e612d4a565b600c544210611f545760405162461bcd60e51b8152602060048201526012602482015271105b1c9958591e4819195c1c9958d85d195960721b6044820152606401610883565b600c8190556040518181527f1c0aa0c666483fbf0cf795d9d646ea3552d1e3008162ba9ab1d6d6dfd8c6ec6b9060200161198e565b336000818152600a6020526040812054611fa4908490614475565b6001600160a01b0383166000908152600a602052604090208190556001549091508111156120075760405162461bcd60e51b815260206004820152601060248201526f746f6f206d616e7920776f726b65727360801b6044820152606401610883565b6120108261230a565b60005b8381101561212057600060098187878581811061203257612032614558565b905060200201602081019061204791906135cb565b6001600160a01b03908116825260208201929092526040016000205416146120b15760405162461bcd60e51b815260206004820152601960248201527f7468697320776f726b6572206861732061206d616e61676572000000000000006044820152606401610883565b82600960008787858181106120c8576120c8614558565b90506020020160208101906120dd91906135cb565b6001600160a01b039081168252602082019290925260400160002080546001600160a01b031916929091169190911790558061211881614511565b915050612013565b50816001600160a01b03167febf4a9bffb39f7c5dbf3f65540183b9381ae226ac3d0a45b4cad484713bd4a2885858460405161215e93929190614056565b60405180910390a250505050565b612174612d4a565b604051633962216760e01b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063396221679060240160206040518083038186803b1580156121d557600080fd5b505afa1580156121e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061220d9190613823565b6122595760405162461bcd60e51b815260206004820181905260248201527f72656c617920736572766572206e6f74206573636865617461626c65207965746044820152606401610883565b6001600160a01b038082166000908152600b6020526040808220805490839055600654909316825290205461228f908290614475565b6006546001600160a01b039081166000908152600b60209081526040918290209390935551838152908416917f3fb672a061b9c3ab083062a9a8ff532d341e5fa42145b91f3922a711ae135659910160405180910390a25050565b606060405180606001604052806022815260200161459a60229139905090565b60405163c345315360e01b81526001600160a01b03828116600483015260009182917f0000000000000000000000000000000000000000000000000000000000000000169063c3453153906024016101006040518083038186803b15801561237157600080fd5b505afa158015612385573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a99190613bda565b60a0820180516001600160a01b039081166000908152600860205260409020549151939550919350911661241a5760405162461bcd60e51b81526020600482015260186024820152771c995b185e481b585b9859d95c881b9bdd081cdd185ad95960421b6044820152606401610883565b825181111561246b5760405162461bcd60e51b815260206004820152601960248201527f7374616b6520616d6f756e7420697320746f6f20736d616c6c000000000000006044820152606401610883565b806124b85760405162461bcd60e51b815260206004820152601f60248201527f7374616b696e67207468697320746f6b656e20697320666f7262696464656e006044820152606401610883565b6005546020840151101561250e5760405162461bcd60e51b815260206004820152601a60248201527f756e7374616b652064656c617920697320746f6f20736d616c6c0000000000006044820152606401610883565b6040830151156125605760405162461bcd60e51b815260206004820152601860248201527f7374616b6520686173206265656e2077697468647261776e00000000000000006044820152606401610883565b816115cb5760405162461bcd60e51b815260206004820181905260248201527f7468697320687562206973206e6f7420617574686f72697a656420627920534d6044820152606401610883565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146126155760405162461bcd60e51b815260206004820152600d60248201526c2737ba103832b730b634bd32b960991b6044820152606401610883565b6001600160a01b0380831660009081526009602052604090205416806126745760405162461bcd60e51b81526020600482015260146024820152732ab735b737bbb7103932b630bc903bb7b935b2b960611b6044820152606401610883565b60405163c345315360e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063c3453153906024016101006040518083038186803b1580156126db57600080fd5b505afa1580156126ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127139190613bda565b5080519091506127605760405162461bcd60e51b81526020600482015260186024820152771c995b185e481b585b9859d95c881b9bdd081cdd185ad95960421b6044820152606401610883565b805160405163026822bd60e21b81526001600160a01b038481166004830152858116602483015260448201929092527f0000000000000000000000000000000000000000000000000000000000000000909116906309a08af490606401600060405180830381600087803b1580156127d757600080fd5b505af11580156127eb573d6000803e3d6000fd5b5050505050505050565b6127fd612d4a565b6001600160a01b0381166128625760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610883565b61286b81612da4565b50565b60408051600180825281830190925260009160208083019080368337505060408051600180825281830190925292935060009291506020808301908036833701905050905082826000815181106128c7576128c7614558565b60200260200101818152505083816000815181106128e7576128e7614558565b60200260200101906001600160a01b031690816001600160a01b0316815250506115cb81836113a8565b60006001600160e01b036129258580614340565b6129339060208101906135cb565b61293d8680614340565b608001358585604051602001612956949392919061402e565b604051602081830303815290604052805190602001201690509392505050565b6000612983826004614475565b835110156129d35760405162461bcd60e51b815260206004820152601a60248201527f726561644279746573343a206461746120746f6f2073686f72740000000000006044820152606401610883565b5001602001516001600160e01b03191690565b612a116040518060800160405280600081526020016000815260200160008152602001600081525090565b6000612a206020850185614356565b612a319060a08101906080016135cb565b6001600160a01b031663b039a88f61c3506040518263ffffffff1660e01b815260040160806040518083038187803b158015612a6c57600080fd5b5086fa158015612a80573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612aa59190613ae2565b6060810151909250361115612afc5760405162461bcd60e51b815260206004820152601760248201527f6d73672e64617461206578636565646564206c696d69740000000000000000006044820152606401610883565b8151851015612b4d5760405162461bcd60e51b815260206004820152601a60248201527f616363657074616e63652062756467657420746f6f20686967680000000000006044820152606401610883565b602082015182511015612ba25760405162461bcd60e51b815260206004820152601960248201527f616363657074616e63652062756467657420746f6f206c6f77000000000000006044820152606401610883565b82612bb06020860186614356565b60400135612bbe9190614475565b90506000612bd3826104c26020880188614356565b9050600b6000612be66020880188614356565b612bf79060a08101906080016135cb565b6001600160a01b03166001600160a01b0316815260200190815260200160002054811115612c675760405162461bcd60e51b815260206004820152601960248201527f5061796d61737465722062616c616e636520746f6f206c6f77000000000000006044820152606401610883565b50935093915050565b6000612c95612c826020880188614356565b612c909060c08101906142fa565b613053565b612cb0612ca28880614340565b612c909060a08101906142fa565b612cba8585613053565b612cc48888613053565b612cce8c8c613053565b612cda906102c4614475565b612ce49190614475565b612cee9190614475565b612cf89190614475565b612d029190614475565b90503681146127eb5760405162461bcd60e51b81526020600482015260146024820152736578747261206d73672e6461746120627974657360601b6044820152606401610883565b6000546001600160a01b031633146113a65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610883565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000818310612e035781612e05565b825b9392505050565b61286b81610400613079565b60008282604051602001612e2d92919061410d565b6040516020818303038152906040529050612e4781612e0c565b8051602082018181fd5b60008060606000612e618761308a565b90506000612e8c89612e7660208b018b614356565b612e879060c081019060a0016135cb565b6130c9565b9050612e9b6020890189614356565b612eac9060c081019060a0016135cb565b6001600160a01b031663e024dc7f60e01b612ec78a80614340565b836040518060400160405280600c81526020016b14995b185e54995c5d595cdd60a21b8152506040518060a00160405280606181526020016145bc606191396040518060e0016040528060bc815260200161461d60bc9139604051602001612f2f9190613f4d565b60408051601f1981840301815290829052612f4e939291602001613eee565b60405160208183030381529060405280519060200120868c8c604051602401612f7c969594939291906141d6565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051612fba9190613ed2565b6000604051808303816000865af19150503d8060008114612ff7576040519150601f19603f3d011682016040523d82523d6000602084013e612ffc565b606091505b5090955092508415613022578280602001905181019061301c919061383e565b90945092505b61302b83612e0c565b50509450945094915050565b60006130428361311c565b8015612e055750612e05838361314f565b6000613062601f6000196144ce565b61306d83601f614475565b612e0591166020614475565b8082511115613086578082525b5050565b60606130a161309c6020840184614356565b6131d8565b6040516020016130b391815260200190565b6040516020818303038152906040529050919050565b6000612e056040518060800160405280858152602001604051806040016040528060018152602001603360f81b81525081526020016131054690565b8152602001846001600160a01b03168152506132d6565b600061312f826301ffc9a760e01b61314f565b80156107635750613148826001600160e01b031961314f565b1592915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03166301ffc9a760e01b178152825160009392849283928392918391908a617530fa92503d915060005190508280156131c1575060208210155b80156131cd5750600081115b979650505050505050565b60006040518060e0016040528060bc815260200161461d60bc9139805160209182012090833590840135604085013561321760808701606088016135cb565b61322760a08801608089016135cb565b61323760c0890160a08a016135cb565b61324460c08a018a6142fa565b604051613252929190613ec2565b6040519081900381206132b9989796959493929160e08c0135906020019889526020890197909752604088019590955260608701939093526001600160a01b039182166080870152811660a08601521660c084015260e08301526101008201526101200190565b604051602081830303815290604052805190602001209050919050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f826000015180519060200120836020015180519060200120846040015185606001516040516020016132b99594939291909485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b60405180610240016040528060001515815260200160006001600160e01b03191681526020016000815260200160608152602001606081526020016133bc6040518060800160405280600081526020016000815260200160008152602001600081525090565b8152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016060815260200160006001600160a01b031681526020016000801916815260200160008152602001606081525090565b803561342d81614584565b919050565b600082601f83011261344357600080fd5b81356020613458613453836143e7565b6143b7565b80838252828201915082860187848660051b890101111561347857600080fd5b60005b858110156134975781358452928401929084019060010161347b565b5090979650505050505050565b8051801515811461342d57600080fd5b60008083601f8401126134c657600080fd5b5081356001600160401b038111156134dd57600080fd5b6020830191508360208285010111156134f557600080fd5b9250929050565b600082601f83011261350d57600080fd5b81516001600160401b038111156135265761352661456e565b613539601f8201601f19166020016143b7565b81815284602083860101111561354e57600080fd5b61355f8260208301602087016144e5565b949350505050565b60006080828403121561357957600080fd5b50919050565b60006040828403121561357957600080fd5b803561ffff8116811461342d57600080fd5b803560ff8116811461342d57600080fd5b80356001600160501b038116811461342d57600080fd5b6000602082840312156135dd57600080fd5b8135612e0581614584565b600080604083850312156135fb57600080fd5b823561360681614584565b946020939093013593505050565b6000806040838503121561362757600080fd5b823561363281614584565b9150602083013561364281614584565b809150509250929050565b6000806020838503121561366057600080fd5b82356001600160401b038082111561367757600080fd5b818501915085601f83011261368b57600080fd5b81358181111561369a57600080fd5b8660208260051b85010111156136af57600080fd5b60209290920196919550909350505050565b600080604083850312156136d457600080fd5b82356001600160401b03808211156136eb57600080fd5b818501915085601f8301126136ff57600080fd5b8135602061370f613453836143e7565b8083825282820191508286018a848660051b890101111561372f57600080fd5b600096505b8487101561375b57803561374781614584565b835260019690960195918301918301613734565b509650508601359250508082111561377257600080fd5b5061377f85828601613432565b9150509250929050565b6000806040838503121561379c57600080fd5b82356001600160401b03808211156137b357600080fd5b818501915085601f8301126137c757600080fd5b813560206137d7613453836143e7565b8083825282820191508286018a848660051b89010111156137f757600080fd5b600096505b8487101561375b57803561380f81614584565b8352600196909601959183019183016137fc565b60006020828403121561383557600080fd5b612e05826134a4565b6000806040838503121561385157600080fd5b61385a836134a4565b915060208301516001600160401b0381111561387557600080fd5b61377f858286016134fc565b60006020828403121561389357600080fd5b81356001600160e01b031981168114612e0557600080fd5b600080604083850312156138be57600080fd5b82516001600160401b038111156138d457600080fd5b6138e0858286016134fc565b9250506138ef602084016134a4565b90509250929050565b6000806040838503121561390b57600080fd5b82516007811061391a57600080fd5b60208401519092506001600160401b0381111561387557600080fd5b6000806000806000806000806000806101408b8d03121561395657600080fd5b8a356001600160401b038082111561396d57600080fd5b6139798e838f016134b4565b909c509a5060208d013591508082111561399257600080fd5b61399e8e838f0161357f565b995060408d01359150808211156139b457600080fd5b6139c08e838f016134b4565b909950975060608d01359150808211156139d957600080fd5b506139e68d828e016134b4565b90965094506139fa90508c60808d01613567565b92506101008b013591506101208b013590509295989b9194979a5092959850565b60008060008060008060008060a0898b031215613a3757600080fd5b88356001600160401b0380821115613a4e57600080fd5b613a5a8c838d016134b4565b909a50985060208b0135975060408b0135915080821115613a7a57600080fd5b613a868c838d0161357f565b965060608b0135915080821115613a9c57600080fd5b613aa88c838d016134b4565b909650945060808b0135915080821115613ac157600080fd5b50613ace8b828c016134b4565b999c989b5096995094979396929594505050565b600060808284031215613af457600080fd5b604051608081018181106001600160401b0382111715613b1657613b1661456e565b8060405250825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b60006101208284031215613b5a57600080fd5b613b6261436c565b8235815260208301356020820152604083013560408201526060830135606082015260808301356080820152613b9a60a08401613422565b60a0820152613bab60c084016135a3565b60c0820152613bbc60e084016135b4565b60e0820152610100613bcf818501613591565b908201529392505050565b600080828403610100811215613bef57600080fd5b60e0811215613bfd57600080fd5b50613c06614395565b835181526020840151602082015260408401516040820152606084015160608201526080840151608082015260a0840151613c4081614584565b60a082015260c0840151613c5381614584565b60c082015291506138ef60e084016134a4565b600060208284031215613c7857600080fd5b5035919050565b60008060408385031215613c9257600080fd5b8235915060208301356001600160401b03811115613caf57600080fd5b8301610100818603121561364257600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008151808452613d038160208601602086016144e5565b601f01601f19169290920160200192915050565b60078110613d3557634e487b7160e01b600052602160045260246000fd5b9052565b60008135613d4681614584565b6001600160a01b039081168452602083013590613d6282614584565b166020840152604082810135908401526060808301359084015260808083013590840152613d9360a083018361440a565b60e060a0860152613da860e086018284613cc2565b91505060c083013560c08501528091505092915050565b60006101008235845260208301356020850152604083013560408501526060830135613dea81614584565b6001600160a01b039081166060860152608084013590613e0982614584565b16608085015260a0830135613e1d81614584565b6001600160a01b031660a0850152613e3860c084018461440a565b8260c0870152613e4b8387018284613cc2565b9250505060e083013560e08501528091505092915050565b6000813582360360de1981018212613e7a57600080fd5b60408552613e8d60408601858401613d39565b9150602084013560fe1982018112613ea457600080fd5b8583036020870152613eb883868301613dbf565b9695505050505050565b8183823760009101908152919050565b60008251613ee48184602087016144e5565b9190910192915050565b60008451613f008184602089016144e5565b600560fb1b9083019081528451613f1e8160018401602089016144e5565b600b60fa1b600192909101918201528351613f408160028401602088016144e5565b0160020195945050505050565b7352656c6179446174612072656c6179446174612960601b815260008251613f7c8160148501602087016144e5565b9190910160140192915050565b6001600160a01b0387811682528681166020830152851660408201526001600160e01b03198416606082015260c08101613fc66080830185613d17565b8260a0830152979650505050505050565b6001600160a01b0387811682528681166020830152851660408201526001600160e01b0319841660608201526080810183905260c060a0820181905260009061402290830184613ceb565b98975050505050505050565b60018060a01b0385168152836020820152606060408201526000613eb8606083018486613cc2565b6040808252810183905260008460608301825b8681101561409957823561407c81614584565b6001600160a01b0316825260209283019290910190600101614069565b5060209390930193909352509392505050565b84151581528360208201526140c46040820184613d17565b608060608201526000613eb86080830184613ceb565b6080815260006140ed6080830187613ceb565b851515602084015284604084015282810360608401526131cd8185613dbf565b6141178184613d17565b60406020820152600061355f6040830184613ceb565b60006101408083526141428184018d8f613cc2565b90508281036020840152614156818c613e63565b9050828103604084015261416b818a8c613cc2565b9050828103606084015261418081888a613cc2565b86516080850152602087015160a0850152604087015160c085015260609096015160e0840152505061010081019290925261012090910152979650505050505050565b602081526000612e056020830184613ceb565b60a0815260006141e960a0830189613d39565b87602084015286604084015282810360608401526142078187613ceb565b9050828103608084015261421c818587613cc2565b9998505050505050505050565b600061012082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260018060a01b0360a08401511660a083015260ff60c08401511660c083015260e083015161429760e08401826001600160501b03169052565b506101009283015161ffff16919092015290565b6080815260006142be6080830189613e63565b82810360208401526142d181888a613cc2565b905082810360408401526142e6818688613cc2565b915050826060830152979650505050505050565b6000808335601e1984360301811261431157600080fd5b8301803591506001600160401b0382111561432b57600080fd5b6020019150368190038213156134f557600080fd5b6000823560de19833603018112613ee457600080fd5b6000823560fe19833603018112613ee457600080fd5b60405161012081016001600160401b038111828210171561438f5761438f61456e565b60405290565b60405160e081016001600160401b038111828210171561438f5761438f61456e565b604051601f8201601f191681016001600160401b03811182821017156143df576143df61456e565b604052919050565b60006001600160401b038211156144005761440061456e565b5060051b60200190565b6000808335601e1984360301811261442157600080fd5b83016020810192503590506001600160401b0381111561444057600080fd5b8036038313156134f557600080fd5b600061ffff80831681851680830382111561446c5761446c61452c565b01949350505050565b600082198211156144885761448861452c565b500190565b6000826144aa57634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156144c9576144c961452c565b500290565b6000828210156144e0576144e061452c565b500390565b60005b838110156145005781810151838201526020016144e8565b838111156115cb5750506000910152565b60006000198214156145255761452561452c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461286b57600080fdfe332e302e302d626574612e332b6f70656e67736e2e6875622e6972656c6179687562616464726573732066726f6d2c6164647265737320746f2c75696e743235362076616c75652c75696e74323536206761732c75696e74323536206e6f6e63652c627974657320646174612c75696e743235362076616c6964556e74696c54696d6552656c6179446174612875696e74323536206d61784665655065724761732c75696e74323536206d61785072696f726974794665655065724761732c75696e74323536207472616e73616374696f6e43616c6c64617461476173557365642c616464726573732072656c6179576f726b65722c61646472657373207061796d61737465722c6164647265737320666f727761726465722c6279746573207061796d6173746572446174612c75696e7432353620636c69656e74496429a26469706673582212207d6bd8e535b6e7fa593f9f748b1320db033eca884c06f56c96553762c6d07ff264736f6c634300080700330000000000000000000000004424fff6cfaa1c1a51975be65f371e10276ff5bb000000000000000000000000064ddcc99c7d0fdc8e2ab4f9330d5f603f9a843500000000000000000000000000000000000000000000000000000000000000000000000000000000000000009dc769b8cbd07131227b0815bed3526b1f8acd52000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000446c000000000000000000000000000000000000000000000000000000000000da650000000000000000000000000000000000000000000000000000000000003a98000000000000000000000000d21934ed8eaf27a67f0a70042af50a1d6d195e81000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000000000000000000032
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004424fff6cfaa1c1a51975be65f371e10276ff5bb000000000000000000000000064ddcc99c7d0fdc8e2ab4f9330d5f603f9a843500000000000000000000000000000000000000000000000000000000000000000000000000000000000000009dc769b8cbd07131227b0815bed3526b1f8acd52000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000446c000000000000000000000000000000000000000000000000000000000000da650000000000000000000000000000000000000000000000000000000000003a98000000000000000000000000d21934ed8eaf27a67f0a70042af50a1d6d195e81000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000000000000000000032
-----Decoded View---------------
Arg [0] : _stakeManager (address): 0x4424fFF6cfaA1c1A51975BE65F371e10276ff5BB
Arg [1] : _penalizer (address): 0x064Ddcc99C7D0FDC8E2aB4f9330d5F603F9A8435
Arg [2] : _batchGateway (address): 0x0000000000000000000000000000000000000000
Arg [3] : _relayRegistrar (address): 0x9dC769B8cBD07131227b0815BEd3526b1f8ACD52
Arg [4] : _config (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
13 Constructor Arguments found :
Arg [0] : 0000000000000000000000004424fff6cfaa1c1a51975be65f371e10276ff5bb
Arg [1] : 000000000000000000000000064ddcc99c7d0fdc8e2ab4f9330d5f603f9a8435
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 0000000000000000000000009dc769b8cbd07131227b0815bed3526b1f8acd52
Arg [4] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [5] : 00000000000000000000000000000000000000000000000000000000000186a0
Arg [6] : 000000000000000000000000000000000000000000000000000000000000446c
Arg [7] : 000000000000000000000000000000000000000000000000000000000000da65
Arg [8] : 0000000000000000000000000000000000000000000000000000000000003a98
Arg [9] : 000000000000000000000000d21934ed8eaf27a67f0a70042af50a1d6d195e81
Arg [10] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [11] : 00000000000000000000000000000000000000000000000000038d7ea4c68000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000032