From 73c6598989fc36766478cf663c240ff75886b1a1 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 10 Mar 2022 20:05:11 +0100 Subject: [PATCH 1/3] improve wrapper decimal support --- contracts/mocks/ERC20DecimalsMock.sol | 8 ++++++++ .../token/ERC20/extensions/ERC20Wrapper.sol | 11 +++++++++++ .../token/ERC20/extensions/ERC20Wrapper.test.js | 17 +++++++++++++---- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/contracts/mocks/ERC20DecimalsMock.sol b/contracts/mocks/ERC20DecimalsMock.sol index 924c3af31f3..1374cbc52a0 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 32a3b830afe..c6dd0184a6d 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 ec074e1b8cf..7b17bcfeee5 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 () { + this.nodecimals = await NotAnERC20.new(); + this.othertoken = await ERC20WrapperMock.new(this.nodecimals.address, `Wrapped ${name}`, `W${symbol}`); + expect(await this.othertoken.decimals()).to.be.bignumber.equal('18'); }); it('has underlying', async function () { From f58f39cca58688f6e35fa66055f47ca5cc80e5e9 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 10 Mar 2022 21:08:39 +0100 Subject: [PATCH 2/3] Update test/token/ERC20/extensions/ERC20Wrapper.test.js Co-authored-by: Francisco Giordano --- test/token/ERC20/extensions/ERC20Wrapper.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/token/ERC20/extensions/ERC20Wrapper.test.js b/test/token/ERC20/extensions/ERC20Wrapper.test.js index 7b17bcfeee5..ceb813e08d8 100644 --- a/test/token/ERC20/extensions/ERC20Wrapper.test.js +++ b/test/token/ERC20/extensions/ERC20Wrapper.test.js @@ -40,9 +40,9 @@ contract('ERC20', function (accounts) { }); it('decimals default back to 18 if token has no metadata', async function () { - this.nodecimals = await NotAnERC20.new(); - this.othertoken = await ERC20WrapperMock.new(this.nodecimals.address, `Wrapped ${name}`, `W${symbol}`); - expect(await this.othertoken.decimals()).to.be.bignumber.equal('18'); + 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 () { From f986f9a6f73463da87ba5ed46a0e5da4b6864fcc Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 22 Mar 2022 15:59:05 +0100 Subject: [PATCH 3/3] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a64abe50308..2c5d150f605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * `Governor`: Add a way to parameterize votes. This can be used to implement voting systems such as fractionalized voting, ERC721 based voting, or any number of other systems. The `params` argument added to `_countVote` method, and included in the newly added `_getVotes` method, can be used by counting and voting modules respectively for such purposes. * `TimelockController`: Add a separate canceller role for the ability to cancel. ([#3165](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3165)) * `draft-ERC20Permit`: replace `immutable` with `constant` for `_PERMIT_TYPEHASH` since the `keccak256` of string literals is treated specially and the hash is evaluated at compile time. ([#3196](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3196)) + * `ERC20Wrapper`: the `decimals()` function now tries to fetch the value from the underlying token instance. If that calls revert, then the default value is used. ([#3259](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3259)) ### Breaking changes