diff --git a/.changeset/heavy-drinks-fail.md b/.changeset/heavy-drinks-fail.md new file mode 100644 index 00000000000..bbe93ca90da --- /dev/null +++ b/.changeset/heavy-drinks-fail.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': major +--- + +`ERC20`: Remove `Approval` event previously emitted in `transferFrom` to indicate that part of the allowance was consumed. With this change, allowances are no longer reconstructible from events. See the code for guidelines on how to re-enable this event if needed. diff --git a/contracts/mocks/token/ERC20ApprovalMock.sol b/contracts/mocks/token/ERC20ApprovalMock.sol new file mode 100644 index 00000000000..da8f51e1ecc --- /dev/null +++ b/contracts/mocks/token/ERC20ApprovalMock.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "../../token/ERC20/ERC20.sol"; + +abstract contract ERC20ApprovalMock is ERC20 { + function _approve(address owner, address spender, uint256 amount, bool) internal virtual override { + super._approve(owner, spender, amount, true); + } +} diff --git a/contracts/token/ERC20/ERC20.sol b/contracts/token/ERC20/ERC20.sol index aacfe79b752..5ddebf22b25 100644 --- a/contracts/token/ERC20/ERC20.sol +++ b/contracts/token/ERC20/ERC20.sol @@ -311,6 +311,27 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { + _approve(owner, spender, amount, true); + } + + /** + * @dev Alternative version of {_approve} with an optional flag that can enable or disable the Approval event. + * + * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by + * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any + * `Approval` event during `transferFrom` operations. + * + * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to true + * using the following override: + * ``` + * function _approve(address owner, address spender, uint256 amount, bool) internal virtual override { + * super._approve(owner, spender, amount, true); + * } + * ``` + * + * Requirements are the same as {_approve}. + */ + function _approve(address owner, address spender, uint256 amount, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } @@ -318,7 +339,9 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); + if (emitEvent) { + emit Approval(owner, spender, amount); + } } /** @@ -336,7 +359,7 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { revert ERC20InsufficientAllowance(spender, currentAllowance, amount); } unchecked { - _approve(owner, spender, currentAllowance - amount); + _approve(owner, spender, currentAllowance - amount, false); } } } diff --git a/test/token/ERC20/ERC20.behavior.js b/test/token/ERC20/ERC20.behavior.js index 7f547b112b1..bb2efda893c 100644 --- a/test/token/ERC20/ERC20.behavior.js +++ b/test/token/ERC20/ERC20.behavior.js @@ -4,7 +4,10 @@ const { ZERO_ADDRESS, MAX_UINT256 } = constants; const { expectRevertCustomError } = require('../../helpers/customError'); -function shouldBehaveLikeERC20(initialSupply, initialHolder, recipient, anotherAccount) { +function shouldBehaveLikeERC20(initialSupply, accounts, opts = {}) { + const [initialHolder, recipient, anotherAccount] = accounts; + const { forcedApproval } = opts; + describe('total supply', function () { it('returns the total amount of tokens', async function () { expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply); @@ -70,13 +73,22 @@ function shouldBehaveLikeERC20(initialSupply, initialHolder, recipient, anotherA }); }); - it('emits an approval event', async function () { - expectEvent(await this.token.transferFrom(tokenOwner, to, amount, { from: spender }), 'Approval', { - owner: tokenOwner, - spender: spender, - value: await this.token.allowance(tokenOwner, spender), + if (forcedApproval) { + it('emits an approval event', async function () { + expectEvent(await this.token.transferFrom(tokenOwner, to, amount, { from: spender }), 'Approval', { + owner: tokenOwner, + spender: spender, + value: await this.token.allowance(tokenOwner, spender), + }); }); - }); + } else { + it('does not emit an approval event', async function () { + expectEvent.notEmitted( + await this.token.transferFrom(tokenOwner, to, amount, { from: spender }), + 'Approval', + ); + }); + } }); describe('when the token owner does not have enough balance', function () { diff --git a/test/token/ERC20/ERC20.test.js b/test/token/ERC20/ERC20.test.js index 7b97c56f1c3..ef6d82f2bd6 100644 --- a/test/token/ERC20/ERC20.test.js +++ b/test/token/ERC20/ERC20.test.js @@ -9,357 +9,357 @@ const { } = require('./ERC20.behavior'); const { expectRevertCustomError } = require('../../helpers/customError'); -const ERC20 = artifacts.require('$ERC20'); -const ERC20Decimals = artifacts.require('$ERC20DecimalsMock'); +const TOKENS = [ + { Token: artifacts.require('$ERC20') }, + { Token: artifacts.require('$ERC20ApprovalMock'), forcedApproval: true }, +]; contract('ERC20', function (accounts) { - const [initialHolder, recipient, anotherAccount] = accounts; + const [initialHolder, recipient] = accounts; const name = 'My Token'; const symbol = 'MTKN'; - const initialSupply = new BN(100); - beforeEach(async function () { - this.token = await ERC20.new(name, symbol); - await this.token.$_mint(initialHolder, initialSupply); - }); - - it('has a name', async function () { - expect(await this.token.name()).to.equal(name); - }); - - it('has a symbol', async function () { - expect(await this.token.symbol()).to.equal(symbol); - }); - - it('has 18 decimals', async function () { - expect(await this.token.decimals()).to.be.bignumber.equal('18'); - }); - - describe('set decimals', function () { - const decimals = new BN(6); - - it('can set decimals during construction', async function () { - const token = await ERC20Decimals.new(name, symbol, decimals); - expect(await token.decimals()).to.be.bignumber.equal(decimals); - }); - }); - - shouldBehaveLikeERC20(initialSupply, initialHolder, recipient, anotherAccount); + for (const { Token, forcedApproval } of TOKENS) { + describe(`using ${Token._json.contractName}`, function () { + beforeEach(async function () { + this.token = await Token.new(name, symbol); + await this.token.$_mint(initialHolder, initialSupply); + }); - describe('decrease allowance', function () { - describe('when the spender is not the zero address', function () { - const spender = recipient; + shouldBehaveLikeERC20(initialSupply, accounts, { forcedApproval }); - function shouldDecreaseApproval(amount) { - describe('when there was no approved amount before', function () { - it('reverts', async function () { - const allowance = await this.token.allowance(initialHolder, spender); - await expectRevertCustomError( - this.token.decreaseAllowance(spender, amount, { from: initialHolder }), - 'ERC20FailedDecreaseAllowance', - [spender, allowance, amount], - ); - }); - }); + it('has a name', async function () { + expect(await this.token.name()).to.equal(name); + }); - describe('when the spender had an approved amount', function () { - const approvedAmount = amount; + it('has a symbol', async function () { + expect(await this.token.symbol()).to.equal(symbol); + }); - beforeEach(async function () { - await this.token.approve(spender, approvedAmount, { from: initialHolder }); - }); + it('has 18 decimals', async function () { + expect(await this.token.decimals()).to.be.bignumber.equal('18'); + }); - it('emits an approval event', async function () { - expectEvent( - await this.token.decreaseAllowance(spender, approvedAmount, { from: initialHolder }), - 'Approval', - { owner: initialHolder, spender: spender, value: new BN(0) }, - ); + describe('decrease allowance', function () { + describe('when the spender is not the zero address', function () { + const spender = recipient; + + function shouldDecreaseApproval(amount) { + describe('when there was no approved amount before', function () { + it('reverts', async function () { + const allowance = await this.token.allowance(initialHolder, spender); + await expectRevertCustomError( + this.token.decreaseAllowance(spender, amount, { from: initialHolder }), + 'ERC20FailedDecreaseAllowance', + [spender, allowance, amount], + ); + }); + }); + + describe('when the spender had an approved amount', function () { + const approvedAmount = amount; + + beforeEach(async function () { + await this.token.approve(spender, approvedAmount, { from: initialHolder }); + }); + + it('emits an approval event', async function () { + expectEvent( + await this.token.decreaseAllowance(spender, approvedAmount, { from: initialHolder }), + 'Approval', + { owner: initialHolder, spender: spender, value: new BN(0) }, + ); + }); + + it('decreases the spender allowance subtracting the requested amount', async function () { + await this.token.decreaseAllowance(spender, approvedAmount.subn(1), { from: initialHolder }); + + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal('1'); + }); + + it('sets the allowance to zero when all allowance is removed', async function () { + await this.token.decreaseAllowance(spender, approvedAmount, { from: initialHolder }); + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal('0'); + }); + + it('reverts when more than the full allowance is removed', async function () { + await expectRevertCustomError( + this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: initialHolder }), + 'ERC20FailedDecreaseAllowance', + [spender, approvedAmount, approvedAmount.addn(1)], + ); + }); + }); + } + + describe('when the sender has enough balance', function () { + const amount = initialSupply; + + shouldDecreaseApproval(amount); }); - it('decreases the spender allowance subtracting the requested amount', async function () { - await this.token.decreaseAllowance(spender, approvedAmount.subn(1), { from: initialHolder }); + describe('when the sender does not have enough balance', function () { + const amount = initialSupply.addn(1); - expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal('1'); + shouldDecreaseApproval(amount); }); + }); - it('sets the allowance to zero when all allowance is removed', async function () { - await this.token.decreaseAllowance(spender, approvedAmount, { from: initialHolder }); - expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal('0'); - }); + describe('when the spender is the zero address', function () { + const amount = initialSupply; + const spender = ZERO_ADDRESS; - it('reverts when more than the full allowance is removed', async function () { + it('reverts', async function () { await expectRevertCustomError( - this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: initialHolder }), + this.token.decreaseAllowance(spender, amount, { from: initialHolder }), 'ERC20FailedDecreaseAllowance', - [spender, approvedAmount, approvedAmount.addn(1)], + [spender, 0, amount], ); }); }); - } - - describe('when the sender has enough balance', function () { - const amount = initialSupply; - - shouldDecreaseApproval(amount); }); - describe('when the sender does not have enough balance', function () { - const amount = initialSupply.addn(1); + describe('increase allowance', function () { + const amount = initialSupply; - shouldDecreaseApproval(amount); - }); - }); + describe('when the spender is not the zero address', function () { + const spender = recipient; + + describe('when the sender has enough balance', function () { + it('emits an approval event', async function () { + expectEvent(await this.token.increaseAllowance(spender, amount, { from: initialHolder }), 'Approval', { + owner: initialHolder, + spender: spender, + value: amount, + }); + }); + + describe('when there was no approved amount before', function () { + it('approves the requested amount', async function () { + await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount); + }); + }); + + describe('when the spender had an approved amount', function () { + beforeEach(async function () { + await this.token.approve(spender, new BN(1), { from: initialHolder }); + }); + + it('increases the spender allowance adding the requested amount', async function () { + await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount.addn(1)); + }); + }); + }); - describe('when the spender is the zero address', function () { - const amount = initialSupply; - const spender = ZERO_ADDRESS; + describe('when the sender does not have enough balance', function () { + const amount = initialSupply.addn(1); - it('reverts', async function () { - await expectRevertCustomError( - this.token.decreaseAllowance(spender, amount, { from: initialHolder }), - 'ERC20FailedDecreaseAllowance', - [spender, 0, amount], - ); - }); - }); - }); + it('emits an approval event', async function () { + expectEvent(await this.token.increaseAllowance(spender, amount, { from: initialHolder }), 'Approval', { + owner: initialHolder, + spender: spender, + value: amount, + }); + }); - describe('increase allowance', function () { - const amount = initialSupply; + describe('when there was no approved amount before', function () { + it('approves the requested amount', async function () { + await this.token.increaseAllowance(spender, amount, { from: initialHolder }); - describe('when the spender is not the zero address', function () { - const spender = recipient; + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount); + }); + }); - describe('when the sender has enough balance', function () { - it('emits an approval event', async function () { - expectEvent(await this.token.increaseAllowance(spender, amount, { from: initialHolder }), 'Approval', { - owner: initialHolder, - spender: spender, - value: amount, - }); - }); + describe('when the spender had an approved amount', function () { + beforeEach(async function () { + await this.token.approve(spender, new BN(1), { from: initialHolder }); + }); - describe('when there was no approved amount before', function () { - it('approves the requested amount', async function () { - await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + it('increases the spender allowance adding the requested amount', async function () { + await this.token.increaseAllowance(spender, amount, { from: initialHolder }); - expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount); + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount.addn(1)); + }); + }); }); }); - describe('when the spender had an approved amount', function () { - beforeEach(async function () { - await this.token.approve(spender, new BN(1), { from: initialHolder }); - }); + describe('when the spender is the zero address', function () { + const spender = ZERO_ADDRESS; - it('increases the spender allowance adding the requested amount', async function () { - await this.token.increaseAllowance(spender, amount, { from: initialHolder }); - - expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount.addn(1)); + it('reverts', async function () { + await expectRevertCustomError( + this.token.increaseAllowance(spender, amount, { from: initialHolder }), + 'ERC20InvalidSpender', + [ZERO_ADDRESS], + ); }); }); }); - describe('when the sender does not have enough balance', function () { - const amount = initialSupply.addn(1); + describe('_mint', function () { + const amount = new BN(50); + it('rejects a null account', async function () { + await expectRevertCustomError(this.token.$_mint(ZERO_ADDRESS, amount), 'ERC20InvalidReceiver', [ + ZERO_ADDRESS, + ]); + }); - it('emits an approval event', async function () { - expectEvent(await this.token.increaseAllowance(spender, amount, { from: initialHolder }), 'Approval', { - owner: initialHolder, - spender: spender, - value: amount, - }); + it('rejects overflow', async function () { + const maxUint256 = new BN('2').pow(new BN(256)).subn(1); + await expectRevert( + this.token.$_mint(recipient, maxUint256), + 'reverted with panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)', + ); }); - describe('when there was no approved amount before', function () { - it('approves the requested amount', async function () { - await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + describe('for a non zero account', function () { + beforeEach('minting', async function () { + this.receipt = await this.token.$_mint(recipient, amount); + }); - expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount); + it('increments totalSupply', async function () { + const expectedSupply = initialSupply.add(amount); + expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply); }); - }); - describe('when the spender had an approved amount', function () { - beforeEach(async function () { - await this.token.approve(spender, new BN(1), { from: initialHolder }); + it('increments recipient balance', async function () { + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount); }); - it('increases the spender allowance adding the requested amount', async function () { - await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + it('emits Transfer event', async function () { + const event = expectEvent(this.receipt, 'Transfer', { from: ZERO_ADDRESS, to: recipient }); - expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount.addn(1)); + expect(event.args.value).to.be.bignumber.equal(amount); }); }); }); - }); - describe('when the spender is the zero address', function () { - const spender = ZERO_ADDRESS; - - it('reverts', async function () { - await expectRevertCustomError( - this.token.increaseAllowance(spender, amount, { from: initialHolder }), - 'ERC20InvalidSpender', - [ZERO_ADDRESS], - ); - }); - }); - }); - - describe('_mint', function () { - const amount = new BN(50); - it('rejects a null account', async function () { - await expectRevertCustomError(this.token.$_mint(ZERO_ADDRESS, amount), 'ERC20InvalidReceiver', [ZERO_ADDRESS]); - }); - - it('rejects overflow', async function () { - const maxUint256 = new BN('2').pow(new BN(256)).subn(1); - await expectRevert( - this.token.$_mint(recipient, maxUint256), - 'reverted with panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)', - ); - }); + describe('_burn', function () { + it('rejects a null account', async function () { + await expectRevertCustomError(this.token.$_burn(ZERO_ADDRESS, new BN(1)), 'ERC20InvalidSender', [ + ZERO_ADDRESS, + ]); + }); - describe('for a non zero account', function () { - beforeEach('minting', async function () { - this.receipt = await this.token.$_mint(recipient, amount); - }); + describe('for a non zero account', function () { + it('rejects burning more than balance', async function () { + await expectRevertCustomError( + this.token.$_burn(initialHolder, initialSupply.addn(1)), + 'ERC20InsufficientBalance', + [initialHolder, initialSupply, initialSupply.addn(1)], + ); + }); - it('increments totalSupply', async function () { - const expectedSupply = initialSupply.add(amount); - expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply); - }); + const describeBurn = function (description, amount) { + describe(description, function () { + beforeEach('burning', async function () { + this.receipt = await this.token.$_burn(initialHolder, amount); + }); - it('increments recipient balance', async function () { - expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount); - }); + it('decrements totalSupply', async function () { + const expectedSupply = initialSupply.sub(amount); + expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply); + }); - it('emits Transfer event', async function () { - const event = expectEvent(this.receipt, 'Transfer', { from: ZERO_ADDRESS, to: recipient }); + it('decrements initialHolder balance', async function () { + const expectedBalance = initialSupply.sub(amount); + expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(expectedBalance); + }); - expect(event.args.value).to.be.bignumber.equal(amount); - }); - }); - }); + it('emits Transfer event', async function () { + const event = expectEvent(this.receipt, 'Transfer', { from: initialHolder, to: ZERO_ADDRESS }); - describe('_burn', function () { - it('rejects a null account', async function () { - await expectRevertCustomError(this.token.$_burn(ZERO_ADDRESS, new BN(1)), 'ERC20InvalidSender', [ZERO_ADDRESS]); - }); + expect(event.args.value).to.be.bignumber.equal(amount); + }); + }); + }; - describe('for a non zero account', function () { - it('rejects burning more than balance', async function () { - await expectRevertCustomError( - this.token.$_burn(initialHolder, initialSupply.addn(1)), - 'ERC20InsufficientBalance', - [initialHolder, initialSupply, initialSupply.addn(1)], - ); + describeBurn('for entire balance', initialSupply); + describeBurn('for less amount than balance', initialSupply.subn(1)); + }); }); - const describeBurn = function (description, amount) { - describe(description, function () { - beforeEach('burning', async function () { - this.receipt = await this.token.$_burn(initialHolder, amount); - }); + describe('_update', function () { + const amount = new BN(1); - it('decrements totalSupply', async function () { - const expectedSupply = initialSupply.sub(amount); - expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply); - }); + it('from is the zero address', async function () { + const balanceBefore = await this.token.balanceOf(initialHolder); + const totalSupply = await this.token.totalSupply(); - it('decrements initialHolder balance', async function () { - const expectedBalance = initialSupply.sub(amount); - expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(expectedBalance); + expectEvent(await this.token.$_update(ZERO_ADDRESS, initialHolder, amount), 'Transfer', { + from: ZERO_ADDRESS, + to: initialHolder, + value: amount, }); + expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply.add(amount)); + expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(balanceBefore.add(amount)); + }); - it('emits Transfer event', async function () { - const event = expectEvent(this.receipt, 'Transfer', { from: initialHolder, to: ZERO_ADDRESS }); + it('to is the zero address', async function () { + const balanceBefore = await this.token.balanceOf(initialHolder); + const totalSupply = await this.token.totalSupply(); - expect(event.args.value).to.be.bignumber.equal(amount); + expectEvent(await this.token.$_update(initialHolder, ZERO_ADDRESS, amount), 'Transfer', { + from: initialHolder, + to: ZERO_ADDRESS, + value: amount, }); + expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply.sub(amount)); + expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(balanceBefore.sub(amount)); }); - }; - - describeBurn('for entire balance', initialSupply); - describeBurn('for less amount than balance', initialSupply.subn(1)); - }); - }); - describe('_update', function () { - const amount = new BN(1); + it('from and to are the zero address', async function () { + const totalSupply = await this.token.totalSupply(); - it('from is the zero address', async function () { - const balanceBefore = await this.token.balanceOf(initialHolder); - const totalSupply = await this.token.totalSupply(); + await this.token.$_update(ZERO_ADDRESS, ZERO_ADDRESS, amount); - expectEvent(await this.token.$_update(ZERO_ADDRESS, initialHolder, amount), 'Transfer', { - from: ZERO_ADDRESS, - to: initialHolder, - value: amount, - }); - expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply.add(amount)); - expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(balanceBefore.add(amount)); - }); - - it('to is the zero address', async function () { - const balanceBefore = await this.token.balanceOf(initialHolder); - const totalSupply = await this.token.totalSupply(); - - expectEvent(await this.token.$_update(initialHolder, ZERO_ADDRESS, amount), 'Transfer', { - from: initialHolder, - to: ZERO_ADDRESS, - value: amount, - }); - expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply.sub(amount)); - expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(balanceBefore.sub(amount)); - }); - - it('from and to are the zero address', async function () { - const totalSupply = await this.token.totalSupply(); - - await this.token.$_update(ZERO_ADDRESS, ZERO_ADDRESS, amount); - - expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply); - expectEvent(await this.token.$_update(ZERO_ADDRESS, ZERO_ADDRESS, amount), 'Transfer', { - from: ZERO_ADDRESS, - to: ZERO_ADDRESS, - value: amount, + expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply); + expectEvent(await this.token.$_update(ZERO_ADDRESS, ZERO_ADDRESS, amount), 'Transfer', { + from: ZERO_ADDRESS, + to: ZERO_ADDRESS, + value: amount, + }); + }); }); - }); - }); - describe('_transfer', function () { - shouldBehaveLikeERC20Transfer(initialHolder, recipient, initialSupply, function (from, to, amount) { - return this.token.$_transfer(from, to, amount); - }); + describe('_transfer', function () { + shouldBehaveLikeERC20Transfer(initialHolder, recipient, initialSupply, function (from, to, amount) { + return this.token.$_transfer(from, to, amount); + }); - describe('when the sender is the zero address', function () { - it('reverts', async function () { - await expectRevertCustomError( - this.token.$_transfer(ZERO_ADDRESS, recipient, initialSupply), - 'ERC20InvalidSender', - [ZERO_ADDRESS], - ); + describe('when the sender is the zero address', function () { + it('reverts', async function () { + await expectRevertCustomError( + this.token.$_transfer(ZERO_ADDRESS, recipient, initialSupply), + 'ERC20InvalidSender', + [ZERO_ADDRESS], + ); + }); + }); }); - }); - }); - describe('_approve', function () { - shouldBehaveLikeERC20Approve(initialHolder, recipient, initialSupply, function (owner, spender, amount) { - return this.token.$_approve(owner, spender, amount); - }); + describe('_approve', function () { + shouldBehaveLikeERC20Approve(initialHolder, recipient, initialSupply, function (owner, spender, amount) { + return this.token.$_approve(owner, spender, amount); + }); - describe('when the owner is the zero address', function () { - it('reverts', async function () { - await expectRevertCustomError( - this.token.$_approve(ZERO_ADDRESS, recipient, initialSupply), - 'ERC20InvalidApprover', - [ZERO_ADDRESS], - ); + describe('when the owner is the zero address', function () { + it('reverts', async function () { + await expectRevertCustomError( + this.token.$_approve(ZERO_ADDRESS, recipient, initialSupply), + 'ERC20InvalidApprover', + [ZERO_ADDRESS], + ); + }); + }); }); }); - }); + } }); diff --git a/test/token/ERC20/extensions/ERC20Wrapper.test.js b/test/token/ERC20/extensions/ERC20Wrapper.test.js index ffb97e8a212..94415d088d9 100644 --- a/test/token/ERC20/extensions/ERC20Wrapper.test.js +++ b/test/token/ERC20/extensions/ERC20Wrapper.test.js @@ -10,7 +10,7 @@ const ERC20Decimals = artifacts.require('$ERC20DecimalsMock'); const ERC20Wrapper = artifacts.require('$ERC20Wrapper'); contract('ERC20Wrapper', function (accounts) { - const [initialHolder, recipient, anotherAccount] = accounts; + const [initialHolder, receiver] = accounts; const name = 'My Token'; const symbol = 'MTKN'; @@ -85,7 +85,7 @@ contract('ERC20Wrapper', function (accounts) { it('to other account', async function () { await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder }); - const { tx } = await this.token.depositFor(anotherAccount, initialSupply, { from: initialHolder }); + const { tx } = await this.token.depositFor(receiver, initialSupply, { from: initialHolder }); await expectEvent.inTransaction(tx, this.underlying, 'Transfer', { from: initialHolder, to: this.token.address, @@ -93,7 +93,7 @@ contract('ERC20Wrapper', function (accounts) { }); await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: ZERO_ADDRESS, - to: anotherAccount, + to: receiver, value: initialSupply, }); }); @@ -144,10 +144,10 @@ contract('ERC20Wrapper', function (accounts) { }); it('to other account', async function () { - const { tx } = await this.token.withdrawTo(anotherAccount, initialSupply, { from: initialHolder }); + const { tx } = await this.token.withdrawTo(receiver, initialSupply, { from: initialHolder }); await expectEvent.inTransaction(tx, this.underlying, 'Transfer', { from: this.token.address, - to: anotherAccount, + to: receiver, value: initialSupply, }); await expectEvent.inTransaction(tx, this.token, 'Transfer', { @@ -163,10 +163,10 @@ contract('ERC20Wrapper', function (accounts) { await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder }); await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }); - const { tx } = await this.token.$_recover(anotherAccount); + const { tx } = await this.token.$_recover(receiver); await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: ZERO_ADDRESS, - to: anotherAccount, + to: receiver, value: '0', }); }); @@ -174,10 +174,10 @@ contract('ERC20Wrapper', function (accounts) { it('something to recover', async function () { await this.underlying.transfer(this.token.address, initialSupply, { from: initialHolder }); - const { tx } = await this.token.$_recover(anotherAccount); + const { tx } = await this.token.$_recover(receiver); await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: ZERO_ADDRESS, - to: anotherAccount, + to: receiver, value: initialSupply, }); }); @@ -189,6 +189,6 @@ contract('ERC20Wrapper', function (accounts) { await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }); }); - shouldBehaveLikeERC20(initialSupply, initialHolder, recipient, anotherAccount); + shouldBehaveLikeERC20(initialSupply, accounts); }); });