mirror of
https://github.com/th30d4y/OpenLearnX.git
synced 2026-05-27 03:36:32 +00:00
Fix .gitignore: stop tracking ignored files
This commit is contained in:
@@ -0,0 +1,597 @@
|
||||
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
|
||||
const { ZERO_ADDRESS } = constants;
|
||||
|
||||
const { expect } = require('chai');
|
||||
|
||||
const ERC777SenderRecipientMock = artifacts.require('ERC777SenderRecipientMock');
|
||||
|
||||
function shouldBehaveLikeERC777DirectSendBurn(holder, recipient, data) {
|
||||
shouldBehaveLikeERC777DirectSend(holder, recipient, data);
|
||||
shouldBehaveLikeERC777DirectBurn(holder, data);
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777OperatorSendBurn(holder, recipient, operator, data, operatorData) {
|
||||
shouldBehaveLikeERC777OperatorSend(holder, recipient, operator, data, operatorData);
|
||||
shouldBehaveLikeERC777OperatorBurn(holder, operator, data, operatorData);
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, recipient, operator, data, operatorData) {
|
||||
shouldBehaveLikeERC777UnauthorizedOperatorSend(holder, recipient, operator, data, operatorData);
|
||||
shouldBehaveLikeERC777UnauthorizedOperatorBurn(holder, operator, data, operatorData);
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777DirectSend(holder, recipient, data) {
|
||||
describe('direct send', function () {
|
||||
context('when the sender has tokens', function () {
|
||||
shouldDirectSendTokens(holder, recipient, new BN('0'), data);
|
||||
shouldDirectSendTokens(holder, recipient, new BN('1'), data);
|
||||
|
||||
it('reverts when sending more than the balance', async function () {
|
||||
const balance = await this.token.balanceOf(holder);
|
||||
await expectRevert.unspecified(this.token.send(recipient, balance.addn(1), data, { from: holder }));
|
||||
});
|
||||
|
||||
it('reverts when sending to the zero address', async function () {
|
||||
await expectRevert.unspecified(this.token.send(ZERO_ADDRESS, new BN('1'), data, { from: holder }));
|
||||
});
|
||||
});
|
||||
|
||||
context('when the sender has no tokens', function () {
|
||||
removeBalance(holder);
|
||||
|
||||
shouldDirectSendTokens(holder, recipient, new BN('0'), data);
|
||||
|
||||
it('reverts when sending a non-zero amount', async function () {
|
||||
await expectRevert.unspecified(this.token.send(recipient, new BN('1'), data, { from: holder }));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777OperatorSend(holder, recipient, operator, data, operatorData) {
|
||||
describe('operator send', function () {
|
||||
context('when the sender has tokens', async function () {
|
||||
shouldOperatorSendTokens(holder, operator, recipient, new BN('0'), data, operatorData);
|
||||
shouldOperatorSendTokens(holder, operator, recipient, new BN('1'), data, operatorData);
|
||||
|
||||
it('reverts when sending more than the balance', async function () {
|
||||
const balance = await this.token.balanceOf(holder);
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorSend(holder, recipient, balance.addn(1), data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
|
||||
it('reverts when sending to the zero address', async function () {
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorSend(holder, ZERO_ADDRESS, new BN('1'), data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
context('when the sender has no tokens', function () {
|
||||
removeBalance(holder);
|
||||
|
||||
shouldOperatorSendTokens(holder, operator, recipient, new BN('0'), data, operatorData);
|
||||
|
||||
it('reverts when sending a non-zero amount', async function () {
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorSend(holder, recipient, new BN('1'), data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
|
||||
it('reverts when sending from the zero address', async function () {
|
||||
// This is not yet reflected in the spec
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorSend(ZERO_ADDRESS, recipient, new BN('0'), data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777UnauthorizedOperatorSend(holder, recipient, operator, data, operatorData) {
|
||||
describe('operator send', function () {
|
||||
it('reverts', async function () {
|
||||
await expectRevert.unspecified(this.token.operatorSend(holder, recipient, new BN('0'), data, operatorData));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777DirectBurn(holder, data) {
|
||||
describe('direct burn', function () {
|
||||
context('when the sender has tokens', function () {
|
||||
shouldDirectBurnTokens(holder, new BN('0'), data);
|
||||
shouldDirectBurnTokens(holder, new BN('1'), data);
|
||||
|
||||
it('reverts when burning more than the balance', async function () {
|
||||
const balance = await this.token.balanceOf(holder);
|
||||
await expectRevert.unspecified(this.token.burn(balance.addn(1), data, { from: holder }));
|
||||
});
|
||||
});
|
||||
|
||||
context('when the sender has no tokens', function () {
|
||||
removeBalance(holder);
|
||||
|
||||
shouldDirectBurnTokens(holder, new BN('0'), data);
|
||||
|
||||
it('reverts when burning a non-zero amount', async function () {
|
||||
await expectRevert.unspecified(this.token.burn(new BN('1'), data, { from: holder }));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777OperatorBurn(holder, operator, data, operatorData) {
|
||||
describe('operator burn', function () {
|
||||
context('when the sender has tokens', async function () {
|
||||
shouldOperatorBurnTokens(holder, operator, new BN('0'), data, operatorData);
|
||||
shouldOperatorBurnTokens(holder, operator, new BN('1'), data, operatorData);
|
||||
|
||||
it('reverts when burning more than the balance', async function () {
|
||||
const balance = await this.token.balanceOf(holder);
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorBurn(holder, balance.addn(1), data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
context('when the sender has no tokens', function () {
|
||||
removeBalance(holder);
|
||||
|
||||
shouldOperatorBurnTokens(holder, operator, new BN('0'), data, operatorData);
|
||||
|
||||
it('reverts when burning a non-zero amount', async function () {
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorBurn(holder, new BN('1'), data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
|
||||
it('reverts when burning from the zero address', async function () {
|
||||
// This is not yet reflected in the spec
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorBurn(ZERO_ADDRESS, new BN('0'), data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777UnauthorizedOperatorBurn(holder, operator, data, operatorData) {
|
||||
describe('operator burn', function () {
|
||||
it('reverts', async function () {
|
||||
await expectRevert.unspecified(this.token.operatorBurn(holder, new BN('0'), data, operatorData));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shouldDirectSendTokens(from, to, amount, data) {
|
||||
shouldSendTokens(from, null, to, amount, data, null);
|
||||
}
|
||||
|
||||
function shouldOperatorSendTokens(from, operator, to, amount, data, operatorData) {
|
||||
shouldSendTokens(from, operator, to, amount, data, operatorData);
|
||||
}
|
||||
|
||||
function shouldSendTokens(from, operator, to, amount, data, operatorData) {
|
||||
const operatorCall = operator !== null;
|
||||
|
||||
it(`${operatorCall ? 'operator ' : ''}can send an amount of ${amount}`, async function () {
|
||||
const initialTotalSupply = await this.token.totalSupply();
|
||||
const initialFromBalance = await this.token.balanceOf(from);
|
||||
const initialToBalance = await this.token.balanceOf(to);
|
||||
|
||||
let receipt;
|
||||
if (!operatorCall) {
|
||||
receipt = await this.token.send(to, amount, data, { from });
|
||||
expectEvent(receipt, 'Sent', {
|
||||
operator: from,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
data,
|
||||
operatorData: null,
|
||||
});
|
||||
} else {
|
||||
receipt = await this.token.operatorSend(from, to, amount, data, operatorData, { from: operator });
|
||||
expectEvent(receipt, 'Sent', {
|
||||
operator,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
});
|
||||
}
|
||||
|
||||
expectEvent(receipt, 'Transfer', {
|
||||
from,
|
||||
to,
|
||||
value: amount,
|
||||
});
|
||||
|
||||
const finalTotalSupply = await this.token.totalSupply();
|
||||
const finalFromBalance = await this.token.balanceOf(from);
|
||||
const finalToBalance = await this.token.balanceOf(to);
|
||||
|
||||
expect(finalTotalSupply).to.be.bignumber.equal(initialTotalSupply);
|
||||
expect(finalToBalance.sub(initialToBalance)).to.be.bignumber.equal(amount);
|
||||
expect(finalFromBalance.sub(initialFromBalance)).to.be.bignumber.equal(amount.neg());
|
||||
});
|
||||
}
|
||||
|
||||
function shouldDirectBurnTokens(from, amount, data) {
|
||||
shouldBurnTokens(from, null, amount, data, null);
|
||||
}
|
||||
|
||||
function shouldOperatorBurnTokens(from, operator, amount, data, operatorData) {
|
||||
shouldBurnTokens(from, operator, amount, data, operatorData);
|
||||
}
|
||||
|
||||
function shouldBurnTokens(from, operator, amount, data, operatorData) {
|
||||
const operatorCall = operator !== null;
|
||||
|
||||
it(`${operatorCall ? 'operator ' : ''}can burn an amount of ${amount}`, async function () {
|
||||
const initialTotalSupply = await this.token.totalSupply();
|
||||
const initialFromBalance = await this.token.balanceOf(from);
|
||||
|
||||
let receipt;
|
||||
if (!operatorCall) {
|
||||
receipt = await this.token.burn(amount, data, { from });
|
||||
expectEvent(receipt, 'Burned', {
|
||||
operator: from,
|
||||
from,
|
||||
amount,
|
||||
data,
|
||||
operatorData: null,
|
||||
});
|
||||
} else {
|
||||
receipt = await this.token.operatorBurn(from, amount, data, operatorData, { from: operator });
|
||||
expectEvent(receipt, 'Burned', {
|
||||
operator,
|
||||
from,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
});
|
||||
}
|
||||
|
||||
expectEvent(receipt, 'Transfer', {
|
||||
from,
|
||||
to: ZERO_ADDRESS,
|
||||
value: amount,
|
||||
});
|
||||
|
||||
const finalTotalSupply = await this.token.totalSupply();
|
||||
const finalFromBalance = await this.token.balanceOf(from);
|
||||
|
||||
expect(finalTotalSupply.sub(initialTotalSupply)).to.be.bignumber.equal(amount.neg());
|
||||
expect(finalFromBalance.sub(initialFromBalance)).to.be.bignumber.equal(amount.neg());
|
||||
});
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777InternalMint(recipient, operator, amount, data, operatorData) {
|
||||
shouldInternalMintTokens(operator, recipient, new BN('0'), data, operatorData);
|
||||
shouldInternalMintTokens(operator, recipient, amount, data, operatorData);
|
||||
|
||||
it('reverts when minting tokens for the zero address', async function () {
|
||||
await expectRevert.unspecified(
|
||||
this.token.$_mint(ZERO_ADDRESS, amount, data, operatorData, true, { from: operator }),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldInternalMintTokens(operator, to, amount, data, operatorData) {
|
||||
it(`can (internal) mint an amount of ${amount}`, async function () {
|
||||
const initialTotalSupply = await this.token.totalSupply();
|
||||
const initialToBalance = await this.token.balanceOf(to);
|
||||
|
||||
const receipt = await this.token.$_mint(to, amount, data, operatorData, true, { from: operator });
|
||||
|
||||
expectEvent(receipt, 'Minted', {
|
||||
operator,
|
||||
to,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
});
|
||||
|
||||
expectEvent(receipt, 'Transfer', {
|
||||
from: ZERO_ADDRESS,
|
||||
to,
|
||||
value: amount,
|
||||
});
|
||||
|
||||
const finalTotalSupply = await this.token.totalSupply();
|
||||
const finalToBalance = await this.token.balanceOf(to);
|
||||
|
||||
expect(finalTotalSupply.sub(initialTotalSupply)).to.be.bignumber.equal(amount);
|
||||
expect(finalToBalance.sub(initialToBalance)).to.be.bignumber.equal(amount);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData) {
|
||||
context('when TokensRecipient reverts', function () {
|
||||
beforeEach(async function () {
|
||||
await this.tokensRecipientImplementer.setShouldRevertReceive(true);
|
||||
});
|
||||
|
||||
it('send reverts', async function () {
|
||||
await expectRevert.unspecified(sendFromHolder(this.token, this.sender, this.recipient, amount, data));
|
||||
});
|
||||
|
||||
it('operatorSend reverts', async function () {
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
|
||||
it('mint (internal) reverts', async function () {
|
||||
await expectRevert.unspecified(
|
||||
this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
context('when TokensRecipient does not revert', function () {
|
||||
beforeEach(async function () {
|
||||
await this.tokensRecipientImplementer.setShouldRevertSend(false);
|
||||
});
|
||||
|
||||
it('TokensRecipient receives send data and is called after state mutation', async function () {
|
||||
const { tx } = await sendFromHolder(this.token, this.sender, this.recipient, amount, data);
|
||||
|
||||
const postSenderBalance = await this.token.balanceOf(this.sender);
|
||||
const postRecipientBalance = await this.token.balanceOf(this.recipient);
|
||||
|
||||
await assertTokensReceivedCalled(
|
||||
this.token,
|
||||
tx,
|
||||
this.sender,
|
||||
this.sender,
|
||||
this.recipient,
|
||||
amount,
|
||||
data,
|
||||
null,
|
||||
postSenderBalance,
|
||||
postRecipientBalance,
|
||||
);
|
||||
});
|
||||
|
||||
it('TokensRecipient receives operatorSend data and is called after state mutation', async function () {
|
||||
const { tx } = await this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, {
|
||||
from: operator,
|
||||
});
|
||||
|
||||
const postSenderBalance = await this.token.balanceOf(this.sender);
|
||||
const postRecipientBalance = await this.token.balanceOf(this.recipient);
|
||||
|
||||
await assertTokensReceivedCalled(
|
||||
this.token,
|
||||
tx,
|
||||
operator,
|
||||
this.sender,
|
||||
this.recipient,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
postSenderBalance,
|
||||
postRecipientBalance,
|
||||
);
|
||||
});
|
||||
|
||||
it('TokensRecipient receives mint (internal) data and is called after state mutation', async function () {
|
||||
const { tx } = await this.token.$_mint(this.recipient, amount, data, operatorData, true, { from: operator });
|
||||
|
||||
const postRecipientBalance = await this.token.balanceOf(this.recipient);
|
||||
|
||||
await assertTokensReceivedCalled(
|
||||
this.token,
|
||||
tx,
|
||||
operator,
|
||||
ZERO_ADDRESS,
|
||||
this.recipient,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
new BN('0'),
|
||||
postRecipientBalance,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData) {
|
||||
context('when TokensSender reverts', function () {
|
||||
beforeEach(async function () {
|
||||
await this.tokensSenderImplementer.setShouldRevertSend(true);
|
||||
});
|
||||
|
||||
it('send reverts', async function () {
|
||||
await expectRevert.unspecified(sendFromHolder(this.token, this.sender, this.recipient, amount, data));
|
||||
});
|
||||
|
||||
it('operatorSend reverts', async function () {
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
|
||||
it('burn reverts', async function () {
|
||||
await expectRevert.unspecified(burnFromHolder(this.token, this.sender, amount, data));
|
||||
});
|
||||
|
||||
it('operatorBurn reverts', async function () {
|
||||
await expectRevert.unspecified(
|
||||
this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
context('when TokensSender does not revert', function () {
|
||||
beforeEach(async function () {
|
||||
await this.tokensSenderImplementer.setShouldRevertSend(false);
|
||||
});
|
||||
|
||||
it('TokensSender receives send data and is called before state mutation', async function () {
|
||||
const preSenderBalance = await this.token.balanceOf(this.sender);
|
||||
const preRecipientBalance = await this.token.balanceOf(this.recipient);
|
||||
|
||||
const { tx } = await sendFromHolder(this.token, this.sender, this.recipient, amount, data);
|
||||
|
||||
await assertTokensToSendCalled(
|
||||
this.token,
|
||||
tx,
|
||||
this.sender,
|
||||
this.sender,
|
||||
this.recipient,
|
||||
amount,
|
||||
data,
|
||||
null,
|
||||
preSenderBalance,
|
||||
preRecipientBalance,
|
||||
);
|
||||
});
|
||||
|
||||
it('TokensSender receives operatorSend data and is called before state mutation', async function () {
|
||||
const preSenderBalance = await this.token.balanceOf(this.sender);
|
||||
const preRecipientBalance = await this.token.balanceOf(this.recipient);
|
||||
|
||||
const { tx } = await this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, {
|
||||
from: operator,
|
||||
});
|
||||
|
||||
await assertTokensToSendCalled(
|
||||
this.token,
|
||||
tx,
|
||||
operator,
|
||||
this.sender,
|
||||
this.recipient,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
preSenderBalance,
|
||||
preRecipientBalance,
|
||||
);
|
||||
});
|
||||
|
||||
it('TokensSender receives burn data and is called before state mutation', async function () {
|
||||
const preSenderBalance = await this.token.balanceOf(this.sender);
|
||||
|
||||
const { tx } = await burnFromHolder(this.token, this.sender, amount, data, { from: this.sender });
|
||||
|
||||
await assertTokensToSendCalled(
|
||||
this.token,
|
||||
tx,
|
||||
this.sender,
|
||||
this.sender,
|
||||
ZERO_ADDRESS,
|
||||
amount,
|
||||
data,
|
||||
null,
|
||||
preSenderBalance,
|
||||
);
|
||||
});
|
||||
|
||||
it('TokensSender receives operatorBurn data and is called before state mutation', async function () {
|
||||
const preSenderBalance = await this.token.balanceOf(this.sender);
|
||||
|
||||
const { tx } = await this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator });
|
||||
|
||||
await assertTokensToSendCalled(
|
||||
this.token,
|
||||
tx,
|
||||
operator,
|
||||
this.sender,
|
||||
ZERO_ADDRESS,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
preSenderBalance,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeBalance(holder) {
|
||||
beforeEach(async function () {
|
||||
await this.token.burn(await this.token.balanceOf(holder), '0x', { from: holder });
|
||||
expect(await this.token.balanceOf(holder)).to.be.bignumber.equal('0');
|
||||
});
|
||||
}
|
||||
|
||||
async function assertTokensReceivedCalled(
|
||||
token,
|
||||
txHash,
|
||||
operator,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
fromBalance,
|
||||
toBalance = '0',
|
||||
) {
|
||||
await expectEvent.inTransaction(txHash, ERC777SenderRecipientMock, 'TokensReceivedCalled', {
|
||||
operator,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
token: token.address,
|
||||
fromBalance,
|
||||
toBalance,
|
||||
});
|
||||
}
|
||||
|
||||
async function assertTokensToSendCalled(
|
||||
token,
|
||||
txHash,
|
||||
operator,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
fromBalance,
|
||||
toBalance = '0',
|
||||
) {
|
||||
await expectEvent.inTransaction(txHash, ERC777SenderRecipientMock, 'TokensToSendCalled', {
|
||||
operator,
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
data,
|
||||
operatorData,
|
||||
token: token.address,
|
||||
fromBalance,
|
||||
toBalance,
|
||||
});
|
||||
}
|
||||
|
||||
async function sendFromHolder(token, holder, to, amount, data) {
|
||||
if ((await web3.eth.getCode(holder)).length <= '0x'.length) {
|
||||
return token.send(to, amount, data, { from: holder });
|
||||
} else {
|
||||
// assume holder is ERC777SenderRecipientMock contract
|
||||
return (await ERC777SenderRecipientMock.at(holder)).send(token.address, to, amount, data);
|
||||
}
|
||||
}
|
||||
|
||||
async function burnFromHolder(token, holder, amount, data) {
|
||||
if ((await web3.eth.getCode(holder)).length <= '0x'.length) {
|
||||
return token.burn(amount, data, { from: holder });
|
||||
} else {
|
||||
// assume holder is ERC777SenderRecipientMock contract
|
||||
return (await ERC777SenderRecipientMock.at(holder)).burn(token.address, amount, data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
shouldBehaveLikeERC777DirectSendBurn,
|
||||
shouldBehaveLikeERC777OperatorSendBurn,
|
||||
shouldBehaveLikeERC777UnauthorizedOperatorSendBurn,
|
||||
shouldBehaveLikeERC777InternalMint,
|
||||
shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook,
|
||||
shouldBehaveLikeERC777SendBurnWithSendHook,
|
||||
};
|
||||
Reference in New Issue
Block a user