735546619e
Signed-off-by: T-Hax <>
231 lines
9.6 KiB
Solidity
231 lines
9.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.6.0;
|
|
|
|
import "./IRelayRecipient.sol";
|
|
import "./IRelayHub.sol";
|
|
import "./Context.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 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 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(_relayHub).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 payable) {
|
|
if (msg.sender != _relayHub) {
|
|
return msg.sender;
|
|
} else {
|
|
return _getRelayedCallSender();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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 memory) {
|
|
if (msg.sender != _relayHub) {
|
|
return msg.data;
|
|
} else {
|
|
return _getRelayedCallData();
|
|
}
|
|
}
|
|
|
|
// 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 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 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 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 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;
|
|
}
|
|
|
|
function _getRelayedCallSender() private pure returns (address payable result) {
|
|
// We need to read 20 bytes (an address) located at array index msg.data.length - 20. In memory, the array
|
|
// is prefixed with a 32-byte length value, so we first add 32 to get the memory read index. However, doing
|
|
// so would leave the address in the upper 20 bytes of the 32-byte word, which is inconvenient and would
|
|
// require bit shifting. We therefore subtract 12 from the read index so the address lands on the lower 20
|
|
// bytes. This can always be done due to the 32-byte prefix.
|
|
|
|
// The final memory read index is msg.data.length - 20 + 32 - 12 = msg.data.length. Using inline assembly is the
|
|
// easiest/most-efficient way to perform this operation.
|
|
|
|
// These fields are not accessible from assembly
|
|
bytes memory array = msg.data;
|
|
uint256 index = msg.data.length;
|
|
|
|
// solhint-disable-next-line no-inline-assembly
|
|
assembly {
|
|
// Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
|
|
result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function _getRelayedCallData() private pure returns (bytes memory) {
|
|
// RelayHub appends the sender address at the end of the calldata, so in order to retrieve the actual msg.data,
|
|
// we must strip the last 20 bytes (length of an address type) from it.
|
|
|
|
uint256 actualDataLength = msg.data.length - 20;
|
|
bytes memory actualData = new bytes(actualDataLength);
|
|
|
|
for (uint256 i = 0; i < actualDataLength; ++i) {
|
|
actualData[i] = msg.data[i];
|
|
}
|
|
|
|
return actualData;
|
|
}
|
|
}
|