forked from crytic/properties
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathERC721ExternalBasicProperties.sol
141 lines (113 loc) · 5.81 KB
/
ERC721ExternalBasicProperties.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;
import "../util/ERC721ExternalTestBase.sol";
import "../../../util/Hevm.sol";
abstract contract CryticERC721ExternalBasicProperties is CryticERC721ExternalTestBase {
using Address for address;
////////////////////////////////////////
// Properties
// Querying the balance of address(0) should throw
function test_ERC721_external_balanceOfZeroAddressMustRevert() public virtual {
token.balanceOf(address(0));
assertWithMsg(false, "address(0) balance query should have reverted");
}
// Querying the owner of an invalid token should throw
function test_ERC721_external_ownerOfInvalidTokenMustRevert() public virtual {
token.ownerOf(type(uint256).max);
assertWithMsg(false, "Invalid token owner query should have reverted");
}
// Approving an invalid token should throw
function test_ERC721_external_approvingInvalidTokenMustRevert() public virtual {
token.approve(address(0), type(uint256).max);
assertWithMsg(false, "Approving an invalid token should have reverted");
}
// transferFrom a token that the caller is not approved for should revert
function test_ERC721_external_transferFromNotApproved(address target) public virtual {
uint256 selfBalance = token.balanceOf(msg.sender);
require(selfBalance > 0);
require(target != address(this));
require(target != msg.sender);
uint tokenId = token.tokenOfOwnerByIndex(msg.sender, 0);
bool isApproved = token.isApprovedForAll(msg.sender, address(this));
address approved = token.getApproved(tokenId);
require(approved != address(this) && !isApproved);
token.transferFrom(msg.sender, target, tokenId);
assertWithMsg(false, "transferFrom without approval did not revert");
}
// transferFrom should reset approval for that token
function test_ERC721_external_transferFromResetApproval(address target) public virtual {
uint256 selfBalance = token.balanceOf(msg.sender);
require(selfBalance > 0);
require(target != address(this));
require(target != msg.sender);
require(target != address(0));
uint tokenId = token.tokenOfOwnerByIndex(msg.sender, 0);
hevm.prank(msg.sender);
token.approve(address(this), tokenId);
token.transferFrom(msg.sender, target, tokenId);
address approved = token.getApproved(tokenId);
assertWithMsg(approved == address(0), "Approval was not reset");
}
// transferFrom correctly updates owner
function test_ERC721_external_transferFromUpdatesOwner(address target) public virtual {
uint256 selfBalance = token.balanceOf(msg.sender);
require(selfBalance > 0);
require(target != address(this));
require(target != msg.sender);
require(target != address(0));
uint tokenId = token.tokenOfOwnerByIndex(msg.sender, 0);
hevm.prank(msg.sender);
try token.transferFrom(msg.sender, target, tokenId) {
assertWithMsg(token.ownerOf(tokenId) == target, "Token owner not updated");
} catch {
assertWithMsg(false, "transferFrom unexpectedly reverted");
}
}
// transfer from zero address should revert
function test_ERC721_external_transferFromZeroAddress(address target, uint256 tokenId) public virtual {
token.transferFrom(address(0), target, tokenId);
assertWithMsg(false, "transferFrom does not revert when `from` is the zero-address");
}
// Transfers to the zero address should revert
function test_ERC721_external_transferToZeroAddress() public virtual {
uint256 selfBalance = token.balanceOf(msg.sender);
require(selfBalance > 0);
uint tokenId = token.tokenOfOwnerByIndex(msg.sender, 0);
hevm.prank(msg.sender);
token.transferFrom(msg.sender, address(0), tokenId);
assertWithMsg(false, "Transfer to zero address should have reverted");
}
// Transfers to self should not break accounting
function test_ERC721_external_transferFromSelf() public virtual {
uint256 selfBalance = token.balanceOf(msg.sender);
require(selfBalance > 0);
uint tokenId = token.tokenOfOwnerByIndex(msg.sender, 0);
hevm.prank(msg.sender);
try token.transferFrom(msg.sender, msg.sender, tokenId) {
assertWithMsg(token.ownerOf(tokenId) == msg.sender, "Self transfer changes owner");
assertEq(token.balanceOf(msg.sender), selfBalance, "Self transfer breaks accounting");
} catch {
assertWithMsg(false, "transferFrom unexpectedly reverted");
}
}
// Transfer to self reset approval
function test_ERC721_external_transferFromSelfResetsApproval() public virtual {
uint256 selfBalance = token.balanceOf(msg.sender);
require(selfBalance > 0);
uint tokenId = token.tokenOfOwnerByIndex(msg.sender, 0);
require(token.ownerOf(tokenId) == msg.sender);
hevm.prank(msg.sender);
token.approve(address(this), tokenId);
token.transferFrom(msg.sender, msg.sender, tokenId);
assertWithMsg(token.getApproved(tokenId) == address(0), "Self transfer does not reset approvals");
}
// safeTransferFrom reverts if receiver does not implement the callback
function test_ERC721_external_safeTransferFromRevertsOnNoncontractReceiver() public virtual {
uint256 selfBalance = token.balanceOf(msg.sender);
require(selfBalance > 0);
uint tokenId = token.tokenOfOwnerByIndex(msg.sender, 0);
hevm.prank(msg.sender);
token.safeTransferFrom(msg.sender, address(mockUnsafeReceiver), tokenId);
assertWithMsg(false, "safeTransferFrom does not revert if receiver does not implement ERC721.onERC721Received");
}
}