Browse Source

Fix the GSNBouncerERC20Fee token decimals to 18 (#1929)

* fix erc20 fee token decimals to 18

* lint

* Update contracts/GSN/bouncers/GSNBouncerERC20Fee.sol

Co-Authored-By: Nicolás Venturo <nicolas.venturo@gmail.com>

* change location of hardcoded decimals for clarity

* remove mention of decimals from docs

* remove trailing whitespace from guide

(cherry picked from commit b8ccf8e0f185685fb9da427888565ec41c2875ae)
Francisco Giordano 6 years ago
parent
commit
4f4d305784

+ 3 - 4
contracts/GSN/bouncers/GSNBouncerERC20Fee.sol

@@ -27,11 +27,10 @@ contract GSNBouncerERC20Fee is GSNBouncerBase {
     __unstable__ERC20PrimaryAdmin private _token;
 
     /**
-     * @dev The arguments to the constructor are the details that the gas payment token will have: `name`, `symbol`, and
-     * `decimals`.
+     * @dev The arguments to the constructor are the details that the gas payment token will have: `name` and `symbol`. `decimals` is hard-coded to 18.
      */
-    constructor(string memory name, string memory symbol, uint8 decimals) public {
-        _token = new __unstable__ERC20PrimaryAdmin(name, symbol, decimals);
+    constructor(string memory name, string memory symbol) public {
+        _token = new __unstable__ERC20PrimaryAdmin(name, symbol, 18);
     }
 
     /**

+ 1 - 1
contracts/mocks/GSNBouncerERC20FeeMock.sol

@@ -4,7 +4,7 @@ import "../GSN/GSNRecipient.sol";
 import "../GSN/bouncers/GSNBouncerERC20Fee.sol";
 
 contract GSNBouncerERC20FeeMock is GSNRecipient, GSNBouncerERC20Fee {
-    constructor(string memory name, string memory symbol, uint8 decimals) public GSNBouncerERC20Fee(name, symbol, decimals) {
+    constructor(string memory name, string memory symbol) public GSNBouncerERC20Fee(name, symbol) {
         // solhint-disable-previous-line no-empty-blocks
     }
 

+ 9 - 11
docs/modules/ROOT/pages/gsn-bouncers.adoc

@@ -45,7 +45,7 @@ On the other hand, you need to have a backend server, microservice, or lambda fu
 
 === How does GSNBouncerSignature work?
 
-`GSNBouncerSignature` decides whether or not to accept the relayed call based on the included signature. 
+`GSNBouncerSignature` decides whether or not to accept the relayed call based on the included signature.
 
 The `acceptRelayedCall` implementation recovers the address from the signature of the relayed call parameters in `approvalData` and compares to the trusted signer.
 If the included signature matches the trusted signer, the relayed call is approved.
@@ -62,7 +62,7 @@ Your GSN recipient contract needs to inherit from `GSNRecipient` and `GSNBouncer
 contract MyContract is GSNRecipient, GSNBouncerSignature {
     constructor(address trustedSigner) public GSNBouncerSignature(trustedSigner) {
     }
-}  
+}
 ----
 
 == GSNBouncerERC20Fee
@@ -80,7 +80,7 @@ NOTE: *Users do not need to call approve* on their tokens for your recipient con
 
 === How does GSNBouncerERC20Fee work?
 
-`GSNBouncerERC20Fee` decides to approve or reject relayed calls based on the balance of the users tokens.  
+`GSNBouncerERC20Fee` decides to approve or reject relayed calls based on the balance of the users tokens.
 
 The `acceptRelayedCall` function implementation checks the users token balance.
 If the user doesn't have enough tokens the relayed call gets rejected with an error of `INSUFFICIENT_BALANCE`.
@@ -94,18 +94,16 @@ The maximum amount of tokens required is transferred in `_preRelayedCall` to pro
 
 NOTE: The gas cost estimation is not 100% accurate, we may tweak it further down the road.
 
-NOTE: Always use `_preRelayedCall` and `_postRelayedCall` functions.  Internal `_preRelayedCall` and `_postRelayedCall` functions are used instead of public `preRelayedCall` and `postRelayedCall` functions, as the public functions are prevented from being called by non-RelayHub contracts. 
+NOTE: Always use `_preRelayedCall` and `_postRelayedCall` functions.  Internal `_preRelayedCall` and `_postRelayedCall` functions are used instead of public `preRelayedCall` and `postRelayedCall` functions, as the public functions are prevented from being called by non-RelayHub contracts.
 
 === How to use GSNBouncerERC20Fee
 
 Your GSN recipient contract needs to inherit from `GSNRecipient` and `GSNBouncerERC20Fee` along with appropriate xref:access-control.adoc[access control] (for token minting), set the token details in the constructor of `GSNBouncerERC20Fee` and create a public `mint` function suitably protected by your chosen access control as per the following sample code (which uses the xref:api:access.adoc#MinterRole[MinterRole]):
 
-NOTE: The token must have decimals of 18 to match that of ether, due to the baked-in exchange rate of 1:1.
-
 [source,solidity]
 ----
 contract MyContract is GSNRecipient, GSNBouncerERC20Fee, MinterRole {
-    constructor() public GSNBouncerERC20Fee("FeeToken", "FEE", 18) {
+    constructor() public GSNBouncerERC20Fee("FeeToken", "FEE") {
     }
 
     function mint(address account, uint256 amount) public onlyMinter {
@@ -120,13 +118,13 @@ You can create your own Custom Bouncer!  For example, your Custom Bouncer could
 
 Your Custom Bouncer can inherit from `GSNBouncerBase` and must implement the `acceptRelayedCall` function.
 
-Your `acceptRelayedCall` implementation decides whether or not to accept the relayed call.  
-If your logic accepts the relayed call then you should return `_approveRelayedCall`.  
+Your `acceptRelayedCall` implementation decides whether or not to accept the relayed call.
+If your logic accepts the relayed call then you should return `_approveRelayedCall`.
 If your logic rejects the relayed call then you should return `_rejectRelayedCall` with an error code.
 See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/GSN/bouncers/GSNBouncerSignature.sol[GSNBouncerSignature.sol] as an example implementation.
 
-For Custom Bouncers charging end users, `_postRelayedCall` and `_preRelayedCall` should be implemented to handle the charging.  
-Your `_preRelayedCall` implementation should take the maximum possible charge, with your `_postRelayedCall` implementation refunding any difference from the actual charge once the relayed call has been made.  
+For Custom Bouncers charging end users, `_postRelayedCall` and `_preRelayedCall` should be implemented to handle the charging.
+Your `_preRelayedCall` implementation should take the maximum possible charge, with your `_postRelayedCall` implementation refunding any difference from the actual charge once the relayed call has been made.
 When returning `_approveRelayedCall` to approve the relayed call, the end users address, `maxPossibleCharge`, `transactionFee` and `gasPrice` data can also be returned so that the data can be used in `_preRelayedCall` and `_postRelayedCall`.
 See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.4.0/contracts/GSN/bouncers/GSNBouncerERC20Fee.sol[GSNBouncerERC20Fee.sol] as an example implementation.
 

+ 4 - 5
test/GSN/GSNBouncerERC20Fee.test.js

@@ -1,4 +1,4 @@
-const { BN, ether, expectEvent } = require('openzeppelin-test-helpers');
+const { ether, expectEvent } = require('openzeppelin-test-helpers');
 const gsn = require('@openzeppelin/gsn-helpers');
 
 const { expect } = require('chai');
@@ -10,10 +10,9 @@ const IRelayHub = artifacts.require('IRelayHub');
 contract('GSNBouncerERC20Fee', function ([_, sender, other]) {
   const name = 'FeeToken';
   const symbol = 'FTKN';
-  const decimals = new BN('18');
 
   beforeEach(async function () {
-    this.recipient = await GSNBouncerERC20FeeMock.new(name, symbol, decimals);
+    this.recipient = await GSNBouncerERC20FeeMock.new(name, symbol);
     this.token = await ERC20Detailed.at(await this.recipient.token());
   });
 
@@ -26,8 +25,8 @@ contract('GSNBouncerERC20Fee', function ([_, sender, other]) {
       expect(await this.token.symbol()).to.equal(symbol);
     });
 
-    it('has decimals', async function () {
-      expect(await this.token.decimals()).to.be.bignumber.equal(decimals);
+    it('has 18 decimals', async function () {
+      expect(await this.token.decimals()).to.be.bignumber.equal('18');
     });
   });