GSNRecipient.sol 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. pragma solidity ^0.5.0;
  2. import "./IRelayRecipient.sol";
  3. import "./IRelayHub.sol";
  4. import "./Context.sol";
  5. import "./bouncers/GSNBouncerBase.sol";
  6. /**
  7. * @dev Base GSN recipient contract: includes the {IRelayRecipient} interface and enables GSN support on all contracts
  8. * in the inheritance tree.
  9. *
  10. * Not all interface methods are implemented (e.g. {acceptRelayedCall}, derived contracts must provide one themselves.
  11. */
  12. contract GSNRecipient is IRelayRecipient, Context, GSNBouncerBase {
  13. // Default RelayHub address, deployed on mainnet and all testnets at the same address
  14. address private _relayHub = 0xD216153c06E857cD7f72665E0aF1d7D82172F494;
  15. /**
  16. * @dev Emitted when a contract changes its {IRelayHub} contract to a new one.
  17. */
  18. event RelayHubChanged(address indexed oldRelayHub, address indexed newRelayHub);
  19. /**
  20. * @dev Returns the address of the {IRelayHub} contract for this recipient.
  21. */
  22. function getHubAddr() public view returns (address) {
  23. return _relayHub;
  24. }
  25. /**
  26. * @dev Switches to a new {IRelayHub} instance. This method is added for future-proofing: there's no reason to not
  27. * use the default instance.
  28. *
  29. * IMPORTANT: After upgrading, the {GSNRecipient} will no longer be able to receive relayed calls from the old
  30. * {IRelayHub} instance. Additionally, all funds should be previously withdrawn via {_withdrawDeposits}.
  31. */
  32. function _upgradeRelayHub(address newRelayHub) internal {
  33. address currentRelayHub = _relayHub;
  34. require(newRelayHub != address(0), "GSNRecipient: new RelayHub is the zero address");
  35. require(newRelayHub != currentRelayHub, "GSNRecipient: new RelayHub is the current one");
  36. emit RelayHubChanged(currentRelayHub, newRelayHub);
  37. _relayHub = newRelayHub;
  38. }
  39. /**
  40. * @dev Returns the version string of the {IRelayHub} for which this recipient implementation was built. If
  41. * {_upgradeRelayHub} is used, the new {IRelayHub} instance should be compatible with this version.
  42. */
  43. // This function is view for future-proofing, it may require reading from
  44. // storage in the future.
  45. function relayHubVersion() public view returns (string memory) {
  46. this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
  47. return "1.0.0";
  48. }
  49. /**
  50. * @dev Withdraws the recipient's deposits in `RelayHub`.
  51. *
  52. * Derived contracts should expose this in an external interface with proper access control.
  53. */
  54. function _withdrawDeposits(uint256 amount, address payable payee) internal {
  55. IRelayHub(_relayHub).withdraw(amount, payee);
  56. }
  57. // Overrides for Context's functions: when called from RelayHub, sender and
  58. // data require some pre-processing: the actual sender is stored at the end
  59. // of the call data, which in turns means it needs to be removed from it
  60. // when handling said data.
  61. /**
  62. * @dev Replacement for msg.sender. Returns the actual sender of a transaction: msg.sender for regular transactions,
  63. * and the end-user for GSN relayed calls (where msg.sender is actually `RelayHub`).
  64. *
  65. * IMPORTANT: Contracts derived from {GSNRecipient} should never use `msg.sender`, and use {_msgSender} instead.
  66. */
  67. function _msgSender() internal view returns (address) {
  68. if (msg.sender != _relayHub) {
  69. return msg.sender;
  70. } else {
  71. return _getRelayedCallSender();
  72. }
  73. }
  74. /**
  75. * @dev Replacement for msg.data. Returns the actual calldata of a transaction: msg.data for regular transactions,
  76. * and a reduced version for GSN relayed calls (where msg.data contains additional information).
  77. *
  78. * IMPORTANT: Contracts derived from {GSNRecipient} should never use `msg.data`, and use {_msgData} instead.
  79. */
  80. function _msgData() internal view returns (bytes memory) {
  81. if (msg.sender != _relayHub) {
  82. return msg.data;
  83. } else {
  84. return _getRelayedCallData();
  85. }
  86. }
  87. function _getRelayedCallSender() private pure returns (address result) {
  88. // We need to read 20 bytes (an address) located at array index msg.data.length - 20. In memory, the array
  89. // is prefixed with a 32-byte length value, so we first add 32 to get the memory read index. However, doing
  90. // so would leave the address in the upper 20 bytes of the 32-byte word, which is inconvenient and would
  91. // require bit shifting. We therefore subtract 12 from the read index so the address lands on the lower 20
  92. // bytes. This can always be done due to the 32-byte prefix.
  93. // The final memory read index is msg.data.length - 20 + 32 - 12 = msg.data.length. Using inline assembly is the
  94. // easiest/most-efficient way to perform this operation.
  95. // These fields are not accessible from assembly
  96. bytes memory array = msg.data;
  97. uint256 index = msg.data.length;
  98. // solhint-disable-next-line no-inline-assembly
  99. assembly {
  100. // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
  101. result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
  102. }
  103. return result;
  104. }
  105. function _getRelayedCallData() private pure returns (bytes memory) {
  106. // RelayHub appends the sender address at the end of the calldata, so in order to retrieve the actual msg.data,
  107. // we must strip the last 20 bytes (length of an address type) from it.
  108. uint256 actualDataLength = msg.data.length - 20;
  109. bytes memory actualData = new bytes(actualDataLength);
  110. for (uint256 i = 0; i < actualDataLength; ++i) {
  111. actualData[i] = msg.data[i];
  112. }
  113. return actualData;
  114. }
  115. }