-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathTransferableERC721.sol
127 lines (97 loc) · 5.26 KB
/
TransferableERC721.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
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Module} from "../../../Module.sol";
import {Role} from "../../../Role.sol";
import {BeforeTransferCallbackERC721} from "../../../callback/BeforeTransferCallbackERC721.sol";
library TransferableStorage {
/// @custom:storage-location erc7201:token.transferable
bytes32 public constant TRANSFERABLE_STORAGE_POSITION =
keccak256(abi.encode(uint256(keccak256("token.transferable")) - 1)) & ~bytes32(uint256(0xff));
struct Data {
// whether transfer is enabled
bool transferEnabled;
// from/to/operator address => bool, whether transfer is enabled
mapping(address => bool) transferEnabledFor;
}
function data() internal pure returns (Data storage data_) {
bytes32 position = TRANSFERABLE_STORAGE_POSITION;
assembly {
data_.slot := position
}
}
}
contract TransferableERC721 is Module, BeforeTransferCallbackERC721 {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @notice Emitted on attempt to transfer a token when transfers are disabled.
error TransferDisabled();
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/// @notice Emitted on attempt to transfer a token when transfers are disabled.
event TransferEnableFor(address indexed target, bool enabled);
/*//////////////////////////////////////////////////////////////
MODULE CONFIG
//////////////////////////////////////////////////////////////*/
/// @notice Returns all implemented callback and module functions.
function getModuleConfig() external pure override returns (ModuleConfig memory config) {
config.callbackFunctions = new CallbackFunction[](1);
config.fallbackFunctions = new FallbackFunction[](4);
config.callbackFunctions[0] = CallbackFunction(this.beforeTransferERC721.selector);
config.fallbackFunctions[0] = FallbackFunction({selector: this.isTransferEnabled.selector, permissionBits: 0});
config.fallbackFunctions[1] =
FallbackFunction({selector: this.isTransferEnabledFor.selector, permissionBits: 0});
config.fallbackFunctions[2] =
FallbackFunction({selector: this.setTransferable.selector, permissionBits: Role._MANAGER_ROLE});
config.fallbackFunctions[3] =
FallbackFunction({selector: this.setTransferableFor.selector, permissionBits: Role._MANAGER_ROLE});
config.requiredInterfaces = new bytes4[](1);
config.requiredInterfaces[0] = 0x80ac58cd; // ERC721.
config.registerInstallationCallback = true;
}
/// @dev Called by a Core into an Module during the installation of the Module.
function onInstall(bytes calldata data) external {
_transferableStorage().transferEnabled = true;
}
/// @dev Called by a Core into an Module during the uninstallation of the Module.
function onUninstall(bytes calldata data) external {}
/*//////////////////////////////////////////////////////////////
CALLBACK FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Callback function for ERC721.transferFrom/safeTransferFrom
function beforeTransferERC721(address from, address to, uint256) external virtual override returns (bytes memory) {
TransferableStorage.Data storage data = _transferableStorage();
bool isOperatorAllowed =
data.transferEnabledFor[msg.sender] || data.transferEnabledFor[from] || data.transferEnabledFor[to];
if (!isOperatorAllowed && !data.transferEnabled) {
revert TransferDisabled();
}
}
/*//////////////////////////////////////////////////////////////
FALLBACK FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Returns whether transfers is enabled for the token.
function isTransferEnabled() external view returns (bool) {
return _transferableStorage().transferEnabled;
}
/// @notice Returns whether transfers is enabled for the target address for the token.
function isTransferEnabledFor(address target) external view returns (bool) {
return _transferableStorage().transferEnabledFor[target];
}
/// @notice Set transferability for a token.
function setTransferable(bool enableTransfer) external {
_transferableStorage().transferEnabled = enableTransfer;
}
/// @notice Set transferability for an address for a token.
function setTransferableFor(address target, bool enableTransfer) external {
_transferableStorage().transferEnabledFor[target] = enableTransfer;
emit TransferEnableFor(target, enableTransfer);
}
/*//////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
function _transferableStorage() internal pure returns (TransferableStorage.Data storage) {
return TransferableStorage.data();
}
}