diff --git a/contracts/mocks/ERC20DecimalsMock.sol b/contracts/mocks/ERC20DecimalsMock.sol index 924c3af31..1374cbc52 100644 --- a/contracts/mocks/ERC20DecimalsMock.sol +++ b/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); + } } diff --git a/contracts/token/ERC20/extensions/ERC20Wrapper.sol b/contracts/token/ERC20/extensions/ERC20Wrapper.sol index 32a3b830a..c6dd0184a 100644 --- a/contracts/token/ERC20/extensions/ERC20Wrapper.sol +++ b/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. */ diff --git a/test/token/ERC20/extensions/ERC20Wrapper.test.js b/test/token/ERC20/extensions/ERC20Wrapper.test.js index ec074e1b8..ceb813e08 100644 --- a/test/token/ERC20/extensions/ERC20Wrapper.test.js +++ b/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 () {