// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "./utils/GsnTypes.sol";
import "./interfaces/IPaymaster.sol";
import "./interfaces/IRelayHub.sol";
import "./utils/GsnEip712Library.sol";
import "./forwarder/IForwarder.sol";
/**
* @notice An abstract base class to be inherited by a concrete Paymaster.
* A subclass must implement:
* - preRelayedCall
* - postRelayedCall
*/
abstract contract BasePaymaster is IPaymaster, Ownable, ERC165 {
using ERC165Checker for address;
IRelayHub internal relayHub;
address private _trustedForwarder;
/// @inheritdoc IPaymaster
function getRelayHub() public override view returns (address) {
return address(relayHub);
}
//overhead of forwarder verify+signature, plus hub overhead.
uint256 constant public FORWARDER_HUB_OVERHEAD = 50000;
//These parameters are documented in IPaymaster.GasAndDataLimits
uint256 constant public PRE_RELAYED_CALL_GAS_LIMIT = 100000;
uint256 constant public POST_RELAYED_CALL_GAS_LIMIT = 110000;
uint256 constant public PAYMASTER_ACCEPTANCE_BUDGET = PRE_RELAYED_CALL_GAS_LIMIT + FORWARDER_HUB_OVERHEAD;
uint256 constant public CALLDATA_SIZE_LIMIT = 10500;
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IPaymaster).interfaceId ||
interfaceId == type(Ownable).interfaceId ||
super.supportsInterface(interfaceId);
}
/// @inheritdoc IPaymaster
function getGasAndDataLimits()
public
override
virtual
view
returns (
IPaymaster.GasAndDataLimits memory limits
) {
return IPaymaster.GasAndDataLimits(
PAYMASTER_ACCEPTANCE_BUDGET,
PRE_RELAYED_CALL_GAS_LIMIT,
POST_RELAYED_CALL_GAS_LIMIT,
CALLDATA_SIZE_LIMIT
);
}
/**
* @notice this method must be called from preRelayedCall to validate that the forwarder
* is approved by the paymaster as well as by the recipient contract.
*/
function _verifyForwarder(GsnTypes.RelayRequest calldata relayRequest)
internal
virtual
view
{
require(getTrustedForwarder() == relayRequest.relayData.forwarder, "Forwarder is not trusted");
GsnEip712Library.verifyForwarderTrusted(relayRequest);
}
function _verifyRelayHubOnly() internal virtual view {
require(msg.sender == getRelayHub(), "can only be called by RelayHub");
}
function _verifyValue(GsnTypes.RelayRequest calldata relayRequest) internal virtual view{
require(relayRequest.request.value == 0, "value transfer not supported");
}
function _verifyPaymasterData(GsnTypes.RelayRequest calldata relayRequest) internal virtual view {
require(relayRequest.relayData.paymasterData.length == 0, "should have no paymasterData");
}
function _verifyApprovalData(bytes calldata approvalData) internal virtual view{
require(approvalData.length == 0, "should have no approvalData");
}
/**
* @notice The owner of the Paymaster can change the instance of the RelayHub this Paymaster works with.
* :warning: **Warning** :warning: The deposit on the previous RelayHub must be withdrawn first.
*/
function setRelayHub(IRelayHub hub) public onlyOwner {
require(address(hub).supportsInterface(type(IRelayHub).interfaceId), "target is not a valid IRelayHub");
relayHub = hub;
}
/**
* @notice The owner of the Paymaster can change the instance of the Forwarder this Paymaster works with.
* @notice the Recipients must trust this Forwarder as well in order for the configuration to remain functional.
*/
function setTrustedForwarder(address forwarder) public virtual onlyOwner {
require(forwarder.supportsInterface(type(IForwarder).interfaceId), "target is not a valid IForwarder");
_trustedForwarder = forwarder;
}
function getTrustedForwarder() public virtual view override returns (address){
return _trustedForwarder;
}
/**
* @notice Any native Ether transferred into the paymaster is transferred as a deposit to the RelayHub.
* This way, we don't need to understand the RelayHub API in order to replenish the paymaster.
*/
receive() external virtual payable {
require(address(relayHub) != address(0), "relay hub address not set");
relayHub.depositFor{value:msg.value}(address(this));
}
/**
* @notice Withdraw deposit from the RelayHub.
* @param amount The amount to be subtracted from the sender.
* @param target The target to which the amount will be transferred.
*/
function withdrawRelayHubDepositTo(uint256 amount, address payable target) public onlyOwner {
relayHub.withdraw(target, amount);
}
/// @inheritdoc IPaymaster
function preRelayedCall(
GsnTypes.RelayRequest calldata relayRequest,
bytes calldata signature,
bytes calldata approvalData,
uint256 maxPossibleGas
)
external
override
returns (bytes memory, bool) {
_verifyRelayHubOnly();
_verifyForwarder(relayRequest);
_verifyValue(relayRequest);
_verifyPaymasterData(relayRequest);
_verifyApprovalData(approvalData);
return _preRelayedCall(relayRequest, signature, approvalData, maxPossibleGas);
}
/**
* @notice internal logic the paymasters need to provide to select which transactions they are willing to pay for
* @notice see the documentation for `IPaymaster::preRelayedCall` for details
*/
function _preRelayedCall(
GsnTypes.RelayRequest calldata,
bytes calldata,
bytes calldata,
uint256
)
internal
virtual
returns (bytes memory, bool);
/// @inheritdoc IPaymaster
function postRelayedCall(
bytes calldata context,
bool success,
uint256 gasUseWithoutPost,
GsnTypes.RelayData calldata relayData
)
external
override
{
_verifyRelayHubOnly();
_postRelayedCall(context, success, gasUseWithoutPost, relayData);
}
/**
* @notice internal logic the paymasters need to provide if they need to take some action after the transaction
* @notice see the documentation for `IPaymaster::postRelayedCall` for details
*/
function _postRelayedCall(
bytes calldata,
bool,
uint256,
GsnTypes.RelayData calldata
)
internal
virtual;
}
// 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.8.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
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
!supportsERC165InterfaceUnchecked(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) && supportsERC165InterfaceUnchecked(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] = supportsERC165InterfaceUnchecked(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 (!supportsERC165InterfaceUnchecked(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}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal 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: GPL-3.0-only
pragma solidity ^0.8.0;
pragma abicoder v2;
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
));
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;
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;
}
}
/* solhint-disable no-inline-assembly */
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;
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))
}
}
}
// 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
pragma solidity >=0.6.0;
/**
* @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);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.6;
pragma abicoder v2;
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;
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.6;
pragma abicoder v2;
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);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.6;
pragma abicoder v2;
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);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.6;
pragma abicoder v2;
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.6.2;
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
pragma solidity >=0.6.2;
import './IUniswapV2Router01.sol';
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
// SPDX-License-Identifier: MIT
// minimal bytes manipulation required by GSN
// a minimal subset from 0x/LibBytes
/* solhint-disable no-inline-assembly */
pragma solidity ^0.8.0;
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;
}
}
// 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: UNLICENSED
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {BasePaymaster, IPaymaster, IRelayHub, IForwarder, GsnTypes} from "@opengsn/contracts/src/BasePaymaster.sol";
import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
/**
* A Token-based paymaster.
* - each request is paid for by the caller.
* - preRelayedCall - pre-pay the maximum possible price for the tx
* - postRelayedCall - refund the caller for the unused gas
*/
contract TokenPaymasterERC20toAny is BasePaymaster {
function versionPaymaster()
external
view
virtual
override
returns (string memory)
{
return "3.0.0+opengsn.token.ipaymaster";
}
IUniswapV2Router02 private immutable _router;
mapping(address => bool) private _isTokenWhitelisted;
IERC20 private _paymentToken;
uint256 private _fee;
address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 public gasUsedByPost = 200000;
uint256 public minGas =
gasUsedByPost + PRE_RELAYED_CALL_GAS_LIMIT + FORWARDER_HUB_OVERHEAD;
address public target;
uint256 public minBalance = 0.02 ether;
function getGasAndDataLimits()
public
view
virtual
override
returns (IPaymaster.GasAndDataLimits memory limits)
{
return
IPaymaster.GasAndDataLimits(
PRE_RELAYED_CALL_GAS_LIMIT + FORWARDER_HUB_OVERHEAD,
PRE_RELAYED_CALL_GAS_LIMIT,
gasUsedByPost,
CALLDATA_SIZE_LIMIT
);
}
constructor(
address uniswapRouter,
address forwarder,
IERC20 paymentToken,
uint256 fee,
IRelayHub hub
) {
_router = IUniswapV2Router02(uniswapRouter);
_paymentToken = paymentToken;
_fee = fee;
setTrustedForwarder(forwarder);
setRelayHub(hub);
}
function setMinBalance(uint256 _minBalance) external onlyOwner {
require(_minBalance > 0, "Wrong min balance");
minBalance = _minBalance;
}
function setPaymentToken(address paymentToken) external onlyOwner {
require(paymentToken != address(0), "Wrong Payment Token");
_paymentToken = IERC20(paymentToken);
}
function getPaymentData() external view returns (address, uint256) {
return (address(_paymentToken), _fee);
}
function setFee(uint256 fee) external onlyOwner {
_fee = fee;
}
function whitelistToken(address token, bool whitelist) external onlyOwner {
require(token != address(0), "Token address is 0");
_isTokenWhitelisted[token] = whitelist;
}
function isTokenWhitelisted(address token) external view returns (bool) {
return _isTokenWhitelisted[token];
}
function setGasUsedByPost(uint256 _gasUsedByPost) external onlyOwner {
gasUsedByPost = _gasUsedByPost;
}
function setMinGas(uint256 _minGas) external onlyOwner {
minGas = _minGas;
}
function setTarget(address _target) external onlyOwner {
target = _target;
}
function _verifyPreRelayed(
IForwarder.ForwardRequest calldata request,
address tokenIn,
uint256 amount
) private view {
require(request.to == target, "Unknown target");
require(_isTokenWhitelisted[tokenIn], "Token not whitelisted");
require(
relayHub.balanceOf(address(this)) >= minBalance,
"Not enough balance on Paymaster"
);
address from = request.from;
if (address(_paymentToken) == tokenIn) {
require(
_paymentToken.allowance(from, target) >= _fee + amount,
"Fee+amount: Not enough allowance"
);
require(
_paymentToken.balanceOf(from) >= _fee + amount,
"Fee+amount: Not enough balance"
);
} else {
require(
_paymentToken.allowance(from, target) >= _fee,
"Fee: Not enough allowance"
);
require(
_paymentToken.balanceOf(from) >= _fee,
"Fee: Not enough balance"
);
IERC20 token = IERC20(tokenIn);
require(
token.allowance(from, target) >= amount,
"Not enough allowance"
);
require(token.balanceOf(from) >= amount, "Not enough balance");
}
}
function _preRelayedCall(
GsnTypes.RelayRequest calldata relayRequest,
bytes calldata,
bytes calldata,
uint256 maxPossibleGas
) internal virtual override returns (bytes memory, bool) {
IForwarder.ForwardRequest calldata request = relayRequest.request;
(address[] memory path, uint256 amount) = abi.decode(
request.data[4:],
(address[], uint256)
);
require(path.length >= 1, "Wrong path");
_verifyPreRelayed(request, path[0], amount);
uint256 ethMaxCharge = relayHub.calculateCharge(
maxPossibleGas + gasUsedByPost,
relayRequest.relayData
);
uint256 pathLength = path.length;
if (path[pathLength - 1] == WETH) {
require(
_router.getAmountsOut(amount, path)[pathLength - 1] >
ethMaxCharge,
"Not enough to pay for tx"
);
} else {
address[] memory newPath = new address[](pathLength + 1);
for (uint256 i = 0; i < pathLength; i++) {
newPath[i] = path[i];
}
newPath[pathLength] = WETH;
require(
_router.getAmountsOut(amount, newPath)[pathLength] >
ethMaxCharge,
"Not enough to pay for tx"
);
}
require(request.gas >= minGas, "Not enough gas");
return (
abi.encode(
request.from,
path[pathLength - 1],
path[pathLength - 1] == WETH
),
false
);
}
function _postRelayedCall(
bytes calldata context,
bool success,
uint256 gasUsedWithoutPost,
GsnTypes.RelayData calldata relayData
) internal virtual override {
require(success, "No success");
(address payer, address tokenOut, bool toEth) = abi.decode(
context,
(address, address, bool)
);
IRelayHub _relayHub = relayHub;
uint256 ethActualCharge = _relayHub.calculateCharge(
gasUsedWithoutPost + gasUsedByPost,
relayData
);
if (toEth) {
uint256 balance = address(this).balance;
if (balance > ethActualCharge) {
payable(payer).transfer(balance - ethActualCharge);
}
} else {
address[] memory path = new address[](2);
path[0] = tokenOut;
path[1] = WETH;
uint256 balance = IERC20(tokenOut).balanceOf(address(this));
IERC20(tokenOut).approve(address(_router), balance);
uint256 tookTokenOut = _router.swapTokensForExactETH(
ethActualCharge,
balance,
path,
address(this),
// solhint-disable-next-line not-rely-on-time
block.timestamp
)[0];
if (balance > tookTokenOut) {
IERC20(tokenOut).transfer(payer, balance - tookTokenOut);
}
}
relayHub.depositFor{value: ethActualCharge}(address(this));
}
receive() external payable override {
emit Received(msg.value);
}
event Received(uint256 eth);
}
{
"compilationTarget": {
"contracts/ERC20toAny/TokenPaymasterERC20toAny.sol": "TokenPaymasterERC20toAny"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"uniswapRouter","type":"address"},{"internalType":"address","name":"forwarder","type":"address"},{"internalType":"contract IERC20","name":"paymentToken","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"contract IRelayHub","name":"hub","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":[{"indexed":false,"internalType":"uint256","name":"eth","type":"uint256"}],"name":"Received","type":"event"},{"inputs":[],"name":"CALLDATA_SIZE_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FORWARDER_HUB_OVERHEAD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAYMASTER_ACCEPTANCE_BUDGET","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POST_RELAYED_CALL_GAS_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRE_RELAYED_CALL_GAS_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasUsedByPost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGasAndDataLimits","outputs":[{"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":"limits","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPaymentData","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRelayHub","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTrustedForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isTokenWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minGas","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"context","type":"bytes"},{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"gasUseWithoutPost","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":"postRelayedCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"gas","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"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"},{"internalType":"uint256","name":"maxPossibleGas","type":"uint256"}],"name":"preRelayedCall","outputs":[{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasUsedByPost","type":"uint256"}],"name":"setGasUsedByPost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minBalance","type":"uint256"}],"name":"setMinBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minGas","type":"uint256"}],"name":"setMinGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"paymentToken","type":"address"}],"name":"setPaymentToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IRelayHub","name":"hub","type":"address"}],"name":"setRelayHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"}],"name":"setTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"setTrustedForwarder","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":[],"name":"target","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"versionPaymaster","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"whitelist","type":"bool"}],"name":"whitelistToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"target","type":"address"}],"name":"withdrawRelayHubDepositTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]