123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.0;
- import "../utils/Context.sol";
- import "./IRelayRecipient.sol";
- import "./IRelayHub.sol";
- /**
- * @dev Base GSN recipient contract: includes the {IRelayRecipient} interface
- * and enables GSN support on all contracts in the inheritance tree.
- *
- * TIP: This contract is abstract. The functions {IRelayRecipient-acceptRelayedCall},
- * {_preRelayedCall}, and {_postRelayedCall} are not implemented and must be
- * provided by derived contracts. See the
- * xref:ROOT:gsn-strategies.adoc#gsn-strategies[GSN strategies] for more
- * information on how to use the pre-built {GSNRecipientSignature} and
- * {GSNRecipientERC20Fee}, or how to write your own.
- */
- abstract contract GSNRecipient is IRelayRecipient, Context {
- // Default RelayHub address, deployed on mainnet and all testnets at the same address
- address private _relayHub = 0xD216153c06E857cD7f72665E0aF1d7D82172F494;
- uint256 constant private _RELAYED_CALL_ACCEPTED = 0;
- uint256 constant private _RELAYED_CALL_REJECTED = 11;
- // How much gas is forwarded to postRelayedCall
- uint256 constant internal _POST_RELAYED_CALL_MAX_GAS = 100000;
- /**
- * @dev Emitted when a contract changes its {IRelayHub} contract to a new one.
- */
- event RelayHubChanged(address indexed oldRelayHub, address indexed newRelayHub);
- /**
- * @dev Returns the address of the {IRelayHub} contract for this recipient.
- */
- function getHubAddr() public view virtual override returns (address) {
- return _relayHub;
- }
- /**
- * @dev Switches to a new {IRelayHub} instance. This method is added for future-proofing: there's no reason to not
- * use the default instance.
- *
- * IMPORTANT: After upgrading, the {GSNRecipient} will no longer be able to receive relayed calls from the old
- * {IRelayHub} instance. Additionally, all funds should be previously withdrawn via {_withdrawDeposits}.
- */
- function _upgradeRelayHub(address newRelayHub) internal virtual {
- address currentRelayHub = _relayHub;
- require(newRelayHub != address(0), "GSNRecipient: new RelayHub is the zero address");
- require(newRelayHub != currentRelayHub, "GSNRecipient: new RelayHub is the current one");
- emit RelayHubChanged(currentRelayHub, newRelayHub);
- _relayHub = newRelayHub;
- }
- /**
- * @dev Returns the version string of the {IRelayHub} for which this recipient implementation was built. If
- * {_upgradeRelayHub} is used, the new {IRelayHub} instance should be compatible with this version.
- */
- // This function is view for future-proofing, it may require reading from
- // storage in the future.
- function relayHubVersion() public view virtual returns (string memory) {
- this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
- return "1.0.0";
- }
- /**
- * @dev Withdraws the recipient's deposits in `RelayHub`.
- *
- * Derived contracts should expose this in an external interface with proper access control.
- */
- function _withdrawDeposits(uint256 amount, address payable payee) internal virtual {
- IRelayHub(getHubAddr()).withdraw(amount, payee);
- }
- // Overrides for Context's functions: when called from RelayHub, sender and
- // data require some pre-processing: the actual sender is stored at the end
- // of the call data, which in turns means it needs to be removed from it
- // when handling said data.
- /**
- * @dev Replacement for msg.sender. Returns the actual sender of a transaction: msg.sender for regular transactions,
- * and the end-user for GSN relayed calls (where msg.sender is actually `RelayHub`).
- *
- * IMPORTANT: Contracts derived from {GSNRecipient} should never use `msg.sender`, and use {_msgSender} instead.
- */
- function _msgSender() internal view virtual override returns (address msgSender) {
- if (msg.sender == getHubAddr()) {
- assembly { msgSender := shr(96, calldataload(sub(calldatasize(), 20))) }
- } else {
- return msg.sender;
- }
- }
- /**
- * @dev Replacement for msg.data. Returns the actual calldata of a transaction: msg.data for regular transactions,
- * and a reduced version for GSN relayed calls (where msg.data contains additional information).
- *
- * IMPORTANT: Contracts derived from {GSNRecipient} should never use `msg.data`, and use {_msgData} instead.
- */
- function _msgData() internal view virtual override returns (bytes calldata) {
- if (msg.sender == getHubAddr()) {
- return msg.data[:msg.data.length - 20];
- } else {
- return msg.data;
- }
- }
- // Base implementations for pre and post relayedCall: only RelayHub can invoke them, and data is forwarded to the
- // internal hook.
- /**
- * @dev See `IRelayRecipient.preRelayedCall`.
- *
- * This function should not be overridden directly, use `_preRelayedCall` instead.
- *
- * * Requirements:
- *
- * - the caller must be the `RelayHub` contract.
- */
- function preRelayedCall(bytes memory context) public virtual override returns (bytes32) {
- require(msg.sender == getHubAddr(), "GSNRecipient: caller is not RelayHub");
- return _preRelayedCall(context);
- }
- /**
- * @dev See `IRelayRecipient.preRelayedCall`.
- *
- * Called by `GSNRecipient.preRelayedCall`, which asserts the caller is the `RelayHub` contract. Derived contracts
- * must implement this function with any relayed-call preprocessing they may wish to do.
- *
- */
- function _preRelayedCall(bytes memory context) internal virtual returns (bytes32);
- /**
- * @dev See `IRelayRecipient.postRelayedCall`.
- *
- * This function should not be overridden directly, use `_postRelayedCall` instead.
- *
- * * Requirements:
- *
- * - the caller must be the `RelayHub` contract.
- */
- function postRelayedCall(bytes memory context, bool success, uint256 actualCharge, bytes32 preRetVal) public virtual override {
- require(msg.sender == getHubAddr(), "GSNRecipient: caller is not RelayHub");
- _postRelayedCall(context, success, actualCharge, preRetVal);
- }
- /**
- * @dev See `IRelayRecipient.postRelayedCall`.
- *
- * Called by `GSNRecipient.postRelayedCall`, which asserts the caller is the `RelayHub` contract. Derived contracts
- * must implement this function with any relayed-call postprocessing they may wish to do.
- *
- */
- function _postRelayedCall(bytes memory context, bool success, uint256 actualCharge, bytes32 preRetVal) internal virtual;
- /**
- * @dev Return this in acceptRelayedCall to proceed with the execution of a relayed call. Note that this contract
- * will be charged a fee by RelayHub
- */
- function _approveRelayedCall() internal pure virtual returns (uint256, bytes memory) {
- return _approveRelayedCall("");
- }
- /**
- * @dev See `GSNRecipient._approveRelayedCall`.
- *
- * This overload forwards `context` to _preRelayedCall and _postRelayedCall.
- */
- function _approveRelayedCall(bytes memory context) internal pure virtual returns (uint256, bytes memory) {
- return (_RELAYED_CALL_ACCEPTED, context);
- }
- /**
- * @dev Return this in acceptRelayedCall to impede execution of a relayed call. No fees will be charged.
- */
- function _rejectRelayedCall(uint256 errorCode) internal pure virtual returns (uint256, bytes memory) {
- return (_RELAYED_CALL_REJECTED + errorCode, "");
- }
- /*
- * @dev Calculates how much RelayHub will charge a recipient for using `gas` at a `gasPrice`, given a relayer's
- * `serviceFee`.
- */
- function _computeCharge(uint256 gas, uint256 gasPrice, uint256 serviceFee) internal pure virtual returns (uint256) {
- // The fee is expressed as a percentage. E.g. a value of 40 stands for a 40% fee, so the recipient will be
- // charged for 1.4 times the spent amount.
- return (gas * gasPrice * (100 + serviceFee)) / 100;
- }
- }
|