Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/improve revert reason #1727 #2018

Merged
merged 24 commits into from
Jan 23, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
839b6bf
adding mock contacts, test code
alant Dec 8, 2019
bcb3125
adding changes to ERC721.sol per @frangio's comments on original PR #…
alant Dec 8, 2019
2ee16ec
fix solhint warnings
alant Dec 8, 2019
027822d
Update contracts/token/ERC721/ERC721.sol
alant Dec 10, 2019
07dcbd3
same revert wording per @frangio's review suggestion
alant Dec 10, 2019
1755395
Merge branch 'fix/improve-revert-reason-#1727' of https://github.com/…
alant Dec 10, 2019
be4d51d
per @frangio's feedback, changing the inline assembly to accomplish: …
alant Dec 10, 2019
f0a90f6
change revert msg assembly per PR comment by @frangio
alant Dec 11, 2019
ab1fb60
unify revert msg in test code
alant Dec 11, 2019
59cba42
Merge branch 'master' into fix/improve-revert-reason-#1727
alant Dec 11, 2019
cd436a6
fix some failed tests, wording change
alant Dec 12, 2019
ee84cda
Update contracts/token/ERC721/ERC721.sol
alant Dec 13, 2019
829032d
Update contracts/token/ERC721/ERC721.sol
alant Dec 13, 2019
f5a6a95
Merge branch 'fix/improve-revert-reason-#1727' of https://github.com/…
alant Dec 13, 2019
1162b59
fix test case, revert without reason
alant Dec 24, 2019
6aec945
fix 'ERC721ReceiverRevertsMock: Transaction rejected by receiver'
alant Jan 7, 2020
604f88b
Merge branch 'master' into fix/improve-revert-reason-#1727
alant Jan 7, 2020
9668a49
style change per review by @frangio
alant Jan 14, 2020
cf5c2b8
Merge branch 'fix/improve-revert-reason-#1727' of https://github.com/…
alant Jan 14, 2020
f279bec
fix revert reason forwarding
frangio Jan 14, 2020
6f33362
remove duplicate contracts/mocks/ERC721ReceiverRevertsMock.sol per re…
alant Jan 15, 2020
4c2930d
Add changelog entry
nventuro Jan 23, 2020
2538e97
Fix tests
nventuro Jan 23, 2020
f3e382a
Make tests more clear
nventuro Jan 23, 2020
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
7 changes: 7 additions & 0 deletions contracts/mocks/ERC721ReceiverNotImplementedMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity ^0.5.0;

contract ERC721ReceiverNotImplementedMock{

event Received(address operator, address from, uint256 tokenId, bytes data, uint256 gas);

}
23 changes: 23 additions & 0 deletions contracts/mocks/ERC721ReceiverRevertsMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pragma solidity ^0.5.0;

import "../token/ERC721/IERC721Receiver.sol";

contract ERC721ReceiverRevertsMock is IERC721Receiver {
bytes4 private _retval;
bool private _reverts;

event Received(address operator, address from, uint256 tokenId, bytes data, uint256 gas);

constructor (bytes4 retval, bool reverts) public {
_retval = retval;
_reverts = reverts;
}

function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public returns (bytes4)
{
require(!_reverts, "ERC721ReceiverRevertsMock: Transaction rejected by receiver");
emit Received(operator, from, tokenId, data, gasleft());
return _retval;
}
}
29 changes: 26 additions & 3 deletions contracts/token/ERC721/ERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,32 @@ contract ERC721 is Context, ERC165, IERC721 {
if (!to.isContract()) {
return true;
}

bytes4 retval = IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data);
return (retval == _ERC721_RECEIVED);
bytes memory payload = abi.encodeWithSelector(
IERC721Receiver(to).onERC721Received.selector,
"onERC721Received(address,address,uint256,bytes)",
alant marked this conversation as resolved.
Show resolved Hide resolved
msg.sender,
alant marked this conversation as resolved.
Show resolved Hide resolved
from,
tokenId,
_data
);
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = to.call(payload);
alant marked this conversation as resolved.
Show resolved Hide resolved
if (!success) {
if (returndata.length > 0) {
uint memOffset;
// solhint-disable-next-line no-inline-assembly
assembly {
alant marked this conversation as resolved.
Show resolved Hide resolved
memOffset := msize() // Get the highest available block of memory
mstore(add(memOffset, 0x00), returndata) // Set value
mstore(0x40, add(memOffset, 0x20)) // Update the msize offset to be our memory reference plus the amount of bytes we're using
revert(memOffset, 0x20) // revert returning 1 byte
}
} else {
revert("ERC721: to address does not implement ERC721Received interface");
alant marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
return true;
alant marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
Expand Down
29 changes: 19 additions & 10 deletions test/token/ERC721/ERC721.behavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const { shouldSupportInterfaces } = require('../../introspection/SupportsInterfa

const ERC721ReceiverMock = contract.fromArtifact('ERC721ReceiverMock');
const ERC721Mock = contract.fromArtifact('ERC721Mock');
const ERC721NoReceiverMock = contract.fromArtifact('ERC721ReceiverNotImplementedMock');
const ERC721ReceiverRevertMock = contract.fromArtifact('ERC721ReceiverRevertsMock');

function shouldBehaveLikeERC721 (
creator,
Expand Down Expand Up @@ -297,25 +299,32 @@ function shouldBehaveLikeERC721 (

describe('to a receiver contract returning unexpected value', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', false);
const noReceiverImplemented = await ERC721NoReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
await expectRevert(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer'
this.token.safeTransferFrom(owner, noReceiverImplemented.address, tokenId, { from: owner }),
'ERC721: to address does not implement ERC721Received interface'
);
});
});

describe('to a receiver contract that throws', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
const invalidReceiver = await ERC721ReceiverRevertMock.new(RECEIVER_MAGIC_VALUE, true);
await expectRevert(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'ERC721ReceiverMock: reverting'
'ERC721ReceiverMock: Transaction rejected by receiver'
);
});
});

describe('to a contract that does not implement the required function', function () {
it('reverts', async function () {
const noReceiverImplemented = await ERC721NoReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
await expectRevert(
this.token.safeTransferFrom(owner, noReceiverImplemented.address, tokenId, { from: owner }),
'ERC721: to address does not implement ERC721Received interface'
);
});
it('reverts', async function () {
const invalidReceiver = this.token;
await expectRevert.unspecified(
Expand Down Expand Up @@ -359,20 +368,20 @@ function shouldBehaveLikeERC721 (

context('to a receiver contract returning unexpected value', function () {
alant marked this conversation as resolved.
Show resolved Hide resolved
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', false);
const noReceiverImplemented = await ERC721NoReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
await expectRevert(
this.ERC721Mock.safeMint(invalidReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer'
this.ERC721Mock.safeMint(noReceiverImplemented.address, tokenId),
'ERC721: to address does not implement ERC721Received interface'
);
});
});

context('to a receiver contract that throws', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
const invalidReceiver = await ERC721ReceiverRevertMock.new(RECEIVER_MAGIC_VALUE, true);
await expectRevert(
this.ERC721Mock.safeMint(invalidReceiver.address, tokenId),
'ERC721ReceiverMock: reverting'
'ERC721ReceiverMock: Transaction rejected by receiver'
);
});
});
Expand Down