-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathZoneTIP403Registry.sol
More file actions
227 lines (184 loc) · 7.48 KB
/
ZoneTIP403Registry.sol
File metadata and controls
227 lines (184 loc) · 7.48 KB
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import { ITIP403Registry } from "../interfaces/ITIP403Registry.sol";
import { ITempoState, TEMPO_STATE, TIP403_REGISTRY_ADDRESS } from "./IZone.sol";
/// @title ZoneTIP403Registry
/// @notice Read-only zone mirror of Tempo's TIP-403 registry
/// @dev Deployed at the same address as the Tempo TIP-403 registry. Queries are
/// answered by reading Tempo storage through `TempoState` at the latest
/// finalized Tempo block. Mutating methods revert on the zone.
contract ZoneTIP403Registry is ITIP403Registry {
uint64 internal constant REJECT_ALL_POLICY_ID = 0;
uint64 internal constant ALLOW_ALL_POLICY_ID = 1;
// TIP403Registry storage layout on Tempo:
// slot 0: policyIdCounter
// slot 1: policyRecords (mapping(uint64 => PolicyRecord))
// slot 2: policySet (mapping(uint64 => mapping(address => bool)))
uint256 internal constant POLICY_ID_COUNTER_SLOT = 0;
uint256 internal constant POLICY_RECORDS_SLOT = 1;
uint256 internal constant POLICY_SET_SLOT = 2;
ITempoState internal constant TEMPO_STATE_CONTRACT = ITempoState(TEMPO_STATE);
error ReadOnlyRegistry();
function policyIdCounter() public view returns (uint64) {
return _policyIdCounter();
}
function policyExists(uint64 policyId) public view returns (bool) {
if (policyId <= ALLOW_ALL_POLICY_ID) {
return true;
}
return policyId < _policyIdCounter();
}
function policyData(uint64 policyId)
public
view
returns (PolicyType policyType, address admin)
{
if (policyId == REJECT_ALL_POLICY_ID) {
return (PolicyType.WHITELIST, address(0));
}
if (policyId == ALLOW_ALL_POLICY_ID) {
return (PolicyType.BLACKLIST, address(0));
}
return _getPolicyData(policyId);
}
function createPolicy(address, PolicyType) external pure returns (uint64) {
revert ReadOnlyRegistry();
}
function createPolicyWithAccounts(
address,
PolicyType,
address[] calldata
)
external
pure
returns (uint64)
{
revert ReadOnlyRegistry();
}
function setPolicyAdmin(uint64, address) external pure {
revert ReadOnlyRegistry();
}
function modifyPolicyWhitelist(uint64, address, bool) external pure {
revert ReadOnlyRegistry();
}
function modifyPolicyBlacklist(uint64, address, bool) external pure {
revert ReadOnlyRegistry();
}
function isAuthorized(uint64 policyId, address user) public view returns (bool) {
if (policyId <= ALLOW_ALL_POLICY_ID) {
return policyId == ALLOW_ALL_POLICY_ID;
}
(PolicyType policyType,) = _getPolicyData(policyId);
if (policyType == PolicyType.COMPOUND) {
if (!isAuthorizedSender(policyId, user)) {
return false;
}
return isAuthorizedRecipient(policyId, user);
}
return _isAuthorizedSimple(policyId, user, policyType);
}
function createCompoundPolicy(uint64, uint64, uint64)
external
pure
returns (uint64)
{
revert ReadOnlyRegistry();
}
function isAuthorizedSender(uint64 policyId, address user) public view returns (bool) {
if (policyId <= ALLOW_ALL_POLICY_ID) {
return policyId == ALLOW_ALL_POLICY_ID;
}
(PolicyType policyType,) = _getPolicyData(policyId);
if (policyType == PolicyType.COMPOUND) {
(uint64 senderPolicyId,,) = _getCompoundPolicyData(policyId);
return isAuthorized(senderPolicyId, user);
}
return _isAuthorizedSimple(policyId, user, policyType);
}
function isAuthorizedRecipient(uint64 policyId, address user) public view returns (bool) {
if (policyId <= ALLOW_ALL_POLICY_ID) {
return policyId == ALLOW_ALL_POLICY_ID;
}
(PolicyType policyType,) = _getPolicyData(policyId);
if (policyType == PolicyType.COMPOUND) {
(, uint64 recipientPolicyId,) = _getCompoundPolicyData(policyId);
return isAuthorized(recipientPolicyId, user);
}
return _isAuthorizedSimple(policyId, user, policyType);
}
function isAuthorizedMintRecipient(uint64 policyId, address user)
public
view
returns (bool)
{
if (policyId <= ALLOW_ALL_POLICY_ID) {
return policyId == ALLOW_ALL_POLICY_ID;
}
(PolicyType policyType,) = _getPolicyData(policyId);
if (policyType == PolicyType.COMPOUND) {
(,, uint64 mintRecipientPolicyId) = _getCompoundPolicyData(policyId);
return isAuthorized(mintRecipientPolicyId, user);
}
return _isAuthorizedSimple(policyId, user, policyType);
}
function compoundPolicyData(uint64 policyId)
external
view
returns (uint64 senderPolicyId, uint64 recipientPolicyId, uint64 mintRecipientPolicyId)
{
(PolicyType policyType,) = policyData(policyId);
if (policyType != PolicyType.COMPOUND) revert IncompatiblePolicyType();
return _getCompoundPolicyData(policyId);
}
function _policyIdCounter() internal view returns (uint64) {
return uint64(uint256(_readTempoStorage(bytes32(POLICY_ID_COUNTER_SLOT))));
}
function _getPolicyData(uint64 policyId)
internal
view
returns (PolicyType policyType, address admin)
{
bytes32 raw = _readTempoStorage(_policyRecordBaseSlot(policyId));
uint8 rawPolicyType = uint8(uint256(raw));
if (rawPolicyType > uint8(PolicyType.COMPOUND)) revert InvalidPolicyType();
policyType = PolicyType(rawPolicyType);
admin = address(uint160(uint256(raw) >> 8));
// Match the L1 registry's "default slot means maybe-missing" check.
if (
rawPolicyType == uint8(PolicyType.WHITELIST) && admin == address(0)
&& policyId >= _policyIdCounter()
) {
revert PolicyNotFound();
}
}
function _getCompoundPolicyData(uint64 policyId)
internal
view
returns (uint64 senderPolicyId, uint64 recipientPolicyId, uint64 mintRecipientPolicyId)
{
bytes32 raw = _readTempoStorage(bytes32(uint256(_policyRecordBaseSlot(policyId)) + 1));
senderPolicyId = uint64(uint256(raw));
recipientPolicyId = uint64(uint256(raw) >> 64);
mintRecipientPolicyId = uint64(uint256(raw) >> 128);
}
function _isAuthorizedSimple(uint64 policyId, address user, PolicyType policyType)
internal
view
returns (bool)
{
if (policyType == PolicyType.COMPOUND) revert IncompatiblePolicyType();
bool inSet = _readPolicySet(policyId, user);
return policyType == PolicyType.WHITELIST ? inSet : !inSet;
}
function _readPolicySet(uint64 policyId, address user) internal view returns (bool) {
bytes32 policySetBase = keccak256(abi.encode(policyId, uint256(POLICY_SET_SLOT)));
bytes32 userSlot = keccak256(abi.encode(user, policySetBase));
return uint8(uint256(_readTempoStorage(userSlot)) & 0xff) != 0;
}
function _policyRecordBaseSlot(uint64 policyId) internal pure returns (bytes32) {
return keccak256(abi.encode(policyId, uint256(POLICY_RECORDS_SLOT)));
}
function _readTempoStorage(bytes32 slot) internal view returns (bytes32) {
return TEMPO_STATE_CONTRACT.readTempoStorageSlot(TIP403_REGISTRY_ADDRESS, slot);
}
}