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;
     __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";
 import "../GSN/bouncers/GSNBouncerERC20Fee.sol";
 
 
 contract GSNBouncerERC20FeeMock is GSNRecipient, GSNBouncerERC20Fee {
 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
         // 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?
 === 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.
 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.
 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 {
 contract MyContract is GSNRecipient, GSNBouncerSignature {
     constructor(address trustedSigner) public GSNBouncerSignature(trustedSigner) {
     constructor(address trustedSigner) public GSNBouncerSignature(trustedSigner) {
     }
     }
-}  
+}
 ----
 ----
 
 
 == GSNBouncerERC20Fee
 == GSNBouncerERC20Fee
@@ -80,7 +80,7 @@ NOTE: *Users do not need to call approve* on their tokens for your recipient con
 
 
 === How does GSNBouncerERC20Fee work?
 === 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.
 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`.
 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: 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
 === 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]):
 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]
 [source,solidity]
 ----
 ----
 contract MyContract is GSNRecipient, GSNBouncerERC20Fee, MinterRole {
 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 {
     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 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.
 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.
 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`.
 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.
 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 gsn = require('@openzeppelin/gsn-helpers');
 
 
 const { expect } = require('chai');
 const { expect } = require('chai');
@@ -10,10 +10,9 @@ const IRelayHub = artifacts.require('IRelayHub');
 contract('GSNBouncerERC20Fee', function ([_, sender, other]) {
 contract('GSNBouncerERC20Fee', function ([_, sender, other]) {
   const name = 'FeeToken';
   const name = 'FeeToken';
   const symbol = 'FTKN';
   const symbol = 'FTKN';
-  const decimals = new BN('18');
 
 
   beforeEach(async function () {
   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());
     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);
       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');
     });
     });
   });
   });