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.
763 lines
28 KiB
763 lines
28 KiB
const { ethers } = require('hardhat');
|
|
const { expect } = require('chai');
|
|
const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
|
|
|
|
const { RevertType } = require('../../helpers/enums');
|
|
const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior');
|
|
|
|
function shouldBehaveLikeERC1155() {
|
|
const firstTokenId = 1n;
|
|
const secondTokenId = 2n;
|
|
const unknownTokenId = 3n;
|
|
|
|
const firstTokenValue = 1000n;
|
|
const secondTokenValue = 2000n;
|
|
|
|
const RECEIVER_SINGLE_MAGIC_VALUE = '0xf23a6e61';
|
|
const RECEIVER_BATCH_MAGIC_VALUE = '0xbc197c81';
|
|
|
|
beforeEach(async function () {
|
|
[this.recipient, this.proxy, this.alice, this.bruce] = this.otherAccounts;
|
|
});
|
|
|
|
describe('like an ERC1155', function () {
|
|
describe('balanceOf', function () {
|
|
it('should return 0 when queried about the zero address', async function () {
|
|
expect(await this.token.balanceOf(ethers.ZeroAddress, firstTokenId)).to.equal(0n);
|
|
});
|
|
|
|
describe("when accounts don't own tokens", function () {
|
|
it('returns zero for given addresses', async function () {
|
|
expect(await this.token.balanceOf(this.alice, firstTokenId)).to.equal(0n);
|
|
expect(await this.token.balanceOf(this.bruce, secondTokenId)).to.equal(0n);
|
|
expect(await this.token.balanceOf(this.alice, unknownTokenId)).to.equal(0n);
|
|
});
|
|
});
|
|
|
|
describe('when accounts own some tokens', function () {
|
|
beforeEach(async function () {
|
|
await this.token.$_mint(this.alice, firstTokenId, firstTokenValue, '0x');
|
|
await this.token.$_mint(this.bruce, secondTokenId, secondTokenValue, '0x');
|
|
});
|
|
|
|
it('returns the amount of tokens owned by the given addresses', async function () {
|
|
expect(await this.token.balanceOf(this.alice, firstTokenId)).to.equal(firstTokenValue);
|
|
expect(await this.token.balanceOf(this.bruce, secondTokenId)).to.equal(secondTokenValue);
|
|
expect(await this.token.balanceOf(this.alice, unknownTokenId)).to.equal(0n);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('balanceOfBatch', function () {
|
|
it("reverts when input arrays don't match up", async function () {
|
|
const accounts1 = [this.alice, this.bruce, this.alice, this.bruce];
|
|
const ids1 = [firstTokenId, secondTokenId, unknownTokenId];
|
|
|
|
await expect(this.token.balanceOfBatch(accounts1, ids1))
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
|
|
.withArgs(ids1.length, accounts1.length);
|
|
|
|
const accounts2 = [this.alice, this.bruce];
|
|
const ids2 = [firstTokenId, secondTokenId, unknownTokenId];
|
|
await expect(this.token.balanceOfBatch(accounts2, ids2))
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
|
|
.withArgs(ids2.length, accounts2.length);
|
|
});
|
|
|
|
it('should return 0 as the balance when one of the addresses is the zero address', async function () {
|
|
const result = await this.token.balanceOfBatch(
|
|
[this.alice, this.bruce, ethers.ZeroAddress],
|
|
[firstTokenId, secondTokenId, unknownTokenId],
|
|
);
|
|
expect(result).to.deep.equal([0n, 0n, 0n]);
|
|
});
|
|
|
|
describe("when accounts don't own tokens", function () {
|
|
it('returns zeros for each account', async function () {
|
|
const result = await this.token.balanceOfBatch(
|
|
[this.alice, this.bruce, this.alice],
|
|
[firstTokenId, secondTokenId, unknownTokenId],
|
|
);
|
|
expect(result).to.deep.equal([0n, 0n, 0n]);
|
|
});
|
|
});
|
|
|
|
describe('when accounts own some tokens', function () {
|
|
beforeEach(async function () {
|
|
await this.token.$_mint(this.alice, firstTokenId, firstTokenValue, '0x');
|
|
await this.token.$_mint(this.bruce, secondTokenId, secondTokenValue, '0x');
|
|
});
|
|
|
|
it('returns amounts owned by each account in order passed', async function () {
|
|
const result = await this.token.balanceOfBatch(
|
|
[this.bruce, this.alice, this.alice],
|
|
[secondTokenId, firstTokenId, unknownTokenId],
|
|
);
|
|
expect(result).to.deep.equal([secondTokenValue, firstTokenValue, 0n]);
|
|
});
|
|
|
|
it('returns multiple times the balance of the same address when asked', async function () {
|
|
const result = await this.token.balanceOfBatch(
|
|
[this.alice, this.bruce, this.alice],
|
|
[firstTokenId, secondTokenId, firstTokenId],
|
|
);
|
|
expect(result).to.deep.equal([firstTokenValue, secondTokenValue, firstTokenValue]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('setApprovalForAll', function () {
|
|
beforeEach(async function () {
|
|
this.tx = await this.token.connect(this.holder).setApprovalForAll(this.proxy, true);
|
|
});
|
|
|
|
it('sets approval status which can be queried via isApprovedForAll', async function () {
|
|
expect(await this.token.isApprovedForAll(this.holder, this.proxy)).to.be.true;
|
|
});
|
|
|
|
it('emits an ApprovalForAll log', async function () {
|
|
await expect(this.tx).to.emit(this.token, 'ApprovalForAll').withArgs(this.holder, this.proxy, true);
|
|
});
|
|
|
|
it('can unset approval for an operator', async function () {
|
|
await this.token.connect(this.holder).setApprovalForAll(this.proxy, false);
|
|
expect(await this.token.isApprovedForAll(this.holder, this.proxy)).to.be.false;
|
|
});
|
|
|
|
it('reverts if attempting to approve zero address as an operator', async function () {
|
|
await expect(this.token.connect(this.holder).setApprovalForAll(ethers.ZeroAddress, true))
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidOperator')
|
|
.withArgs(ethers.ZeroAddress);
|
|
});
|
|
});
|
|
|
|
describe('safeTransferFrom', function () {
|
|
beforeEach(async function () {
|
|
await this.token.$_mint(this.holder, firstTokenId, firstTokenValue, '0x');
|
|
await this.token.$_mint(this.holder, secondTokenId, secondTokenValue, '0x');
|
|
});
|
|
|
|
it('reverts when transferring more than balance', async function () {
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeTransferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue + 1n, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
|
|
.withArgs(this.holder, firstTokenValue, firstTokenValue + 1n, firstTokenId);
|
|
});
|
|
|
|
it('reverts when transferring to zero address', async function () {
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeTransferFrom(this.holder, ethers.ZeroAddress, firstTokenId, firstTokenValue, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
|
|
.withArgs(ethers.ZeroAddress);
|
|
});
|
|
|
|
function transferWasSuccessful() {
|
|
it('debits transferred balance from sender', async function () {
|
|
expect(await this.token.balanceOf(this.args.from, this.args.id)).to.equal(0n);
|
|
});
|
|
|
|
it('credits transferred balance to receiver', async function () {
|
|
expect(await this.token.balanceOf(this.args.to, this.args.id)).to.equal(this.args.value);
|
|
});
|
|
|
|
it('emits a TransferSingle log', async function () {
|
|
await expect(this.tx)
|
|
.to.emit(this.token, 'TransferSingle')
|
|
.withArgs(this.args.operator, this.args.from, this.args.to, this.args.id, this.args.value);
|
|
});
|
|
}
|
|
|
|
describe('when called by the holder', async function () {
|
|
beforeEach(async function () {
|
|
this.args = {
|
|
operator: this.holder,
|
|
from: this.holder,
|
|
to: this.recipient,
|
|
id: firstTokenId,
|
|
value: firstTokenValue,
|
|
data: '0x',
|
|
};
|
|
this.tx = await this.token
|
|
.connect(this.args.operator)
|
|
.safeTransferFrom(this.args.from, this.args.to, this.args.id, this.args.value, this.args.data);
|
|
});
|
|
|
|
transferWasSuccessful();
|
|
|
|
it('preserves existing balances which are not transferred by holder', async function () {
|
|
expect(await this.token.balanceOf(this.holder, secondTokenId)).to.equal(secondTokenValue);
|
|
expect(await this.token.balanceOf(this.recipient, secondTokenId)).to.equal(0n);
|
|
});
|
|
});
|
|
|
|
describe('when called by an operator on behalf of the holder', function () {
|
|
describe('when operator is not approved by holder', function () {
|
|
beforeEach(async function () {
|
|
await this.token.connect(this.holder).setApprovalForAll(this.proxy, false);
|
|
});
|
|
|
|
it('reverts', async function () {
|
|
await expect(
|
|
this.token
|
|
.connect(this.proxy)
|
|
.safeTransferFrom(this.holder, this.recipient, firstTokenId, firstTokenValue, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
|
|
.withArgs(this.proxy, this.holder);
|
|
});
|
|
});
|
|
|
|
describe('when operator is approved by holder', function () {
|
|
beforeEach(async function () {
|
|
await this.token.connect(this.holder).setApprovalForAll(this.proxy, true);
|
|
|
|
this.args = {
|
|
operator: this.proxy,
|
|
from: this.holder,
|
|
to: this.recipient,
|
|
id: firstTokenId,
|
|
value: firstTokenValue,
|
|
data: '0x',
|
|
};
|
|
this.tx = await this.token
|
|
.connect(this.args.operator)
|
|
.safeTransferFrom(this.args.from, this.args.to, this.args.id, this.args.value, this.args.data);
|
|
});
|
|
|
|
transferWasSuccessful();
|
|
|
|
it("preserves operator's balances not involved in the transfer", async function () {
|
|
expect(await this.token.balanceOf(this.proxy, firstTokenId)).to.equal(0n);
|
|
expect(await this.token.balanceOf(this.proxy, secondTokenId)).to.equal(0n);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when sending to a valid receiver', function () {
|
|
beforeEach(async function () {
|
|
this.receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.None,
|
|
]);
|
|
});
|
|
|
|
describe('without data', function () {
|
|
beforeEach(async function () {
|
|
this.args = {
|
|
operator: this.holder,
|
|
from: this.holder,
|
|
to: this.receiver,
|
|
id: firstTokenId,
|
|
value: firstTokenValue,
|
|
data: '0x',
|
|
};
|
|
this.tx = await this.token
|
|
.connect(this.args.operator)
|
|
.safeTransferFrom(this.args.from, this.args.to, this.args.id, this.args.value, this.args.data);
|
|
});
|
|
|
|
transferWasSuccessful();
|
|
|
|
it('calls onERC1155Received', async function () {
|
|
await expect(this.tx)
|
|
.to.emit(this.receiver, 'Received')
|
|
.withArgs(this.args.operator, this.args.from, this.args.id, this.args.value, this.args.data, anyValue);
|
|
});
|
|
});
|
|
|
|
describe('with data', function () {
|
|
beforeEach(async function () {
|
|
this.args = {
|
|
operator: this.holder,
|
|
from: this.holder,
|
|
to: this.receiver,
|
|
id: firstTokenId,
|
|
value: firstTokenValue,
|
|
data: '0xf00dd00d',
|
|
};
|
|
this.tx = await this.token
|
|
.connect(this.args.operator)
|
|
.safeTransferFrom(this.args.from, this.args.to, this.args.id, this.args.value, this.args.data);
|
|
});
|
|
|
|
transferWasSuccessful();
|
|
|
|
it('calls onERC1155Received', async function () {
|
|
await expect(this.tx)
|
|
.to.emit(this.receiver, 'Received')
|
|
.withArgs(this.args.operator, this.args.from, this.args.id, this.args.value, this.args.data, anyValue);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('to a receiver contract returning unexpected value', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
'0x00c0ffee',
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.None,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
|
|
.withArgs(receiver);
|
|
});
|
|
});
|
|
|
|
describe('to a receiver contract that reverts', function () {
|
|
describe('with a revert string', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.RevertWithMessage,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
|
|
).to.be.revertedWith('ERC1155ReceiverMock: reverting on receive');
|
|
});
|
|
});
|
|
|
|
describe('without a revert string', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.RevertWithoutMessage,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
|
|
.withArgs(receiver);
|
|
});
|
|
});
|
|
|
|
describe('with a custom error', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.RevertWithCustomError,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(receiver, 'CustomError')
|
|
.withArgs(RECEIVER_SINGLE_MAGIC_VALUE);
|
|
});
|
|
});
|
|
|
|
describe('with a panic', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.Panic,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeTransferFrom(this.holder, receiver, firstTokenId, firstTokenValue, '0x'),
|
|
).to.be.revertedWithPanic();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('to a contract that does not implement the required function', function () {
|
|
it('reverts', async function () {
|
|
const invalidReceiver = this.token;
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeTransferFrom(this.holder, invalidReceiver, firstTokenId, firstTokenValue, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
|
|
.withArgs(invalidReceiver);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('safeBatchTransferFrom', function () {
|
|
beforeEach(async function () {
|
|
await this.token.$_mint(this.holder, firstTokenId, firstTokenValue, '0x');
|
|
await this.token.$_mint(this.holder, secondTokenId, secondTokenValue, '0x');
|
|
});
|
|
|
|
it('reverts when transferring value more than any of balances', async function () {
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
this.recipient,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue + 1n],
|
|
'0x',
|
|
),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InsufficientBalance')
|
|
.withArgs(this.holder, secondTokenValue, secondTokenValue + 1n, secondTokenId);
|
|
});
|
|
|
|
it("reverts when ids array length doesn't match values array length", async function () {
|
|
const ids1 = [firstTokenId];
|
|
const tokenValues1 = [firstTokenValue, secondTokenValue];
|
|
|
|
await expect(
|
|
this.token.connect(this.holder).safeBatchTransferFrom(this.holder, this.recipient, ids1, tokenValues1, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
|
|
.withArgs(ids1.length, tokenValues1.length);
|
|
|
|
const ids2 = [firstTokenId, secondTokenId];
|
|
const tokenValues2 = [firstTokenValue];
|
|
|
|
await expect(
|
|
this.token.connect(this.holder).safeBatchTransferFrom(this.holder, this.recipient, ids2, tokenValues2, '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidArrayLength')
|
|
.withArgs(ids2.length, tokenValues2.length);
|
|
});
|
|
|
|
it('reverts when transferring to zero address', async function () {
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
ethers.ZeroAddress,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue],
|
|
'0x',
|
|
),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
|
|
.withArgs(ethers.ZeroAddress);
|
|
});
|
|
|
|
it('reverts when transferring from zero address', async function () {
|
|
await expect(
|
|
this.token.$_safeBatchTransferFrom(ethers.ZeroAddress, this.holder, [firstTokenId], [firstTokenValue], '0x'),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidSender')
|
|
.withArgs(ethers.ZeroAddress);
|
|
});
|
|
|
|
function batchTransferWasSuccessful() {
|
|
it('debits transferred balances from sender', async function () {
|
|
const newBalances = await this.token.balanceOfBatch(
|
|
this.args.ids.map(() => this.args.from),
|
|
this.args.ids,
|
|
);
|
|
expect(newBalances).to.deep.equal(this.args.ids.map(() => 0n));
|
|
});
|
|
|
|
it('credits transferred balances to receiver', async function () {
|
|
const newBalances = await this.token.balanceOfBatch(
|
|
this.args.ids.map(() => this.args.to),
|
|
this.args.ids,
|
|
);
|
|
expect(newBalances).to.deep.equal(this.args.values);
|
|
});
|
|
|
|
it('emits a TransferBatch log', async function () {
|
|
await expect(this.tx)
|
|
.to.emit(this.token, 'TransferBatch')
|
|
.withArgs(this.args.operator, this.args.from, this.args.to, this.args.ids, this.args.values);
|
|
});
|
|
}
|
|
|
|
describe('when called by the holder', async function () {
|
|
beforeEach(async function () {
|
|
this.args = {
|
|
operator: this.holder,
|
|
from: this.holder,
|
|
to: this.recipient,
|
|
ids: [firstTokenId, secondTokenId],
|
|
values: [firstTokenValue, secondTokenValue],
|
|
data: '0x',
|
|
};
|
|
this.tx = await this.token
|
|
.connect(this.args.operator)
|
|
.safeBatchTransferFrom(this.args.from, this.args.to, this.args.ids, this.args.values, this.args.data);
|
|
});
|
|
|
|
batchTransferWasSuccessful();
|
|
});
|
|
|
|
describe('when called by an operator on behalf of the holder', function () {
|
|
describe('when operator is not approved by holder', function () {
|
|
beforeEach(async function () {
|
|
await this.token.connect(this.holder).setApprovalForAll(this.proxy, false);
|
|
});
|
|
|
|
it('reverts', async function () {
|
|
await expect(
|
|
this.token
|
|
.connect(this.proxy)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
this.recipient,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue],
|
|
'0x',
|
|
),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155MissingApprovalForAll')
|
|
.withArgs(this.proxy, this.holder);
|
|
});
|
|
});
|
|
|
|
describe('when operator is approved by holder', function () {
|
|
beforeEach(async function () {
|
|
await this.token.connect(this.holder).setApprovalForAll(this.proxy, true);
|
|
|
|
this.args = {
|
|
operator: this.proxy,
|
|
from: this.holder,
|
|
to: this.recipient,
|
|
ids: [firstTokenId, secondTokenId],
|
|
values: [firstTokenValue, secondTokenValue],
|
|
data: '0x',
|
|
};
|
|
this.tx = await this.token
|
|
.connect(this.args.operator)
|
|
.safeBatchTransferFrom(this.args.from, this.args.to, this.args.ids, this.args.values, this.args.data);
|
|
});
|
|
|
|
batchTransferWasSuccessful();
|
|
|
|
it("preserves operator's balances not involved in the transfer", async function () {
|
|
expect(await this.token.balanceOf(this.proxy, firstTokenId)).to.equal(0n);
|
|
expect(await this.token.balanceOf(this.proxy, secondTokenId)).to.equal(0n);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when sending to a valid receiver', function () {
|
|
beforeEach(async function () {
|
|
this.receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.None,
|
|
]);
|
|
});
|
|
|
|
describe('without data', function () {
|
|
beforeEach(async function () {
|
|
this.args = {
|
|
operator: this.holder,
|
|
from: this.holder,
|
|
to: this.receiver,
|
|
ids: [firstTokenId, secondTokenId],
|
|
values: [firstTokenValue, secondTokenValue],
|
|
data: '0x',
|
|
};
|
|
this.tx = await this.token
|
|
.connect(this.args.operator)
|
|
.safeBatchTransferFrom(this.args.from, this.args.to, this.args.ids, this.args.values, this.args.data);
|
|
});
|
|
|
|
batchTransferWasSuccessful();
|
|
|
|
it('calls onERC1155BatchReceived', async function () {
|
|
await expect(this.tx)
|
|
.to.emit(this.receiver, 'BatchReceived')
|
|
.withArgs(this.holder, this.holder, this.args.ids, this.args.values, this.args.data, anyValue);
|
|
});
|
|
});
|
|
|
|
describe('with data', function () {
|
|
beforeEach(async function () {
|
|
this.args = {
|
|
operator: this.holder,
|
|
from: this.holder,
|
|
to: this.receiver,
|
|
ids: [firstTokenId, secondTokenId],
|
|
values: [firstTokenValue, secondTokenValue],
|
|
data: '0xf00dd00d',
|
|
};
|
|
this.tx = await this.token
|
|
.connect(this.args.operator)
|
|
.safeBatchTransferFrom(this.args.from, this.args.to, this.args.ids, this.args.values, this.args.data);
|
|
});
|
|
|
|
batchTransferWasSuccessful();
|
|
|
|
it('calls onERC1155Received', async function () {
|
|
await expect(this.tx)
|
|
.to.emit(this.receiver, 'BatchReceived')
|
|
.withArgs(this.holder, this.holder, this.args.ids, this.args.values, this.args.data, anyValue);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('to a receiver contract returning unexpected value', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RevertType.None,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
receiver,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue],
|
|
'0x',
|
|
),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
|
|
.withArgs(receiver);
|
|
});
|
|
});
|
|
|
|
describe('to a receiver contract that reverts', function () {
|
|
describe('with a revert string', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.RevertWithMessage,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
receiver,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue],
|
|
'0x',
|
|
),
|
|
).to.be.revertedWith('ERC1155ReceiverMock: reverting on batch receive');
|
|
});
|
|
});
|
|
|
|
describe('without a revert string', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.RevertWithoutMessage,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
receiver,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue],
|
|
'0x',
|
|
),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
|
|
.withArgs(receiver);
|
|
});
|
|
});
|
|
|
|
describe('with a custom error', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.RevertWithCustomError,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
receiver,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue],
|
|
'0x',
|
|
),
|
|
)
|
|
.to.be.revertedWithCustomError(receiver, 'CustomError')
|
|
.withArgs(RECEIVER_SINGLE_MAGIC_VALUE);
|
|
});
|
|
});
|
|
|
|
describe('with a panic', function () {
|
|
it('reverts', async function () {
|
|
const receiver = await ethers.deployContract('$ERC1155ReceiverMock', [
|
|
RECEIVER_SINGLE_MAGIC_VALUE,
|
|
RECEIVER_BATCH_MAGIC_VALUE,
|
|
RevertType.Panic,
|
|
]);
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
receiver,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue],
|
|
'0x',
|
|
),
|
|
).to.be.revertedWithPanic();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('to a contract that does not implement the required function', function () {
|
|
it('reverts', async function () {
|
|
const invalidReceiver = this.token;
|
|
|
|
await expect(
|
|
this.token
|
|
.connect(this.holder)
|
|
.safeBatchTransferFrom(
|
|
this.holder,
|
|
invalidReceiver,
|
|
[firstTokenId, secondTokenId],
|
|
[firstTokenValue, secondTokenValue],
|
|
'0x',
|
|
),
|
|
)
|
|
.to.be.revertedWithCustomError(this.token, 'ERC1155InvalidReceiver')
|
|
.withArgs(invalidReceiver);
|
|
});
|
|
});
|
|
});
|
|
|
|
shouldSupportInterfaces(['ERC1155', 'ERC1155MetadataURI']);
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
shouldBehaveLikeERC1155,
|
|
};
|
|
|