* Initial ERC1155 implementation with some tests (#1803) * Initial ERC1155 implementation with some tests * Remove mocked isERC1155TokenReceiver * Revert reason edit nit * Remove parameters associated with isERC1155TokenReceiver call * Add tests for approvals and single transfers * Add tests for transferring to contracts * Add tests for batch transfers * Make expectEvent.inTransaction tests async * Renamed "owner" to "account" and "holder" * Document unspecified balanceOfBatch reversion on zero behavior * Ensure accounts can't set their own operator status * Specify descriptive messages for underflow errors * Bring SafeMath.add calls in line with OZ style * Explicitly prevent _burn on the zero account * Implement batch minting/burning * Refactored operator approval check into isApprovedForAll calls * Renamed ERC1155TokenReceiver to ERC1155Receiver * Added ERC1155Holder * Fix lint issues * Migrate tests to @openzeppelin/test-environment * Port ERC 1155 branch to Solidity 0.6 (and current master) (#2130) * port ERC1155 to Solidity 0.6 * make ERC1155 constructor more similar to ERC721 one * also migrate mock contracts to Solidity 0.6 * mark all non-view functions as virtual Co-authored-by: Alan Lu <alanlu1023@gmail.com> Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com> Co-authored-by: Robert Kaiser <kairo@kairo.at>
220 lines
7.2 KiB
JavaScript
220 lines
7.2 KiB
JavaScript
const { accounts, contract } = require('@openzeppelin/test-environment');
|
|
|
|
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
|
|
const { ZERO_ADDRESS } = constants;
|
|
|
|
const { expect } = require('chai');
|
|
|
|
const { shouldBehaveLikeERC1155 } = require('./ERC1155.behavior');
|
|
const ERC1155Mock = contract.fromArtifact('ERC1155Mock');
|
|
|
|
describe('ERC1155', function () {
|
|
const [creator, tokenHolder, tokenBatchHolder, ...otherAccounts] = accounts;
|
|
|
|
beforeEach(async function () {
|
|
this.token = await ERC1155Mock.new({ from: creator });
|
|
});
|
|
|
|
shouldBehaveLikeERC1155(otherAccounts);
|
|
|
|
describe('internal functions', function () {
|
|
const tokenId = new BN(1990);
|
|
const mintAmount = new BN(9001);
|
|
const burnAmount = new BN(3000);
|
|
|
|
const tokenBatchIds = [new BN(2000), new BN(2010), new BN(2020)];
|
|
const mintAmounts = [new BN(5000), new BN(10000), new BN(42195)];
|
|
const burnAmounts = [new BN(5000), new BN(9001), new BN(195)];
|
|
|
|
const data = '0xcafebabe';
|
|
|
|
describe('_mint(address, uint256, uint256, bytes memory)', function () {
|
|
it('reverts with a null destination address', async function () {
|
|
await expectRevert(
|
|
this.token.mint(ZERO_ADDRESS, tokenId, mintAmount, data),
|
|
'ERC1155: mint to the zero address'
|
|
);
|
|
});
|
|
|
|
context('with minted tokens', function () {
|
|
beforeEach(async function () {
|
|
({ logs: this.logs } = await this.token.mint(
|
|
tokenHolder,
|
|
tokenId,
|
|
mintAmount,
|
|
data,
|
|
{ from: creator }
|
|
));
|
|
});
|
|
|
|
it('emits a TransferSingle event', function () {
|
|
expectEvent.inLogs(this.logs, 'TransferSingle', {
|
|
operator: creator,
|
|
from: ZERO_ADDRESS,
|
|
to: tokenHolder,
|
|
id: tokenId,
|
|
value: mintAmount,
|
|
});
|
|
});
|
|
|
|
it('credits the minted amount of tokens', async function () {
|
|
expect(await this.token.balanceOf(
|
|
tokenHolder,
|
|
tokenId
|
|
)).to.be.bignumber.equal(mintAmount);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('_mintBatch(address, uint256[] memory, uint256[] memory, bytes memory)', function () {
|
|
it('reverts with a null destination address', async function () {
|
|
await expectRevert(
|
|
this.token.mintBatch(ZERO_ADDRESS, tokenBatchIds, mintAmounts, data),
|
|
'ERC1155: batch mint to the zero address'
|
|
);
|
|
});
|
|
|
|
it('reverts if length of inputs do not match', async function () {
|
|
await expectRevert(
|
|
this.token.mintBatch(tokenBatchHolder, tokenBatchIds, mintAmounts.slice(1), data),
|
|
'ERC1155: minted IDs and values must have same lengths'
|
|
);
|
|
});
|
|
|
|
context('with minted batch of tokens', function () {
|
|
beforeEach(async function () {
|
|
({ logs: this.logs } = await this.token.mintBatch(
|
|
tokenBatchHolder,
|
|
tokenBatchIds,
|
|
mintAmounts,
|
|
data,
|
|
{ from: creator }
|
|
));
|
|
});
|
|
|
|
it('emits a TransferBatch event', function () {
|
|
expectEvent.inLogs(this.logs, 'TransferBatch', {
|
|
operator: creator,
|
|
from: ZERO_ADDRESS,
|
|
to: tokenBatchHolder,
|
|
// ids: tokenBatchIds,
|
|
// values: mintAmounts,
|
|
});
|
|
});
|
|
|
|
it('credits the minted batch of tokens', async function () {
|
|
const holderBatchBalances = await this.token.balanceOfBatch(
|
|
new Array(tokenBatchIds.length).fill(tokenBatchHolder),
|
|
tokenBatchIds
|
|
);
|
|
|
|
for (let i = 0; i < holderBatchBalances.length; i++) {
|
|
expect(holderBatchBalances[i]).to.be.bignumber.equal(mintAmounts[i]);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('_burn(address, uint256, uint256)', function () {
|
|
it('reverts when burning the zero account\'s tokens', async function () {
|
|
await expectRevert(
|
|
this.token.burn(ZERO_ADDRESS, tokenId, mintAmount),
|
|
'ERC1155: attempting to burn tokens on zero account'
|
|
);
|
|
});
|
|
|
|
it('reverts when burning a non-existent token id', async function () {
|
|
await expectRevert(
|
|
this.token.burn(tokenHolder, tokenId, mintAmount),
|
|
'ERC1155: attempting to burn more than balance'
|
|
);
|
|
});
|
|
|
|
context('with minted-then-burnt tokens', function () {
|
|
beforeEach(async function () {
|
|
await this.token.mint(tokenHolder, tokenId, mintAmount, data);
|
|
({ logs: this.logs } = await this.token.burn(
|
|
tokenHolder,
|
|
tokenId,
|
|
burnAmount,
|
|
{ from: creator }
|
|
));
|
|
});
|
|
|
|
it('emits a TransferSingle event', function () {
|
|
expectEvent.inLogs(this.logs, 'TransferSingle', {
|
|
operator: creator,
|
|
from: tokenHolder,
|
|
to: ZERO_ADDRESS,
|
|
id: tokenId,
|
|
value: burnAmount,
|
|
});
|
|
});
|
|
|
|
it('accounts for both minting and burning', async function () {
|
|
expect(await this.token.balanceOf(
|
|
tokenHolder,
|
|
tokenId
|
|
)).to.be.bignumber.equal(mintAmount.sub(burnAmount));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('_burnBatch(address, uint256[] memory, uint256[] memory)', function () {
|
|
it('reverts when burning the zero account\'s tokens', async function () {
|
|
await expectRevert(
|
|
this.token.burnBatch(ZERO_ADDRESS, tokenBatchIds, burnAmounts),
|
|
'ERC1155: attempting to burn batch of tokens on zero account'
|
|
);
|
|
});
|
|
|
|
it('reverts if length of inputs do not match', async function () {
|
|
await expectRevert(
|
|
this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts.slice(1)),
|
|
'ERC1155: burnt IDs and values must have same lengths'
|
|
);
|
|
});
|
|
|
|
it('reverts when burning a non-existent token id', async function () {
|
|
await expectRevert(
|
|
this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts),
|
|
'ERC1155: attempting to burn more than balance for some token'
|
|
);
|
|
});
|
|
|
|
context('with minted-then-burnt tokens', function () {
|
|
beforeEach(async function () {
|
|
await this.token.mintBatch(tokenBatchHolder, tokenBatchIds, mintAmounts, data);
|
|
({ logs: this.logs } = await this.token.burnBatch(
|
|
tokenBatchHolder,
|
|
tokenBatchIds,
|
|
burnAmounts,
|
|
{ from: creator }
|
|
));
|
|
});
|
|
|
|
it('emits a TransferBatch event', function () {
|
|
expectEvent.inLogs(this.logs, 'TransferBatch', {
|
|
operator: creator,
|
|
from: tokenBatchHolder,
|
|
to: ZERO_ADDRESS,
|
|
// ids: tokenBatchIds,
|
|
// values: burnAmounts,
|
|
});
|
|
});
|
|
|
|
it('accounts for both minting and burning', async function () {
|
|
const holderBatchBalances = await this.token.balanceOfBatch(
|
|
new Array(tokenBatchIds.length).fill(tokenBatchHolder),
|
|
tokenBatchIds
|
|
);
|
|
|
|
for (let i = 0; i < holderBatchBalances.length; i++) {
|
|
expect(holderBatchBalances[i]).to.be.bignumber.equal(mintAmounts[i].sub(burnAmounts[i]));
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|