Bladeren bron

Inherit ERC20Wrapper decimals from the underlying (#3259)

Hadrien Croubois 3 jaren geleden
bovenliggende
commit
98716177ae

+ 8 - 0
contracts/mocks/ERC20DecimalsMock.sol

@@ -18,4 +18,12 @@ contract ERC20DecimalsMock is ERC20 {
     function decimals() public view virtual override returns (uint8) {
         return _decimals;
     }
+
+    function mint(address account, uint256 amount) public {
+        _mint(account, amount);
+    }
+
+    function burn(address account, uint256 amount) public {
+        _burn(account, amount);
+    }
 }

+ 11 - 0
contracts/token/ERC20/extensions/ERC20Wrapper.sol

@@ -22,6 +22,17 @@ abstract contract ERC20Wrapper is ERC20 {
         underlying = underlyingToken;
     }
 
+    /**
+     * @dev See {ERC20-decimals}.
+     */
+    function decimals() public view virtual override returns (uint8) {
+        try IERC20Metadata(address(underlying)).decimals() returns (uint8 value) {
+            return value;
+        } catch {
+            return super.decimals();
+        }
+    }
+
     /**
      * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens.
      */

+ 13 - 4
test/token/ERC20/extensions/ERC20Wrapper.test.js

@@ -4,7 +4,8 @@ const { ZERO_ADDRESS, MAX_UINT256 } = constants;
 
 const { shouldBehaveLikeERC20 } = require('../ERC20.behavior');
 
-const ERC20Mock = artifacts.require('ERC20Mock');
+const NotAnERC20 = artifacts.require('CallReceiverMock');
+const ERC20Mock = artifacts.require('ERC20DecimalsMock');
 const ERC20WrapperMock = artifacts.require('ERC20WrapperMock');
 
 contract('ERC20', function (accounts) {
@@ -16,8 +17,10 @@ contract('ERC20', function (accounts) {
   const initialSupply = new BN(100);
 
   beforeEach(async function () {
-    this.underlying = await ERC20Mock.new(name, symbol, initialHolder, initialSupply);
+    this.underlying = await ERC20Mock.new(name, symbol, 9);
     this.token = await ERC20WrapperMock.new(this.underlying.address, `Wrapped ${name}`, `W${symbol}`);
+
+    await this.underlying.mint(initialHolder, initialSupply);
   });
 
   afterEach(async function () {
@@ -32,8 +35,14 @@ contract('ERC20', function (accounts) {
     expect(await this.token.symbol()).to.equal(`W${symbol}`);
   });
 
-  it('has 18 decimals', async function () {
-    expect(await this.token.decimals()).to.be.bignumber.equal('18');
+  it('has the same decimals as the underlying token', async function () {
+    expect(await this.token.decimals()).to.be.bignumber.equal('9');
+  });
+
+  it('decimals default back to 18 if token has no metadata', async function () {
+    const noDecimals = await NotAnERC20.new();
+    const otherToken = await ERC20WrapperMock.new(noDecimals.address, `Wrapped ${name}`, `W${symbol}`);
+    expect(await otherToken.decimals()).to.be.bignumber.equal('18');
   });
 
   it('has underlying', async function () {