GSNBouncerERC20Fee.sol 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. pragma solidity ^0.5.0;
  2. import "./GSNBouncerBase.sol";
  3. import "../../math/SafeMath.sol";
  4. import "../../ownership/Secondary.sol";
  5. import "../../token/ERC20/SafeERC20.sol";
  6. import "../../token/ERC20/ERC20.sol";
  7. import "../../token/ERC20/ERC20Detailed.sol";
  8. /**
  9. * @dev A xref:ROOT:gsn-bouncers.adoc#gsn-bouncers[GSN Bouncer] that charges transaction fees in a special purpose ERC20
  10. * token, which we refer to as the gas payment token. The amount charged is exactly the amount of Ether charged to the
  11. * recipient. This means that the token is essentially pegged to the value of Ether.
  12. *
  13. * The distribution strategy of the gas payment token to users is not defined by this contract. It's a mintable token
  14. * whose only minter is the recipient, so the strategy must be implemented in a derived contract, making use of the
  15. * internal {_mint} function.
  16. */
  17. contract GSNBouncerERC20Fee is GSNBouncerBase {
  18. using SafeERC20 for __unstable__ERC20PrimaryAdmin;
  19. using SafeMath for uint256;
  20. enum GSNBouncerERC20FeeErrorCodes {
  21. INSUFFICIENT_BALANCE
  22. }
  23. __unstable__ERC20PrimaryAdmin private _token;
  24. /**
  25. * @dev The arguments to the constructor are the details that the gas payment token will have: `name`, `symbol`, and
  26. * `decimals`.
  27. */
  28. constructor(string memory name, string memory symbol, uint8 decimals) public {
  29. _token = new __unstable__ERC20PrimaryAdmin(name, symbol, decimals);
  30. }
  31. /**
  32. * @dev Returns the gas payment token.
  33. */
  34. function token() public view returns (IERC20) {
  35. return IERC20(_token);
  36. }
  37. /**
  38. * @dev Internal function that mints the gas payment token. Derived contracts should expose this function in their public API, with proper access control mechanisms.
  39. */
  40. function _mint(address account, uint256 amount) internal {
  41. _token.mint(account, amount);
  42. }
  43. /**
  44. * @dev Ensures that only users with enough gas payment token balance can have transactions relayed through the GSN.
  45. */
  46. function acceptRelayedCall(
  47. address,
  48. address from,
  49. bytes calldata,
  50. uint256 transactionFee,
  51. uint256 gasPrice,
  52. uint256,
  53. uint256,
  54. bytes calldata,
  55. uint256 maxPossibleCharge
  56. )
  57. external
  58. view
  59. returns (uint256, bytes memory)
  60. {
  61. if (_token.balanceOf(from) < maxPossibleCharge) {
  62. return _rejectRelayedCall(uint256(GSNBouncerERC20FeeErrorCodes.INSUFFICIENT_BALANCE));
  63. }
  64. return _approveRelayedCall(abi.encode(from, maxPossibleCharge, transactionFee, gasPrice));
  65. }
  66. /**
  67. * @dev Implements the precharge to the user. The maximum possible charge (depending on gas limit, gas price, and
  68. * fee) will be deducted from the user balance of gas payment token. Note that this is an overestimation of the
  69. * actual charge, necessary because we cannot predict how much gas the execution will actually need. The remainder
  70. * is returned to the user in {_postRelayedCall}.
  71. */
  72. function _preRelayedCall(bytes memory context) internal returns (bytes32) {
  73. (address from, uint256 maxPossibleCharge) = abi.decode(context, (address, uint256));
  74. // The maximum token charge is pre-charged from the user
  75. _token.safeTransferFrom(from, address(this), maxPossibleCharge);
  76. }
  77. /**
  78. * @dev Returns to the user the extra amount that was previously charged, once the actual execution cost is known.
  79. */
  80. function _postRelayedCall(bytes memory context, bool, uint256 actualCharge, bytes32) internal {
  81. (address from, uint256 maxPossibleCharge, uint256 transactionFee, uint256 gasPrice) =
  82. abi.decode(context, (address, uint256, uint256, uint256));
  83. // actualCharge is an _estimated_ charge, which assumes postRelayedCall will use all available gas.
  84. // This implementation's gas cost can be roughly estimated as 10k gas, for the two SSTORE operations in an
  85. // ERC20 transfer.
  86. uint256 overestimation = _computeCharge(POST_RELAYED_CALL_MAX_GAS.sub(10000), gasPrice, transactionFee);
  87. actualCharge = actualCharge.sub(overestimation);
  88. // After the relayed call has been executed and the actual charge estimated, the excess pre-charge is returned
  89. _token.safeTransfer(from, maxPossibleCharge.sub(actualCharge));
  90. }
  91. }
  92. /**
  93. * @title __unstable__ERC20PrimaryAdmin
  94. * @dev An ERC20 token owned by another contract, which has minting permissions and can use transferFrom to receive
  95. * anyone's tokens. This contract is an internal helper for GSNRecipientERC20Fee, and should not be used
  96. * outside of this context.
  97. */
  98. // solhint-disable-next-line contract-name-camelcase
  99. contract __unstable__ERC20PrimaryAdmin is ERC20, ERC20Detailed, Secondary {
  100. uint256 private constant UINT256_MAX = 2**256 - 1;
  101. constructor(string memory name, string memory symbol, uint8 decimals) public ERC20Detailed(name, symbol, decimals) {
  102. // solhint-disable-previous-line no-empty-blocks
  103. }
  104. // The primary account (GSNRecipientERC20Fee) can mint tokens
  105. function mint(address account, uint256 amount) public onlyPrimary {
  106. _mint(account, amount);
  107. }
  108. // The primary account has 'infinite' allowance for all token holders
  109. function allowance(address owner, address spender) public view returns (uint256) {
  110. if (spender == primary()) {
  111. return UINT256_MAX;
  112. } else {
  113. return super.allowance(owner, spender);
  114. }
  115. }
  116. // Allowance for the primary account cannot be changed (it is always 'infinite')
  117. function _approve(address owner, address spender, uint256 value) internal {
  118. if (spender == primary()) {
  119. return;
  120. } else {
  121. super._approve(owner, spender, value);
  122. }
  123. }
  124. function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
  125. if (recipient == primary()) {
  126. _transfer(sender, recipient, amount);
  127. return true;
  128. } else {
  129. return super.transferFrom(sender, recipient, amount);
  130. }
  131. }
  132. }