Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions contracts/token/ERC1155/ERC1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -178,18 +178,41 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
* IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
* update to the contract state after this function would break the check-effect-interaction pattern. Consider
* overriding {_update} instead.
*
* NOTE: this version is kept for backward compatibility. we recommend calling the alternate version with a boolean
* flag to achieve better control over which hook to call.
*/
function _updateWithAcceptanceCheck(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal virtual {
_updateWithAcceptanceCheck(from, to, ids, values, data, ids.length == 1);
}

/**
* @dev Version of {_update} that performs the token acceptance check by calling
* {IERC1155Receiver-onERC1155Received} or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it
* contains code (eg. is a smart contract at the moment of execution).
*
* IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
* update to the contract state after this function would break the check-effect-interaction pattern. Consider
* overriding {_update} instead.
*/
function _updateWithAcceptanceCheck(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data,
bool single
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool single
bool batch

Looks more natural to me (0/false/single < 1/true/batch). With that the logic below should updated yes.

) internal virtual {
_update(from, to, ids, values);
if (to != address(0)) {
address operator = _msgSender();
if (ids.length == 1) {
if (single) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
ERC1155Utils.checkOnERC1155Received(operator, from, to, id, value, data);
Expand Down Expand Up @@ -219,7 +242,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, to, ids, values, data);
_updateWithAcceptanceCheck(from, to, ids, values, data, true);
}

/**
Expand All @@ -246,7 +269,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, to, ids, values, data);
_updateWithAcceptanceCheck(from, to, ids, values, data, false);
}

/**
Expand Down Expand Up @@ -288,7 +311,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
revert ERC1155InvalidReceiver(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
_updateWithAcceptanceCheck(address(0), to, ids, values, data, true);
}

/**
Expand All @@ -307,7 +330,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
_updateWithAcceptanceCheck(address(0), to, ids, values, data, false);
}

/**
Expand All @@ -325,7 +348,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
_updateWithAcceptanceCheck(from, address(0), ids, values, "", true);
}

/**
Expand All @@ -343,7 +366,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
_updateWithAcceptanceCheck(from, address(0), ids, values, "", false);
}

/**
Expand Down
64 changes: 59 additions & 5 deletions test/token/ERC1155/ERC1155.behavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,9 +484,15 @@ function shouldBehaveLikeERC1155() {
});

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);
if (this.args.ids.length == 1) {
await expect(this.tx)
.to.emit(this.token, 'TransferSingle')
.withArgs(this.args.operator, this.args.from, this.args.to, this.args.ids[0], this.args.values[0]);
} else {
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);
}
});
}

Expand Down Expand Up @@ -566,7 +572,31 @@ function shouldBehaveLikeERC1155() {
]);
});

describe('without data', function () {
describe('without data (batch of size = 1)', function () {
beforeEach(async function () {
this.args = {
operator: this.holder,
from: this.holder,
to: this.receiver,
ids: [firstTokenId],
values: [firstTokenValue],
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('without data (batch of size > 1)', function () {
beforeEach(async function () {
this.args = {
operator: this.holder,
Expand All @@ -590,7 +620,31 @@ function shouldBehaveLikeERC1155() {
});
});

describe('with data', function () {
describe('with data (batch of size = 1)', function () {
beforeEach(async function () {
this.args = {
operator: this.holder,
from: this.holder,
to: this.receiver,
ids: [firstTokenId],
values: [firstTokenValue],
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 () {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it('calls onERC1155Received', async function () {
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 (batch of size > 1)', function () {
beforeEach(async function () {
this.args = {
operator: this.holder,
Expand Down
Loading