-
Notifications
You must be signed in to change notification settings - Fork 0
/
ERC721BaseSubsystem.sol
194 lines (172 loc) · 5.54 KB
/
ERC721BaseSubsystem.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// erc165
import { IERC165 } from "@solidstate/contracts/interfaces/IERC165.sol";
import { ERC165Base } from "@solidstate/contracts/introspection/ERC165/base/ERC165Base.sol";
import { IERC721 } from "@solidstate/contracts/interfaces/IERC721.sol";
import { ISystem } from "@latticexyz/solecs/src/interfaces/ISystem.sol";
// ECS
import { IWorld } from "@latticexyz/solecs/src/interfaces/IWorld.sol";
import { Subsystem } from "../../mud/Subsystem.sol";
// ERC721 logic and data provider
import { ERC721BaseLogic } from "./logic/ERC721BaseLogic.sol";
import { ERC721BaseDataComponents } from "./data-providers/ERC721BaseDataComponents.sol";
/**
* @title ERC721 and ECS Subsystem that uses components.
* @dev ALL component changes MUST go through this system.
* Call `authorizeWriter` to allow another System to call the Subsystem.
* ERC721BaseSubsystem deploys its components itself, you only need to deploy the subsystem.
*/
contract ERC721BaseSubsystem is
ERC165Base,
ERC721BaseDataComponents,
ERC721BaseLogic,
Subsystem
{
error ERC721BaseSubsystem__InvalidExecuteSelector();
constructor(
IWorld _world,
address _components,
uint256 ownershipComponentId,
uint256 operatorApprovalComponentId,
uint256 tokenApprovalComponentId
) Subsystem(_world, _components) {
// create components
// (they're tightly coupled to this system, so making them separately isn't useful)
__ERC721BaseDataComponents_init(_world, ownershipComponentId, operatorApprovalComponentId, tokenApprovalComponentId);
// register interfaces
// IERC165
_setSupportsInterface(type(IERC165).interfaceId, true);
// IERC721
_setSupportsInterface(type(IERC721).interfaceId, true);
// ISystem
_setSupportsInterface(type(ISystem).interfaceId, true);
}
/**
* @notice Internally calls the specified execute method, if it's available
*/
function _execute(bytes memory args) internal virtual override returns (bytes memory) {
(bytes4 executeSelector, bytes memory innerArgs)
= abi.decode(args, (bytes4, bytes));
// mint
if (executeSelector == this.executeSafeMint.selector) {
(
address account,
uint256 tokenId,
bytes memory data
) = abi.decode(innerArgs, (address, uint256, bytes));
executeSafeMint(account, tokenId, data);
// burn
} else if (executeSelector == this.executeBurn.selector) {
(
uint256 tokenId
) = abi.decode(innerArgs, (uint256));
executeBurn(tokenId);
// transfer (approval-checked for `operator`)
} else if (executeSelector == this.executeSafeTransfer.selector) {
(
address operator,
address from,
address to,
uint256 id,
bytes memory data
) = abi.decode(innerArgs, (address, address, address, uint256, bytes));
executeSafeTransfer(operator, from, to, id, data);
// transfer (from arbitrary account)
} else if (executeSelector == this.executeArbitrarySafeTransfer.selector) {
(
address from,
address to,
uint256 tokenId,
bytes memory data
) = abi.decode(innerArgs, (address, address, uint256, bytes));
executeArbitrarySafeTransfer(from, to, tokenId, data);
// approve `operator` to use tokens of `account`
} else if (executeSelector == this.executeSetApprovalForAll.selector) {
(
address account,
address operator,
bool status
) = abi.decode(innerArgs, (address, address, bool));
executeSetApprovalForAll(account, operator, status);
} else if (executeSelector == this.executeApprove.selector) {
(
address account,
address operator,
uint256 tokenId
) = abi.decode(innerArgs, (address, address, uint256));
executeApprove(account, operator, tokenId);
} else {
revert ERC721BaseSubsystem__InvalidExecuteSelector();
}
return '';
}
/**
* @notice Mint token to any account
*/
function executeSafeMint(
address account,
uint256 id,
bytes memory data
) public virtual onlyWriter {
_safeMint(account, id, data);
}
/**
* @notice Burn any existing token
*/
function executeBurn(
uint256 tokenId
) public virtual onlyWriter {
_burn(tokenId);
}
/**
* @notice Transfer with approval check for `operator`
*
* This can be used to forward transfers
*/
function executeSafeTransfer(
address operator,
address from,
address to,
uint256 tokenId,
bytes memory data
) public virtual onlyWriter {
if (!_isApprovedOrOwner(operator, tokenId)) revert ERC721Base__NotOwnerOrApproved();
_safeTransfer(operator, from, to, tokenId, data);
}
/**
* @notice Transfer tokens from any account without needing approval
*/
function executeArbitrarySafeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory data
) public virtual onlyWriter {
_safeTransfer(_msgSender(), from, to, tokenId, data);
}
/**
* @notice Approve `operator` to use tokens of `account`
*
* This can be used to forward approval
*/
function executeSetApprovalForAll(
address account,
address operator,
bool status
) public virtual onlyWriter {
_setApprovalForAll(account, operator, status);
}
/**
* @notice Approve `operator` to use `tokenId` of `account`
*
* This can be used to forward approval
*/
function executeApprove(
address account,
address operator,
uint256 tokenId
) public virtual onlyWriter {
_approve(account, operator, tokenId);
}
}