GSNContext.sol 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. pragma solidity ^0.5.0;
  2. import "./Context.sol";
  3. /*
  4. * @dev Enables GSN support on `Context` contracts by recognizing calls from
  5. * RelayHub and extracting the actual sender and call data from the received
  6. * calldata.
  7. *
  8. * > This contract does not perform all required tasks to implement a GSN
  9. * recipient contract: end users should use `GSNRecipient` instead.
  10. */
  11. contract GSNContext is Context {
  12. // We use a random storage slot to allow proxy contracts to enable GSN support in an upgrade without changing their
  13. // storage layout. This value is calculated as: keccak256('gsn.relayhub.address'), minus 1.
  14. bytes32 private constant RELAY_HUB_ADDRESS_STORAGE_SLOT = 0x06b7792c761dcc05af1761f0315ce8b01ac39c16cc934eb0b2f7a8e71414f262;
  15. event RelayHubChanged(address indexed oldRelayHub, address indexed newRelayHub);
  16. constructor() internal {
  17. _upgradeRelayHub(0xD216153c06E857cD7f72665E0aF1d7D82172F494);
  18. }
  19. function _getRelayHub() internal view returns (address relayHub) {
  20. bytes32 slot = RELAY_HUB_ADDRESS_STORAGE_SLOT;
  21. // solhint-disable-next-line no-inline-assembly
  22. assembly {
  23. relayHub := sload(slot)
  24. }
  25. }
  26. function _upgradeRelayHub(address newRelayHub) internal {
  27. address currentRelayHub = _getRelayHub();
  28. require(newRelayHub != address(0), "GSNContext: new RelayHub is the zero address");
  29. require(newRelayHub != currentRelayHub, "GSNContext: new RelayHub is the current one");
  30. emit RelayHubChanged(currentRelayHub, newRelayHub);
  31. bytes32 slot = RELAY_HUB_ADDRESS_STORAGE_SLOT;
  32. // solhint-disable-next-line no-inline-assembly
  33. assembly {
  34. sstore(slot, newRelayHub)
  35. }
  36. }
  37. // Overrides for Context's functions: when called from RelayHub, sender and
  38. // data require some pre-processing: the actual sender is stored at the end
  39. // of the call data, which in turns means it needs to be removed from it
  40. // when handling said data.
  41. function _msgSender() internal view returns (address) {
  42. if (msg.sender != _getRelayHub()) {
  43. return msg.sender;
  44. } else {
  45. return _getRelayedCallSender();
  46. }
  47. }
  48. function _msgData() internal view returns (bytes memory) {
  49. if (msg.sender != _getRelayHub()) {
  50. return msg.data;
  51. } else {
  52. return _getRelayedCallData();
  53. }
  54. }
  55. function _getRelayedCallSender() private pure returns (address result) {
  56. // We need to read 20 bytes (an address) located at array index msg.data.length - 20. In memory, the array
  57. // is prefixed with a 32-byte length value, so we first add 32 to get the memory read index. However, doing
  58. // so would leave the address in the upper 20 bytes of the 32-byte word, which is inconvenient and would
  59. // require bit shifting. We therefore subtract 12 from the read index so the address lands on the lower 20
  60. // bytes. This can always be done due to the 32-byte prefix.
  61. // The final memory read index is msg.data.length - 20 + 32 - 12 = msg.data.length. Using inline assembly is the
  62. // easiest/most-efficient way to perform this operation.
  63. // These fields are not accessible from assembly
  64. bytes memory array = msg.data;
  65. uint256 index = msg.data.length;
  66. // solhint-disable-next-line no-inline-assembly
  67. assembly {
  68. // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
  69. result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
  70. }
  71. return result;
  72. }
  73. function _getRelayedCallData() private pure returns (bytes memory) {
  74. // RelayHub appends the sender address at the end of the calldata, so in order to retrieve the actual msg.data,
  75. // we must strip the last 20 bytes (length of an address type) from it.
  76. uint256 actualDataLength = msg.data.length - 20;
  77. bytes memory actualData = new bytes(actualDataLength);
  78. for (uint256 i = 0; i < actualDataLength; ++i) {
  79. actualData[i] = msg.data[i];
  80. }
  81. return actualData;
  82. }
  83. }