Ver código fonte

Do not emit Approval event when calling transferFrom (#4370)

Co-authored-by: Ernesto García <ernestognw@gmail.com>
Co-authored-by: Francisco <fg@frang.io>
Hadrien Croubois 2 anos atrás
pai
commit
1e0e4e20bb

+ 5 - 0
.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.

+ 10 - 0
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);
+    }
+}

+ 25 - 2
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);
             }
         }
     }

+ 19 - 7
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 () {

+ 272 - 272
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],
+            );
+          });
+        });
       });
     });
-  });
+  }
 });

+ 10 - 10
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);
   });
 });