GSNBouncerERC20Fee.sol 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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. contract GSNBouncerERC20Fee is GSNBouncerBase {
  9. using SafeERC20 for __unstable__ERC20PrimaryAdmin;
  10. using SafeMath for uint256;
  11. enum GSNBouncerERC20FeeErrorCodes {
  12. INSUFFICIENT_BALANCE
  13. }
  14. __unstable__ERC20PrimaryAdmin private _token;
  15. constructor(string memory name, string memory symbol, uint8 decimals) public {
  16. _token = new __unstable__ERC20PrimaryAdmin(name, symbol, decimals);
  17. }
  18. function token() public view returns (IERC20) {
  19. return IERC20(_token);
  20. }
  21. function _mint(address account, uint256 amount) internal {
  22. _token.mint(account, amount);
  23. }
  24. function acceptRelayedCall(
  25. address,
  26. address from,
  27. bytes calldata,
  28. uint256 transactionFee,
  29. uint256 gasPrice,
  30. uint256,
  31. uint256,
  32. bytes calldata,
  33. uint256 maxPossibleCharge
  34. )
  35. external
  36. view
  37. returns (uint256, bytes memory)
  38. {
  39. if (_token.balanceOf(from) < maxPossibleCharge) {
  40. return _rejectRelayedCall(uint256(GSNBouncerERC20FeeErrorCodes.INSUFFICIENT_BALANCE));
  41. }
  42. return _approveRelayedCall(abi.encode(from, maxPossibleCharge, transactionFee, gasPrice));
  43. }
  44. function _preRelayedCall(bytes memory context) internal returns (bytes32) {
  45. (address from, uint256 maxPossibleCharge) = abi.decode(context, (address, uint256));
  46. // The maximum token charge is pre-charged from the user
  47. _token.safeTransferFrom(from, address(this), maxPossibleCharge);
  48. }
  49. function _postRelayedCall(bytes memory context, bool, uint256 actualCharge, bytes32) internal {
  50. (address from, uint256 maxPossibleCharge, uint256 transactionFee, uint256 gasPrice) =
  51. abi.decode(context, (address, uint256, uint256, uint256));
  52. // actualCharge is an _estimated_ charge, which assumes postRelayedCall will use all available gas.
  53. // This implementation's gas cost can be roughly estimated as 10k gas, for the two SSTORE operations in an
  54. // ERC20 transfer.
  55. uint256 overestimation = _computeCharge(POST_RELAYED_CALL_MAX_GAS.sub(10000), gasPrice, transactionFee);
  56. actualCharge = actualCharge.sub(overestimation);
  57. // After the relayed call has been executed and the actual charge estimated, the excess pre-charge is returned
  58. _token.safeTransfer(from, maxPossibleCharge.sub(actualCharge));
  59. }
  60. }
  61. /**
  62. * @title __unstable__ERC20PrimaryAdmin
  63. * @dev An ERC20 token owned by another contract, which has minting permissions and can use transferFrom to receive
  64. * anyone's tokens. This contract is an internal helper for GSNRecipientERC20Fee, and should not be used
  65. * outside of this context.
  66. */
  67. // solhint-disable-next-line contract-name-camelcase
  68. contract __unstable__ERC20PrimaryAdmin is ERC20, ERC20Detailed, Secondary {
  69. uint256 private constant UINT256_MAX = 2**256 - 1;
  70. constructor(string memory name, string memory symbol, uint8 decimals) public ERC20Detailed(name, symbol, decimals) {
  71. // solhint-disable-previous-line no-empty-blocks
  72. }
  73. // The primary account (GSNRecipientERC20Fee) can mint tokens
  74. function mint(address account, uint256 amount) public onlyPrimary {
  75. _mint(account, amount);
  76. }
  77. // The primary account has 'infinite' allowance for all token holders
  78. function allowance(address owner, address spender) public view returns (uint256) {
  79. if (spender == primary()) {
  80. return UINT256_MAX;
  81. } else {
  82. return super.allowance(owner, spender);
  83. }
  84. }
  85. // Allowance for the primary account cannot be changed (it is always 'infinite')
  86. function _approve(address owner, address spender, uint256 value) internal {
  87. if (spender == primary()) {
  88. return;
  89. } else {
  90. super._approve(owner, spender, value);
  91. }
  92. }
  93. function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
  94. if (recipient == primary()) {
  95. _transfer(sender, recipient, amount);
  96. return true;
  97. } else {
  98. return super.transferFrom(sender, recipient, amount);
  99. }
  100. }
  101. }