Add ERC20 opt-in migration contract (#1054)
* Extract standard token behaviuor to reuse it in other tests * Add opt in ERC20 migration contract * Make migration contract not to depend from standard token * Changes based on feedback * Improve MigratableERC20 inline documentation * move behaviors to behaviors directory * refactor MigratableERC20 into ERC20Migrator * fix errors * change expectEvent to support multiple events with same name * fix tests * update documentation * rename MigratableERC20 files to ERC20Migrator * move to drafts * test beginMigration * rename to ERC20Migrator * missing semicolon (╯°□°)╯︵ ┻━┻ * add non-zero check * improve documentation based on review comments * improve test descriptions * improve docs * add getters * fix contract * improve tests
This commit is contained in:
committed by
Francisco Giordano
parent
4b33eaefa2
commit
92133be7ea
156
test/drafts/ERC20Migrator.test.js
Normal file
156
test/drafts/ERC20Migrator.test.js
Normal file
@ -0,0 +1,156 @@
|
||||
const { assertRevert } = require('../helpers/assertRevert');
|
||||
|
||||
const ERC20Mock = artifacts.require('ERC20Mock');
|
||||
const ERC20Mintable = artifacts.require('ERC20Mintable');
|
||||
const ERC20Migrator = artifacts.require('ERC20Migrator');
|
||||
|
||||
const BigNumber = web3.eth.BigNumber;
|
||||
|
||||
require('chai')
|
||||
.use(require('chai-bignumber')(BigNumber))
|
||||
.should();
|
||||
|
||||
contract('ERC20Migrator', function ([_, owner, recipient, anotherAccount]) {
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
const totalSupply = 200;
|
||||
|
||||
it('reverts with a null legacy token address', async function () {
|
||||
await assertRevert(ERC20Migrator.new(ZERO_ADDRESS));
|
||||
});
|
||||
|
||||
describe('with tokens and migrator', function () {
|
||||
beforeEach('deploying tokens and migrator', async function () {
|
||||
this.legacyToken = await ERC20Mock.new(owner, totalSupply);
|
||||
this.migrator = await ERC20Migrator.new(this.legacyToken.address);
|
||||
this.newToken = await ERC20Mintable.new();
|
||||
});
|
||||
|
||||
it('returns legacy token', async function () {
|
||||
(await this.migrator.legacyToken()).should.be.equal(this.legacyToken.address);
|
||||
});
|
||||
|
||||
describe('beginMigration', function () {
|
||||
it('reverts with a null new token address', async function () {
|
||||
await assertRevert(this.migrator.beginMigration(ZERO_ADDRESS));
|
||||
});
|
||||
|
||||
it('reverts if not a minter of the token', async function () {
|
||||
await assertRevert(this.migrator.beginMigration(this.newToken.address));
|
||||
});
|
||||
|
||||
it('succeeds if it is a minter of the token', async function () {
|
||||
await this.newToken.addMinter(this.migrator.address);
|
||||
await this.migrator.beginMigration(this.newToken.address);
|
||||
});
|
||||
|
||||
it('reverts the second time it is called', async function () {
|
||||
await this.newToken.addMinter(this.migrator.address);
|
||||
await this.migrator.beginMigration(this.newToken.address);
|
||||
await assertRevert(this.migrator.beginMigration(this.newToken.address));
|
||||
});
|
||||
});
|
||||
|
||||
describe('once migration began', function () {
|
||||
beforeEach('beginning migration', async function () {
|
||||
await this.newToken.addMinter(this.migrator.address);
|
||||
await this.migrator.beginMigration(this.newToken.address);
|
||||
});
|
||||
|
||||
it('returns new token', async function () {
|
||||
(await this.migrator.newToken()).should.be.equal(this.newToken.address);
|
||||
});
|
||||
|
||||
describe('migrateAll', function () {
|
||||
const baseAmount = totalSupply;
|
||||
|
||||
describe('when the approved balance is equal to the owned balance', function () {
|
||||
const amount = baseAmount;
|
||||
|
||||
beforeEach('approving the whole balance to the new contract', async function () {
|
||||
await this.legacyToken.approve(this.migrator.address, amount, { from: owner });
|
||||
});
|
||||
|
||||
beforeEach('migrating token', async function () {
|
||||
const tx = await this.migrator.migrateAll(owner);
|
||||
this.logs = tx.receipt.logs;
|
||||
});
|
||||
|
||||
it('mints the same balance of the new token', async function () {
|
||||
const currentBalance = await this.newToken.balanceOf(owner);
|
||||
currentBalance.should.be.bignumber.equal(amount);
|
||||
});
|
||||
|
||||
it('burns a given amount of old tokens', async function () {
|
||||
const currentBurnedBalance = await this.legacyToken.balanceOf(this.migrator.address);
|
||||
currentBurnedBalance.should.be.bignumber.equal(amount);
|
||||
|
||||
const currentLegacyTokenBalance = await this.legacyToken.balanceOf(owner);
|
||||
currentLegacyTokenBalance.should.be.bignumber.equal(0);
|
||||
});
|
||||
|
||||
it('updates the total supply', async function () {
|
||||
const currentSupply = await this.newToken.totalSupply();
|
||||
currentSupply.should.be.bignumber.equal(amount);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the approved balance is lower than the owned balance', function () {
|
||||
const amount = baseAmount - 1;
|
||||
|
||||
beforeEach('approving part of the balance to the new contract', async function () {
|
||||
await this.legacyToken.approve(this.migrator.address, amount, { from: owner });
|
||||
await this.migrator.migrateAll(owner);
|
||||
});
|
||||
|
||||
it('migrates only approved amount', async function () {
|
||||
const currentBalance = await this.newToken.balanceOf(owner);
|
||||
currentBalance.should.be.bignumber.equal(amount);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('migrate', function () {
|
||||
const baseAmount = 50;
|
||||
|
||||
beforeEach('approving tokens to the new contract', async function () {
|
||||
await this.legacyToken.approve(this.migrator.address, baseAmount, { from: owner });
|
||||
});
|
||||
|
||||
describe('when the amount is equal to the one approved', function () {
|
||||
const amount = baseAmount;
|
||||
|
||||
beforeEach('migrate token', async function () {
|
||||
({ logs: this.logs } = await this.migrator.migrate(owner, amount));
|
||||
});
|
||||
|
||||
it('mints that amount of the new token', async function () {
|
||||
const currentBalance = await this.newToken.balanceOf(owner);
|
||||
currentBalance.should.be.bignumber.equal(amount);
|
||||
});
|
||||
|
||||
it('burns a given amount of old tokens', async function () {
|
||||
const currentBurnedBalance = await this.legacyToken.balanceOf(this.migrator.address);
|
||||
currentBurnedBalance.should.be.bignumber.equal(amount);
|
||||
|
||||
const currentLegacyTokenBalance = await this.legacyToken.balanceOf(owner);
|
||||
currentLegacyTokenBalance.should.be.bignumber.equal(totalSupply - amount);
|
||||
});
|
||||
|
||||
it('updates the total supply', async function () {
|
||||
const currentSupply = await this.newToken.totalSupply();
|
||||
currentSupply.should.be.bignumber.equal(amount);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given amount is higher than the one approved', function () {
|
||||
const amount = baseAmount + 1;
|
||||
|
||||
it('reverts', async function () {
|
||||
await assertRevert(this.migrator.migrate(owner, amount));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,12 +1,24 @@
|
||||
const should = require('chai').should();
|
||||
|
||||
function inLogs (logs, eventName, eventArgs = {}) {
|
||||
const event = logs.find(e => e.event === eventName);
|
||||
const event = logs.find(function (e) {
|
||||
if (e.event === eventName) {
|
||||
let matches = true;
|
||||
|
||||
for (const [k, v] of Object.entries(eventArgs)) {
|
||||
if (e.args[k] !== v) {
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
should.exist(event);
|
||||
for (const [k, v] of Object.entries(eventArgs)) {
|
||||
should.exist(event.args[k]);
|
||||
event.args[k].should.equal(v);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const { shouldBehaveLikeERC20Burnable } = require('./ERC20Burnable.behavior');
|
||||
const { shouldBehaveLikeERC20Burnable } = require('./behaviors/ERC20Burnable.behavior');
|
||||
const ERC20BurnableMock = artifacts.require('ERC20BurnableMock');
|
||||
|
||||
contract('ERC20Burnable', function ([_, owner, ...otherAccounts]) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
const { assertRevert } = require('../../helpers/assertRevert');
|
||||
const { ether } = require('../../helpers/ether');
|
||||
const { shouldBehaveLikeERC20Mintable } = require('./ERC20Mintable.behavior');
|
||||
const { shouldBehaveLikeERC20Capped } = require('./ERC20Capped.behavior');
|
||||
const { shouldBehaveLikeERC20Mintable } = require('./behaviors/ERC20Mintable.behavior');
|
||||
const { shouldBehaveLikeERC20Capped } = require('./behaviors/ERC20Capped.behavior');
|
||||
|
||||
const ERC20Capped = artifacts.require('ERC20Capped');
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const { shouldBehaveLikeERC20Mintable } = require('./ERC20Mintable.behavior');
|
||||
const { shouldBehaveLikeERC20Mintable } = require('./behaviors/ERC20Mintable.behavior');
|
||||
const ERC20MintableMock = artifacts.require('ERC20MintableMock');
|
||||
const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior');
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const { assertRevert } = require('../../helpers/assertRevert');
|
||||
const expectEvent = require('../../helpers/expectEvent');
|
||||
const { assertRevert } = require('../../../helpers/assertRevert');
|
||||
const expectEvent = require('../../../helpers/expectEvent');
|
||||
|
||||
const BigNumber = web3.BigNumber;
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
@ -1,4 +1,5 @@
|
||||
const { expectThrow } = require('../../helpers/expectThrow');
|
||||
const { expectThrow } = require('../../../helpers/expectThrow');
|
||||
const expectEvent = require('../../../helpers/expectEvent');
|
||||
|
||||
const BigNumber = web3.BigNumber;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const { assertRevert } = require('../../helpers/assertRevert');
|
||||
const expectEvent = require('../../helpers/expectEvent');
|
||||
const { assertRevert } = require('../../../helpers/assertRevert');
|
||||
const expectEvent = require('../../../helpers/expectEvent');
|
||||
|
||||
const BigNumber = web3.BigNumber;
|
||||
|
||||
Reference in New Issue
Block a user