| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 | const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');const { expect } = require('chai');const { ZERO_ADDRESS } = constants;function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recipient, anotherAccount) {  describe('total supply', function () {    it('returns the total amount of tokens', async function () {      expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply);    });  });  describe('balanceOf', function () {    describe('when the requested account has no tokens', function () {      it('returns zero', async function () {        expect(await this.token.balanceOf(anotherAccount)).to.be.bignumber.equal('0');      });    });    describe('when the requested account has some tokens', function () {      it('returns the total amount of tokens', async function () {        expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(initialSupply);      });    });  });  describe('transfer', function () {    shouldBehaveLikeERC20Transfer(errorPrefix, initialHolder, recipient, initialSupply,      function (from, to, value) {        return this.token.transfer(to, value, { from });      },    );  });  describe('transfer from', function () {    const spender = recipient;    describe('when the token owner is not the zero address', function () {      const tokenOwner = initialHolder;      describe('when the recipient is not the zero address', function () {        const to = anotherAccount;        describe('when the spender has enough approved balance', function () {          beforeEach(async function () {            await this.token.approve(spender, initialSupply, { from: initialHolder });          });          describe('when the token owner has enough balance', function () {            const amount = initialSupply;            it('transfers the requested amount', async function () {              await this.token.transferFrom(tokenOwner, to, amount, { from: spender });              expect(await this.token.balanceOf(tokenOwner)).to.be.bignumber.equal('0');              expect(await this.token.balanceOf(to)).to.be.bignumber.equal(amount);            });            it('decreases the spender allowance', async function () {              await this.token.transferFrom(tokenOwner, to, amount, { from: spender });              expect(await this.token.allowance(tokenOwner, spender)).to.be.bignumber.equal('0');            });            it('emits a transfer event', async function () {              const { logs } = await this.token.transferFrom(tokenOwner, to, amount, { from: spender });              expectEvent.inLogs(logs, 'Transfer', {                from: tokenOwner,                to: to,                value: amount,              });            });            it('emits an approval event', async function () {              const { logs } = await this.token.transferFrom(tokenOwner, to, amount, { from: spender });              expectEvent.inLogs(logs, 'Approval', {                owner: tokenOwner,                spender: spender,                value: await this.token.allowance(tokenOwner, spender),              });            });          });          describe('when the token owner does not have enough balance', function () {            const amount = initialSupply.addn(1);            it('reverts', async function () {              await expectRevert(this.token.transferFrom(                tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds balance`,              );            });          });        });        describe('when the spender does not have enough approved balance', function () {          beforeEach(async function () {            await this.token.approve(spender, initialSupply.subn(1), { from: tokenOwner });          });          describe('when the token owner has enough balance', function () {            const amount = initialSupply;            it('reverts', async function () {              await expectRevert(this.token.transferFrom(                tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds allowance`,              );            });          });          describe('when the token owner does not have enough balance', function () {            const amount = initialSupply.addn(1);            it('reverts', async function () {              await expectRevert(this.token.transferFrom(                tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds balance`,              );            });          });        });      });      describe('when the recipient is the zero address', function () {        const amount = initialSupply;        const to = ZERO_ADDRESS;        beforeEach(async function () {          await this.token.approve(spender, amount, { from: tokenOwner });        });        it('reverts', async function () {          await expectRevert(this.token.transferFrom(            tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer to the zero address`,          );        });      });    });    describe('when the token owner is the zero address', function () {      const amount = 0;      const tokenOwner = ZERO_ADDRESS;      const to = recipient;      it('reverts', async function () {        await expectRevert(this.token.transferFrom(          tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer from the zero address`,        );      });    });  });  describe('approve', function () {    shouldBehaveLikeERC20Approve(errorPrefix, initialHolder, recipient, initialSupply,      function (owner, spender, amount) {        return this.token.approve(spender, amount, { from: owner });      },    );  });}function shouldBehaveLikeERC20Transfer (errorPrefix, from, to, balance, transfer) {  describe('when the recipient is not the zero address', function () {    describe('when the sender does not have enough balance', function () {      const amount = balance.addn(1);      it('reverts', async function () {        await expectRevert(transfer.call(this, from, to, amount),          `${errorPrefix}: transfer amount exceeds balance`,        );      });    });    describe('when the sender transfers all balance', function () {      const amount = balance;      it('transfers the requested amount', async function () {        await transfer.call(this, from, to, amount);        expect(await this.token.balanceOf(from)).to.be.bignumber.equal('0');        expect(await this.token.balanceOf(to)).to.be.bignumber.equal(amount);      });      it('emits a transfer event', async function () {        const { logs } = await transfer.call(this, from, to, amount);        expectEvent.inLogs(logs, 'Transfer', {          from,          to,          value: amount,        });      });    });    describe('when the sender transfers zero tokens', function () {      const amount = new BN('0');      it('transfers the requested amount', async function () {        await transfer.call(this, from, to, amount);        expect(await this.token.balanceOf(from)).to.be.bignumber.equal(balance);        expect(await this.token.balanceOf(to)).to.be.bignumber.equal('0');      });      it('emits a transfer event', async function () {        const { logs } = await transfer.call(this, from, to, amount);        expectEvent.inLogs(logs, 'Transfer', {          from,          to,          value: amount,        });      });    });  });  describe('when the recipient is the zero address', function () {    it('reverts', async function () {      await expectRevert(transfer.call(this, from, ZERO_ADDRESS, balance),        `${errorPrefix}: transfer to the zero address`,      );    });  });}function shouldBehaveLikeERC20Approve (errorPrefix, owner, spender, supply, approve) {  describe('when the spender is not the zero address', function () {    describe('when the sender has enough balance', function () {      const amount = supply;      it('emits an approval event', async function () {        const { logs } = await approve.call(this, owner, spender, amount);        expectEvent.inLogs(logs, 'Approval', {          owner: owner,          spender: spender,          value: amount,        });      });      describe('when there was no approved amount before', function () {        it('approves the requested amount', async function () {          await approve.call(this, owner, spender, amount);          expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(amount);        });      });      describe('when the spender had an approved amount', function () {        beforeEach(async function () {          await approve.call(this, owner, spender, new BN(1));        });        it('approves the requested amount and replaces the previous one', async function () {          await approve.call(this, owner, spender, amount);          expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(amount);        });      });    });    describe('when the sender does not have enough balance', function () {      const amount = supply.addn(1);      it('emits an approval event', async function () {        const { logs } = await approve.call(this, owner, spender, amount);        expectEvent.inLogs(logs, 'Approval', {          owner: owner,          spender: spender,          value: amount,        });      });      describe('when there was no approved amount before', function () {        it('approves the requested amount', async function () {          await approve.call(this, owner, spender, amount);          expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(amount);        });      });      describe('when the spender had an approved amount', function () {        beforeEach(async function () {          await approve.call(this, owner, spender, new BN(1));        });        it('approves the requested amount and replaces the previous one', async function () {          await approve.call(this, owner, spender, amount);          expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(amount);        });      });    });  });  describe('when the spender is the zero address', function () {    it('reverts', async function () {      await expectRevert(approve.call(this, owner, ZERO_ADDRESS, supply),        `${errorPrefix}: approve to the zero address`,      );    });  });}module.exports = {  shouldBehaveLikeERC20,  shouldBehaveLikeERC20Transfer,  shouldBehaveLikeERC20Approve,};
 |