GSNContext.sol 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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. address internal _relayHub = 0xD216153c06E857cD7f72665E0aF1d7D82172F494;
  13. event RelayHubChanged(address indexed oldRelayHub, address indexed newRelayHub);
  14. constructor() internal {
  15. // solhint-disable-previous-line no-empty-blocks
  16. }
  17. function _upgradeRelayHub(address newRelayHub) internal {
  18. address currentRelayHub = _relayHub;
  19. require(newRelayHub != address(0), "GSNContext: new RelayHub is the zero address");
  20. require(newRelayHub != currentRelayHub, "GSNContext: new RelayHub is the current one");
  21. emit RelayHubChanged(currentRelayHub, newRelayHub);
  22. _relayHub = newRelayHub;
  23. }
  24. // Overrides for Context's functions: when called from RelayHub, sender and
  25. // data require some pre-processing: the actual sender is stored at the end
  26. // of the call data, which in turns means it needs to be removed from it
  27. // when handling said data.
  28. function _msgSender() internal view returns (address) {
  29. if (msg.sender != _relayHub) {
  30. return msg.sender;
  31. } else {
  32. return _getRelayedCallSender();
  33. }
  34. }
  35. function _msgData() internal view returns (bytes memory) {
  36. if (msg.sender != _relayHub) {
  37. return msg.data;
  38. } else {
  39. return _getRelayedCallData();
  40. }
  41. }
  42. function _getRelayedCallSender() private pure returns (address result) {
  43. // We need to read 20 bytes (an address) located at array index msg.data.length - 20. In memory, the array
  44. // is prefixed with a 32-byte length value, so we first add 32 to get the memory read index. However, doing
  45. // so would leave the address in the upper 20 bytes of the 32-byte word, which is inconvenient and would
  46. // require bit shifting. We therefore subtract 12 from the read index so the address lands on the lower 20
  47. // bytes. This can always be done due to the 32-byte prefix.
  48. // The final memory read index is msg.data.length - 20 + 32 - 12 = msg.data.length. Using inline assembly is the
  49. // easiest/most-efficient way to perform this operation.
  50. // These fields are not accessible from assembly
  51. bytes memory array = msg.data;
  52. uint256 index = msg.data.length;
  53. // solhint-disable-next-line no-inline-assembly
  54. assembly {
  55. // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
  56. result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
  57. }
  58. return result;
  59. }
  60. function _getRelayedCallData() private pure returns (bytes memory) {
  61. // RelayHub appends the sender address at the end of the calldata, so in order to retrieve the actual msg.data,
  62. // we must strip the last 20 bytes (length of an address type) from it.
  63. uint256 actualDataLength = msg.data.length - 20;
  64. bytes memory actualData = new bytes(actualDataLength);
  65. for (uint256 i = 0; i < actualDataLength; ++i) {
  66. actualData[i] = msg.data[i];
  67. }
  68. return actualData;
  69. }
  70. }