diff --git a/test/fair/erc20/ERC20.behavior.js b/test/fair/zos/ERC20.behavior.js similarity index 88% rename from test/fair/erc20/ERC20.behavior.js rename to test/fair/zos/ERC20.behavior.js index dec5e5a4..43e3498b 100644 --- a/test/fair/erc20/ERC20.behavior.js +++ b/test/fair/zos/ERC20.behavior.js @@ -1,4 +1,4 @@ -// Source: openzeppelin-contracts +// Source: https://github.com/OpenZeppelin/openzeppelin-contracts const { BN, @@ -106,9 +106,9 @@ function shouldBehaveLikeERC20( ); expectEvent.inLogs(logs, "Transfer", { - _from: tokenOwner, - _to: to, - _value: amount + from: tokenOwner, + to: to, + value: amount }); }); @@ -121,9 +121,9 @@ function shouldBehaveLikeERC20( ); expectEvent.inLogs(logs, "Approval", { - _owner: tokenOwner, - _spender: spender, - _value: await this.token.allowance(tokenOwner, spender) + owner: tokenOwner, + spender: spender, + value: await this.token.allowance(tokenOwner, spender) }); }); }); @@ -132,10 +132,11 @@ function shouldBehaveLikeERC20( const amount = initialSupply.addn(1); it("reverts", async function() { - await expectRevert.unspecified( + await expectRevert( this.token.transferFrom(tokenOwner, to, amount, { from: spender - }) + }), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." ); }); }); @@ -152,10 +153,11 @@ function shouldBehaveLikeERC20( const amount = initialSupply; it("reverts", async function() { - await expectRevert.unspecified( + await expectRevert( this.token.transferFrom(tokenOwner, to, amount, { from: spender - }) + }), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." ); }); }); @@ -164,10 +166,11 @@ function shouldBehaveLikeERC20( const amount = initialSupply.addn(1); it("reverts", async function() { - await expectRevert.unspecified( + await expectRevert( this.token.transferFrom(tokenOwner, to, amount, { from: spender - }) + }), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." ); }); }); @@ -185,7 +188,7 @@ function shouldBehaveLikeERC20( it("reverts", async function() { await expectRevert( this.token.transferFrom(tokenOwner, to, amount, { from: spender }), - `${errorPrefix}: send to the zero address` + `${errorPrefix}: transfer to the zero address` ); }); }); @@ -199,7 +202,7 @@ function shouldBehaveLikeERC20( it("reverts", async function() { await expectRevert( this.token.transferFrom(tokenOwner, to, amount, { from: spender }), - `${errorPrefix}: send from the zero address` + `${errorPrefix}: transfer from the zero address` ); }); }); @@ -230,7 +233,10 @@ function shouldBehaveLikeERC20Transfer( const amount = balance.addn(1); it("reverts", async function() { - await expectRevert.unspecified(transfer.call(this, from, to, amount)); + await expectRevert( + transfer.call(this, from, to, amount), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." + ); }); }); @@ -249,9 +255,9 @@ function shouldBehaveLikeERC20Transfer( const { logs } = await transfer.call(this, from, to, amount); expectEvent.inLogs(logs, "Transfer", { - _from: from, - _to: to, - _value: amount + from, + to, + value: amount }); }); }); @@ -271,9 +277,9 @@ function shouldBehaveLikeERC20Transfer( const { logs } = await transfer.call(this, from, to, amount); expectEvent.inLogs(logs, "Transfer", { - _from: from, - _to: to, - _value: amount + from, + to, + value: amount }); }); }); @@ -283,7 +289,7 @@ function shouldBehaveLikeERC20Transfer( it("reverts", async function() { await expectRevert( transfer.call(this, from, ZERO_ADDRESS, balance), - `${errorPrefix}: send to the zero address` + `${errorPrefix}: transfer to the zero address` ); }); }); @@ -304,9 +310,9 @@ function shouldBehaveLikeERC20Approve( const { logs } = await approve.call(this, owner, spender, amount); expectEvent.inLogs(logs, "Approval", { - _owner: owner, - _spender: spender, - _value: amount + owner: owner, + spender: spender, + value: amount }); }); @@ -342,9 +348,9 @@ function shouldBehaveLikeERC20Approve( const { logs } = await approve.call(this, owner, spender, amount); expectEvent.inLogs(logs, "Approval", { - _owner: owner, - _spender: spender, - _value: amount + owner: owner, + spender: spender, + value: amount }); }); diff --git a/test/fair/zos/ERC20.test.js b/test/fair/zos/ERC20.test.js new file mode 100644 index 00000000..c1277dd2 --- /dev/null +++ b/test/fair/zos/ERC20.test.js @@ -0,0 +1,375 @@ +const { approveAll, deployDat } = require("../../helpers"); + +// Source: https://github.com/OpenZeppelin/openzeppelin-contracts + +const { + BN, + constants, + expectEvent, + expectRevert +} = require("@openzeppelin/test-helpers"); +const { expect } = require("chai"); +const { ZERO_ADDRESS } = constants; + +const { + shouldBehaveLikeERC20, + shouldBehaveLikeERC20Transfer, + shouldBehaveLikeERC20Approve +} = require("./ERC20.behavior"); + +contract("ERC20", function(accounts) { + const [_, initialHolder, recipient, anotherAccount] = accounts; + const initialSupply = new BN(100); + + beforeEach(async function() { + const contracts = await deployDat(accounts, { + initGoal: 0, + initReserve: initialSupply, + beneficiary: initialHolder + }); + await approveAll(contracts, accounts); + this.token = contracts.dat; + }); + + shouldBehaveLikeERC20( + "ERC20", + initialSupply, + initialHolder, + recipient, + anotherAccount + ); + + 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() { + await expectRevert( + this.token.decreaseAllowance(spender, amount, { + from: initialHolder + }), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." + ); + }); + }); + + describe("when the spender had an approved amount", function() { + const approvedAmount = amount; + + beforeEach(async function() { + ({ logs: this.logs } = await this.token.approve( + spender, + approvedAmount, + { from: initialHolder } + )); + }); + + it("emits an approval event", async function() { + const { logs } = await this.token.decreaseAllowance( + spender, + approvedAmount, + { from: initialHolder } + ); + + expectEvent.inLogs(logs, "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 expectRevert( + this.token.decreaseAllowance(spender, approvedAmount.addn(1), { + from: initialHolder + }), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." + ); + }); + }); + } + + 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); + + shouldDecreaseApproval(amount); + }); + }); + + describe("when the spender is the zero address", function() { + const amount = initialSupply; + const spender = ZERO_ADDRESS; + + it("reverts", async function() { + await expectRevert( + this.token.decreaseAllowance(spender, amount, { + from: initialHolder + }), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." + ); + }); + }); + }); + + describe("increase allowance", function() { + const amount = initialSupply; + + 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() { + const { logs } = await this.token.increaseAllowance(spender, amount, { + from: initialHolder + }); + + expectEvent.inLogs(logs, "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 sender does not have enough balance", function() { + const amount = initialSupply.addn(1); + + it("emits an approval event", async function() { + const { logs } = await this.token.increaseAllowance(spender, amount, { + from: initialHolder + }); + + expectEvent.inLogs(logs, "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 spender = ZERO_ADDRESS; + + it("reverts", async function() { + await expectRevert( + this.token.increaseAllowance(spender, amount, { + from: initialHolder + }), + "ERC20: approve to the zero address" + ); + }); + }); + }); + + describe("_mint", function() { + const amount = new BN("289827534923788771374"); + it("rejects a null account", async function() { + await expectRevert( + this.token.buy(ZERO_ADDRESS, "420000000000000000000", 1, { + value: "420000000000000000000", + from: accounts[1] + }), + "INVALID_ADDRESS" + ); + }); + + describe("for a non zero account", function() { + beforeEach("minting", async function() { + const { logs } = await this.token.buy( + recipient, + "420000000000000000000", + 1, + { + value: "420000000000000000000", + from: accounts[1] + } + ); + this.logs = logs; + }); + + it("increments totalSupply", async function() { + const expectedSupply = initialSupply.add(amount); + 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( + amount + ); + }); + + it("emits Transfer event", async function() { + const event = expectEvent.inLogs(this.logs, "Transfer", { + from: ZERO_ADDRESS, + to: recipient + }); + + expect(event.args.value).to.be.bignumber.equal(amount); + }); + }); + }); + + describe("_burn", function() { + describe("for a non zero account", function() { + it("rejects burning more than balance", async function() { + await expectRevert( + this.token.burn(initialSupply.addn(1), { from: initialHolder }), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." + ); + }); + + const describeBurn = function(description, amount) { + describe(description, function() { + beforeEach("burning", async function() { + const { logs } = await this.token.burn(amount, { + from: initialHolder + }); + this.logs = logs; + }); + + it("decrements totalSupply", async function() { + const expectedSupply = initialSupply.sub(amount); + expect(await this.token.totalSupply()).to.be.bignumber.equal( + expectedSupply + ); + }); + + it("decrements initialHolder balance", async function() { + const expectedBalance = initialSupply.sub(amount); + expect( + await this.token.balanceOf(initialHolder) + ).to.be.bignumber.equal(expectedBalance); + }); + + it("emits Transfer event", async function() { + const event = expectEvent.inLogs(this.logs, "Transfer", { + from: initialHolder, + to: ZERO_ADDRESS + }); + + expect(event.args.value).to.be.bignumber.equal(amount); + }); + }); + }; + + describeBurn("for entire balance", initialSupply); + describeBurn("for less amount than balance", initialSupply.subn(1)); + }); + }); + + describe("_transfer", function() { + shouldBehaveLikeERC20Transfer( + "ERC20", + initialHolder, + recipient, + initialSupply, + function(from, to, amount) { + return this.token.transfer(to, amount, { from }); + } + ); + }); + + describe("_approve", function() { + shouldBehaveLikeERC20Approve( + "ERC20", + initialHolder, + recipient, + initialSupply, + function(owner, spender, amount) { + return this.token.approve(spender, amount, { from: owner }); + } + ); + }); +}); diff --git a/test/fair/zos/ERC20Burnable.test.js b/test/fair/zos/ERC20Burnable.test.js new file mode 100644 index 00000000..13f72f16 --- /dev/null +++ b/test/fair/zos/ERC20Burnable.test.js @@ -0,0 +1,26 @@ +const { approveAll, deployDat } = require("../../helpers"); + +// Source: https://github.com/OpenZeppelin/openzeppelin-contracts + +const { BN } = require("@openzeppelin/test-helpers"); + +const { + shouldBehaveLikeERC20Burnable +} = require("./behaviors/ERC20Burnable.behavior"); + +contract("ERC20Burnable", function(accounts) { + const [_, owner, ...otherAccounts] = accounts; + const initialBalance = new BN(1000); + + beforeEach(async function() { + const contracts = await deployDat(accounts, { + initGoal: 0, + beneficiary: owner, + initReserve: initialBalance + }); + await approveAll(contracts, accounts); + this.token = contracts.dat; + }); + + shouldBehaveLikeERC20Burnable(owner, initialBalance, otherAccounts); +}); diff --git a/test/fair/zos/ERC20Detailed.test.js b/test/fair/zos/ERC20Detailed.test.js new file mode 100644 index 00000000..f5dcf87b --- /dev/null +++ b/test/fair/zos/ERC20Detailed.test.js @@ -0,0 +1,35 @@ +const { approveAll, deployDat } = require("../../helpers"); + +// Source: https://github.com/OpenZeppelin/openzeppelin-contracts + +const { BN } = require("@openzeppelin/test-helpers"); + +const { expect } = require("chai"); + +contract("ERC20Detailed", function(accounts) { + const _name = "My Detailed ERC20"; + const _symbol = "MDT"; + const _decimals = new BN(18); + + beforeEach(async function() { + const contracts = await deployDat(accounts, { + initGoal: 0, + name: _name, + symbol: _symbol + }); + await approveAll(contracts, accounts); + this.token = contracts.dat; + }); + + 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 an amount of decimals", async function() { + expect(await this.token.decimals()).to.be.bignumber.equal(_decimals); + }); +}); diff --git a/test/fair/zos/behaviors/ERC20Burnable.behavior.js b/test/fair/zos/behaviors/ERC20Burnable.behavior.js new file mode 100644 index 00000000..f19a44af --- /dev/null +++ b/test/fair/zos/behaviors/ERC20Burnable.behavior.js @@ -0,0 +1,62 @@ +// Source: https://github.com/OpenZeppelin/openzeppelin-contracts + +const { + BN, + constants, + expectEvent, + expectRevert +} = require("@openzeppelin/test-helpers"); +const { ZERO_ADDRESS } = constants; + +const { expect } = require("chai"); + +function shouldBehaveLikeERC20Burnable(owner, initialBalance, [burner]) { + describe("burn", function() { + describe("when the given amount is not greater than balance of the sender", function() { + context("for a zero amount", function() { + shouldBurn(new BN(0)); + }); + + context("for a non-zero amount", function() { + shouldBurn(new BN(100)); + }); + + function shouldBurn(amount) { + beforeEach(async function() { + ({ logs: this.logs } = await this.token.burn(amount, { + from: owner + })); + }); + + it("burns the requested amount", async function() { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal( + initialBalance.sub(amount) + ); + }); + + it("emits a transfer event", async function() { + expectEvent.inLogs(this.logs, "Transfer", { + from: owner, + to: ZERO_ADDRESS, + value: amount + }); + }); + } + }); + + describe("when the given amount is greater than the balance of the sender", function() { + const amount = initialBalance.addn(1); + + it("reverts", async function() { + await expectRevert( + this.token.burn(amount, { from: owner }), + "SafeMath: subtraction overflow -- Reason given: SafeMath: subtraction overflow." + ); + }); + }); + }); +} + +module.exports = { + shouldBehaveLikeERC20Burnable +};