|
@@ -1,9 +1,9 @@
|
|
|
pragma solidity ^0.5.0;
|
|
|
|
|
|
import "./IRelayRecipient.sol";
|
|
|
-import "./GSNContext.sol";
|
|
|
-import "./bouncers/GSNBouncerBase.sol";
|
|
|
import "./IRelayHub.sol";
|
|
|
+import "./Context.sol";
|
|
|
+import "./bouncers/GSNBouncerBase.sol";
|
|
|
|
|
|
/**
|
|
|
* @dev Base GSN recipient contract: includes the {IRelayRecipient} interface and enables GSN support on all contracts
|
|
@@ -11,16 +11,42 @@ import "./IRelayHub.sol";
|
|
|
*
|
|
|
* Not all interface methods are implemented (e.g. {acceptRelayedCall}, derived contracts must provide one themselves.
|
|
|
*/
|
|
|
-contract GSNRecipient is IRelayRecipient, GSNContext, GSNBouncerBase {
|
|
|
+contract GSNRecipient is IRelayRecipient, Context, GSNBouncerBase {
|
|
|
+ // Default RelayHub address, deployed on mainnet and all testnets at the same address
|
|
|
+ address private _relayHub = 0xD216153c06E857cD7f72665E0aF1d7D82172F494;
|
|
|
+
|
|
|
/**
|
|
|
- * @dev Returns the `RelayHub` address for this recipient contract.
|
|
|
+ * @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 returns (address) {
|
|
|
return _relayHub;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @dev Returns the version string of the `RelayHub` for which this recipient implementation was built.
|
|
|
+ * @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 {
|
|
|
+ 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.
|
|
@@ -37,4 +63,73 @@ contract GSNRecipient is IRelayRecipient, GSNContext, GSNBouncerBase {
|
|
|
function _withdrawDeposits(uint256 amount, address payable payee) internal {
|
|
|
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 returns (address) {
|
|
|
+ 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 returns (bytes memory) {
|
|
|
+ if (msg.sender != _relayHub) {
|
|
|
+ return msg.data;
|
|
|
+ } else {
|
|
|
+ return _getRelayedCallData();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function _getRelayedCallSender() private pure returns (address 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;
|
|
|
+ }
|
|
|
}
|