mirror of openzeppelin-contracts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
openzeppelin-contracts/test/token/ERC20/ERC20.test.js

202 lines
7.5 KiB

const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const { ZERO_ADDRESS } = constants;
const {
shouldBehaveLikeERC20,
shouldBehaveLikeERC20Transfer,
shouldBehaveLikeERC20Approve,
} = require('./ERC20.behavior');
const { expectRevertCustomError } = require('../../helpers/customError');
const TOKENS = [
{ Token: artifacts.require('$ERC20') },
{ Token: artifacts.require('$ERC20ApprovalMock'), forcedApproval: true },
];
contract('ERC20', function (accounts) {
const [initialHolder, recipient] = accounts;
const name = 'My Token';
const symbol = 'MTKN';
const initialSupply = new BN(100);
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);
});
shouldBehaveLikeERC20(initialSupply, accounts, { forcedApproval });
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('_mint', function () {
const value = new BN(50);
it('rejects a null account', async function () {
await expectRevertCustomError(this.token.$_mint(ZERO_ADDRESS, value), '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('for a non zero account', function () {
beforeEach('minting', async function () {
this.receipt = await this.token.$_mint(recipient, value);
});
it('increments totalSupply', async function () {
const expectedSupply = initialSupply.add(value);
expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply);
});
it('increments recipient balance', async function () {
expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(value);
});
it('emits Transfer event', async function () {
const event = expectEvent(this.receipt, 'Transfer', { from: ZERO_ADDRESS, to: recipient });
expect(event.args.value).to.be.bignumber.equal(value);
});
});
});
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 () {
it('rejects burning more than balance', async function () {
await expectRevertCustomError(
this.token.$_burn(initialHolder, initialSupply.addn(1)),
'ERC20InsufficientBalance',
[initialHolder, initialSupply, initialSupply.addn(1)],
);
});
const describeBurn = function (description, value) {
describe(description, function () {
beforeEach('burning', async function () {
this.receipt = await this.token.$_burn(initialHolder, value);
});
it('decrements totalSupply', async function () {
const expectedSupply = initialSupply.sub(value);
expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply);
});
it('decrements initialHolder balance', async function () {
const expectedBalance = initialSupply.sub(value);
expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(expectedBalance);
});
it('emits Transfer event', async function () {
const event = expectEvent(this.receipt, 'Transfer', { from: initialHolder, to: ZERO_ADDRESS });
expect(event.args.value).to.be.bignumber.equal(value);
});
});
};
describeBurn('for entire balance', initialSupply);
describeBurn('for less value than balance', initialSupply.subn(1));
});
});
describe('_update', function () {
const value = new BN(1);
it('from is the zero address', async function () {
const balanceBefore = await this.token.balanceOf(initialHolder);
const totalSupply = await this.token.totalSupply();
expectEvent(await this.token.$_update(ZERO_ADDRESS, initialHolder, value), 'Transfer', {
from: ZERO_ADDRESS,
to: initialHolder,
value: value,
});
expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply.add(value));
expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(balanceBefore.add(value));
});
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, value), 'Transfer', {
from: initialHolder,
to: ZERO_ADDRESS,
value: value,
});
expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply.sub(value));
expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(balanceBefore.sub(value));
});
it('from and to are the zero address', async function () {
const totalSupply = await this.token.totalSupply();
await this.token.$_update(ZERO_ADDRESS, ZERO_ADDRESS, value);
expect(await this.token.totalSupply()).to.be.bignumber.equal(totalSupply);
expectEvent(await this.token.$_update(ZERO_ADDRESS, ZERO_ADDRESS, value), 'Transfer', {
from: ZERO_ADDRESS,
to: ZERO_ADDRESS,
value: value,
});
});
});
describe('_transfer', function () {
shouldBehaveLikeERC20Transfer(initialHolder, recipient, initialSupply, function (from, to, value) {
return this.token.$_transfer(from, to, value);
});
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, value) {
return this.token.$_approve(owner, spender, value);
});
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],
);
});
});
});
});
}
});