@lfdecentralizedtrust/zeto-contracts 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/config/eip170.ts +30 -0
- package/contracts/lib/interfaces/IZetoLockHooks.sol +42 -0
- package/contracts/lib/zeto_fungible.sol +9 -17
- package/contracts/lib/zeto_lockable.sol +85 -479
- package/contracts/lib/zeto_lockable_lib.sol +460 -0
- package/contracts/lib/zeto_lockable_storage.sol +43 -0
- package/contracts/lib/zeto_non_fungible.sol +8 -16
- package/hardhat.config.ts +11 -2
- package/ignition/modules/lib/deps.ts +5 -0
- package/ignition/modules/test/tendecimals.ts +7 -1
- package/ignition/modules/zeto_anon.ts +3 -0
- package/ignition/modules/zeto_anon_burnable.ts +3 -0
- package/ignition/modules/zeto_anon_enc.ts +3 -0
- package/ignition/modules/zeto_anon_enc_nullifier.ts +3 -0
- package/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +3 -0
- package/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts +3 -0
- package/ignition/modules/zeto_anon_nullifier.ts +3 -0
- package/ignition/modules/zeto_anon_nullifier_burnable.ts +3 -0
- package/ignition/modules/zeto_anon_nullifier_kyc.ts +3 -0
- package/ignition/modules/zeto_anon_nullifier_qurrency.ts +3 -0
- package/ignition/modules/zeto_nf_anon.ts +3 -1
- package/ignition/modules/zeto_nf_anon_nullifier.ts +3 -2
- package/package.json +4 -2
- package/scripts/lib/zeto_libraries.ts +47 -0
- package/scripts/tokens/Zeto_Anon.ts +3 -0
- package/scripts/tokens/Zeto_AnonBurnable.ts +3 -0
- package/scripts/tokens/Zeto_AnonEnc.ts +3 -0
- package/scripts/tokens/Zeto_AnonEncNullifier.ts +6 -5
- package/scripts/tokens/Zeto_AnonEncNullifierKyc.ts +6 -5
- package/scripts/tokens/Zeto_AnonEncNullifierNonRepudiation.ts +6 -5
- package/scripts/tokens/Zeto_AnonNullifier.ts +6 -5
- package/scripts/tokens/Zeto_AnonNullifierBurnable.ts +6 -5
- package/scripts/tokens/Zeto_AnonNullifierKyc.ts +6 -5
- package/scripts/tokens/Zeto_AnonNullifierQurrency.ts +6 -7
- package/scripts/tokens/Zeto_NfAnon.ts +3 -1
- package/scripts/tokens/Zeto_NfAnonNullifier.ts +6 -6
- package/test/factory.ts +10 -1
- package/test/lib/deploy.ts +2 -0
- package/test/lib/eip170.ts +23 -0
- package/test/usdc-shielding.ts +0 -1
- package/test/zeto_anon_enc.ts +0 -1
- package/test/zeto_anon_enc_nullifier.ts +3 -1
- package/test/zeto_anon_enc_nullifier_kyc.ts +3 -1
- package/test/zeto_anon_enc_nullifier_non_repudiation.ts +3 -1
- package/test/zeto_anon_nullifier.ts +0 -1
- package/test/zeto_anon_nullifier_kyc.ts +3 -1
- package/test/zeto_anon_nullifier_qurrency.ts +3 -1
- package/test/zeto_nf_anon.ts +0 -1
- package/test/zeto_nf_anon_nullifier.ts +0 -1
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
// Copyright © 2024 Kaleido, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
//
|
|
5
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
// you may not use this file except in compliance with the License.
|
|
7
|
+
// You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
// See the License for the specific language governing permissions and
|
|
15
|
+
// limitations under the License.
|
|
16
|
+
pragma solidity ^0.8.27;
|
|
17
|
+
|
|
18
|
+
import {ILockableCapability} from "./interfaces/ILockableCapability.sol";
|
|
19
|
+
import {IZetoLockableCapability} from "./interfaces/IZetoLockableCapability.sol";
|
|
20
|
+
import {IZetoLockHooks} from "./interfaces/IZetoLockHooks.sol";
|
|
21
|
+
import {ZetoCommonStorage} from "./zeto_common.sol";
|
|
22
|
+
import {ZetoLockableStorage} from "./zeto_lockable_storage.sol";
|
|
23
|
+
|
|
24
|
+
/// @title ZetoLockableLib
|
|
25
|
+
/// @author Kaleido, Inc.
|
|
26
|
+
/// @dev External library holding the shared {IZetoLockableCapability} lock
|
|
27
|
+
/// lifecycle. Deploy once per chain and link into every Zeto token
|
|
28
|
+
/// implementation; logic runs in the token's storage context via
|
|
29
|
+
/// `DELEGATECALL`. Circuit-specific work is delegated back to the
|
|
30
|
+
/// embedding token through {IZetoLockHooks}.
|
|
31
|
+
library ZetoLockableLib {
|
|
32
|
+
/// @dev Domain-separation tag for spend-intent commitments.
|
|
33
|
+
bytes32 private constant _SPEND_HASH_DOMAIN =
|
|
34
|
+
keccak256("Zeto.spendCommitment.v1");
|
|
35
|
+
|
|
36
|
+
/// @dev Domain-separation tag for cancel-intent commitments.
|
|
37
|
+
bytes32 private constant _CANCEL_HASH_DOMAIN =
|
|
38
|
+
keccak256("Zeto.cancelCommitment.v1");
|
|
39
|
+
|
|
40
|
+
function computeLockId(
|
|
41
|
+
bytes calldata createArgs
|
|
42
|
+
) public view returns (bytes32) {
|
|
43
|
+
IZetoLockableCapability.ZetoCreateLockArgs memory args = abi.decode(
|
|
44
|
+
createArgs,
|
|
45
|
+
(IZetoLockableCapability.ZetoCreateLockArgs)
|
|
46
|
+
);
|
|
47
|
+
return _computeLockId(args.txId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function computeSpendHash(
|
|
51
|
+
uint256[] calldata lockedInputs,
|
|
52
|
+
uint256[] calldata lockedOutputs,
|
|
53
|
+
uint256[] calldata outputs,
|
|
54
|
+
bytes calldata data
|
|
55
|
+
) public pure returns (bytes32) {
|
|
56
|
+
return
|
|
57
|
+
_buildUnlockHash(
|
|
58
|
+
lockedInputs,
|
|
59
|
+
lockedOutputs,
|
|
60
|
+
outputs,
|
|
61
|
+
data,
|
|
62
|
+
_SPEND_HASH_DOMAIN
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function computeCancelHash(
|
|
67
|
+
uint256[] calldata lockedInputs,
|
|
68
|
+
uint256[] calldata lockedOutputs,
|
|
69
|
+
uint256[] calldata outputs,
|
|
70
|
+
bytes calldata data
|
|
71
|
+
) public pure returns (bytes32) {
|
|
72
|
+
return
|
|
73
|
+
_buildUnlockHash(
|
|
74
|
+
lockedInputs,
|
|
75
|
+
lockedOutputs,
|
|
76
|
+
outputs,
|
|
77
|
+
data,
|
|
78
|
+
_CANCEL_HASH_DOMAIN
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function createLock(
|
|
83
|
+
bytes calldata createArgs,
|
|
84
|
+
bytes32 spendCommitment,
|
|
85
|
+
bytes32 cancelCommitment,
|
|
86
|
+
bytes calldata data
|
|
87
|
+
) public returns (bytes32) {
|
|
88
|
+
IZetoLockableCapability.ZetoCreateLockArgs memory args = abi.decode(
|
|
89
|
+
createArgs,
|
|
90
|
+
(IZetoLockableCapability.ZetoCreateLockArgs)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
bytes32 lockId = _computeLockId(args.txId);
|
|
94
|
+
if (ZetoLockableStorage.layout().locks[lockId].owner != address(0)) {
|
|
95
|
+
revert IZetoLockableCapability.DuplicateLock(lockId);
|
|
96
|
+
}
|
|
97
|
+
_useTxId(args.txId);
|
|
98
|
+
|
|
99
|
+
IZetoLockHooks(address(this)).zetoLockDoLockTransition(args);
|
|
100
|
+
|
|
101
|
+
// Delegate projection must be set here (not in the hook): the hook
|
|
102
|
+
// runs as an external self-call where msg.sender is address(this).
|
|
103
|
+
setLockDelegates(args.lockedOutputs, msg.sender);
|
|
104
|
+
|
|
105
|
+
ZetoLockableStorage.ZetoLockInfo storage lock = ZetoLockableStorage
|
|
106
|
+
.layout()
|
|
107
|
+
.locks[lockId];
|
|
108
|
+
lock.owner = msg.sender;
|
|
109
|
+
lock.spender = msg.sender;
|
|
110
|
+
lock.spendCommitment = spendCommitment;
|
|
111
|
+
lock.cancelCommitment = cancelCommitment;
|
|
112
|
+
lock.lockedInputs = args.lockedOutputs;
|
|
113
|
+
|
|
114
|
+
emit ILockableCapability.LockCreated(
|
|
115
|
+
lockId,
|
|
116
|
+
msg.sender,
|
|
117
|
+
msg.sender,
|
|
118
|
+
spendCommitment,
|
|
119
|
+
cancelCommitment,
|
|
120
|
+
data
|
|
121
|
+
);
|
|
122
|
+
emit IZetoLockableCapability.ZetoLockCreated(
|
|
123
|
+
args.txId,
|
|
124
|
+
lockId,
|
|
125
|
+
msg.sender,
|
|
126
|
+
args.inputs,
|
|
127
|
+
args.outputs,
|
|
128
|
+
args.lockedOutputs,
|
|
129
|
+
args.proof,
|
|
130
|
+
data
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return lockId;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function updateLock(
|
|
137
|
+
bytes32 lockId,
|
|
138
|
+
bytes calldata updateArgs,
|
|
139
|
+
bytes32 spendCommitment,
|
|
140
|
+
bytes32 cancelCommitment,
|
|
141
|
+
bytes calldata data
|
|
142
|
+
) public {
|
|
143
|
+
_requireLockActive(lockId);
|
|
144
|
+
_requireLockOwner(lockId);
|
|
145
|
+
|
|
146
|
+
ZetoLockableStorage.ZetoLockInfo storage lock = ZetoLockableStorage
|
|
147
|
+
.layout()
|
|
148
|
+
.locks[lockId];
|
|
149
|
+
if (lock.spender != lock.owner) {
|
|
150
|
+
revert ILockableCapability.LockImmutable(lockId);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
IZetoLockableCapability.ZetoUpdateLockArgs memory args = abi.decode(
|
|
154
|
+
updateArgs,
|
|
155
|
+
(IZetoLockableCapability.ZetoUpdateLockArgs)
|
|
156
|
+
);
|
|
157
|
+
_useTxId(args.txId);
|
|
158
|
+
|
|
159
|
+
lock.spendCommitment = spendCommitment;
|
|
160
|
+
lock.cancelCommitment = cancelCommitment;
|
|
161
|
+
|
|
162
|
+
emit ILockableCapability.LockUpdated(
|
|
163
|
+
lockId,
|
|
164
|
+
msg.sender,
|
|
165
|
+
spendCommitment,
|
|
166
|
+
cancelCommitment,
|
|
167
|
+
data
|
|
168
|
+
);
|
|
169
|
+
emit IZetoLockableCapability.ZetoLockUpdated(
|
|
170
|
+
args.txId,
|
|
171
|
+
lockId,
|
|
172
|
+
msg.sender,
|
|
173
|
+
data
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function delegateLock(
|
|
178
|
+
bytes32 lockId,
|
|
179
|
+
bytes calldata delegateArgs,
|
|
180
|
+
address newSpender,
|
|
181
|
+
bytes calldata data
|
|
182
|
+
) public {
|
|
183
|
+
_requireLockActive(lockId);
|
|
184
|
+
_requireLockSpender(lockId);
|
|
185
|
+
|
|
186
|
+
IZetoLockableCapability.ZetoDelegateLockArgs memory args = abi.decode(
|
|
187
|
+
delegateArgs,
|
|
188
|
+
(IZetoLockableCapability.ZetoDelegateLockArgs)
|
|
189
|
+
);
|
|
190
|
+
_useTxId(args.txId);
|
|
191
|
+
|
|
192
|
+
ZetoLockableStorage.ZetoLockInfo storage lock = ZetoLockableStorage
|
|
193
|
+
.layout()
|
|
194
|
+
.locks[lockId];
|
|
195
|
+
address previousSpender = lock.spender;
|
|
196
|
+
lock.spender = newSpender;
|
|
197
|
+
|
|
198
|
+
setLockDelegates(lock.lockedInputs, newSpender);
|
|
199
|
+
|
|
200
|
+
emit ILockableCapability.LockDelegated(
|
|
201
|
+
lockId,
|
|
202
|
+
previousSpender,
|
|
203
|
+
newSpender,
|
|
204
|
+
data
|
|
205
|
+
);
|
|
206
|
+
emit IZetoLockableCapability.ZetoLockDelegated(
|
|
207
|
+
args.txId,
|
|
208
|
+
lockId,
|
|
209
|
+
previousSpender,
|
|
210
|
+
newSpender,
|
|
211
|
+
data
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function spendLock(
|
|
216
|
+
bytes32 lockId,
|
|
217
|
+
bytes calldata spendArgs,
|
|
218
|
+
bytes calldata data
|
|
219
|
+
) public {
|
|
220
|
+
_requireLockActive(lockId);
|
|
221
|
+
_requireLockSpender(lockId);
|
|
222
|
+
|
|
223
|
+
IZetoLockableCapability.ZetoSpendLockArgs memory args = abi.decode(
|
|
224
|
+
spendArgs,
|
|
225
|
+
(IZetoLockableCapability.ZetoSpendLockArgs)
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
uint256[] memory lockedInputs = ZetoLockableStorage
|
|
229
|
+
.layout()
|
|
230
|
+
.locks[lockId]
|
|
231
|
+
.lockedInputs;
|
|
232
|
+
bytes32 expectedHash = ZetoLockableStorage
|
|
233
|
+
.layout()
|
|
234
|
+
.locks[lockId]
|
|
235
|
+
.spendCommitment;
|
|
236
|
+
_consumeLock(
|
|
237
|
+
lockId,
|
|
238
|
+
lockedInputs,
|
|
239
|
+
expectedHash,
|
|
240
|
+
_SPEND_HASH_DOMAIN,
|
|
241
|
+
args
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
emit ILockableCapability.LockSpent(lockId, msg.sender, data);
|
|
245
|
+
emit IZetoLockableCapability.ZetoLockSpent(
|
|
246
|
+
args.txId,
|
|
247
|
+
lockId,
|
|
248
|
+
msg.sender,
|
|
249
|
+
lockedInputs,
|
|
250
|
+
args.lockedOutputs,
|
|
251
|
+
args.outputs,
|
|
252
|
+
args.proof,
|
|
253
|
+
data
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function cancelLock(
|
|
258
|
+
bytes32 lockId,
|
|
259
|
+
bytes calldata cancelArgs,
|
|
260
|
+
bytes calldata data
|
|
261
|
+
) public {
|
|
262
|
+
_requireLockActive(lockId);
|
|
263
|
+
_requireLockSpender(lockId);
|
|
264
|
+
|
|
265
|
+
IZetoLockableCapability.ZetoSpendLockArgs memory args = abi.decode(
|
|
266
|
+
cancelArgs,
|
|
267
|
+
(IZetoLockableCapability.ZetoSpendLockArgs)
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
uint256[] memory lockedInputs = ZetoLockableStorage
|
|
271
|
+
.layout()
|
|
272
|
+
.locks[lockId]
|
|
273
|
+
.lockedInputs;
|
|
274
|
+
bytes32 expectedHash = ZetoLockableStorage
|
|
275
|
+
.layout()
|
|
276
|
+
.locks[lockId]
|
|
277
|
+
.cancelCommitment;
|
|
278
|
+
_consumeLock(
|
|
279
|
+
lockId,
|
|
280
|
+
lockedInputs,
|
|
281
|
+
expectedHash,
|
|
282
|
+
_CANCEL_HASH_DOMAIN,
|
|
283
|
+
args
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
emit ILockableCapability.LockCancelled(lockId, msg.sender, data);
|
|
287
|
+
emit IZetoLockableCapability.ZetoLockCancelled(
|
|
288
|
+
args.txId,
|
|
289
|
+
lockId,
|
|
290
|
+
msg.sender,
|
|
291
|
+
lockedInputs,
|
|
292
|
+
args.lockedOutputs,
|
|
293
|
+
args.outputs,
|
|
294
|
+
args.proof,
|
|
295
|
+
data
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function getLock(
|
|
300
|
+
bytes32 lockId
|
|
301
|
+
) public view returns (ILockableCapability.LockInfo memory) {
|
|
302
|
+
_requireLockActive(lockId);
|
|
303
|
+
ZetoLockableStorage.ZetoLockInfo storage lock = ZetoLockableStorage
|
|
304
|
+
.layout()
|
|
305
|
+
.locks[lockId];
|
|
306
|
+
return
|
|
307
|
+
ILockableCapability.LockInfo({
|
|
308
|
+
owner: lock.owner,
|
|
309
|
+
spender: lock.spender,
|
|
310
|
+
spendCommitment: lock.spendCommitment,
|
|
311
|
+
cancelCommitment: lock.cancelCommitment
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function isLockActive(bytes32 lockId) public view returns (bool) {
|
|
316
|
+
return ZetoLockableStorage.layout().locks[lockId].owner != address(0);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function getLockContent(
|
|
320
|
+
bytes32 lockId
|
|
321
|
+
) public view returns (bytes memory content) {
|
|
322
|
+
_requireLockActive(lockId);
|
|
323
|
+
return
|
|
324
|
+
abi.encode(ZetoLockableStorage.layout().locks[lockId].lockedInputs);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function getLockedInputs(
|
|
328
|
+
bytes32 lockId
|
|
329
|
+
) public view returns (uint256[] memory lockedInputs) {
|
|
330
|
+
_requireLockActive(lockId);
|
|
331
|
+
return ZetoLockableStorage.layout().locks[lockId].lockedInputs;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function locked(uint256 utxo) public view returns (bool, address) {
|
|
335
|
+
if (!ZetoCommonStorage.layout().utxoStorage.locked(utxo)) {
|
|
336
|
+
return (false, address(0));
|
|
337
|
+
}
|
|
338
|
+
return (true, ZetoLockableStorage.layout().utxoDelegates[utxo]);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function setLockDelegates(
|
|
342
|
+
uint256[] memory utxos,
|
|
343
|
+
address spender
|
|
344
|
+
) public {
|
|
345
|
+
for (uint256 i = 0; i < utxos.length; ++i) {
|
|
346
|
+
if (utxos[i] != 0) {
|
|
347
|
+
ZetoLockableStorage.layout().utxoDelegates[utxos[i]] = spender;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function _computeLockId(bytes32 txId) private view returns (bytes32) {
|
|
353
|
+
return keccak256(abi.encode(address(this), msg.sender, txId));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function _useTxId(bytes32 txId) private {
|
|
357
|
+
if (ZetoLockableStorage.layout().txIds[msg.sender][txId]) {
|
|
358
|
+
revert IZetoLockableCapability.DuplicateTransaction(txId);
|
|
359
|
+
}
|
|
360
|
+
ZetoLockableStorage.layout().txIds[msg.sender][txId] = true;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function _requireLockActive(bytes32 lockId) private view {
|
|
364
|
+
if (ZetoLockableStorage.layout().locks[lockId].owner == address(0)) {
|
|
365
|
+
revert ILockableCapability.LockNotActive(lockId);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function _requireLockOwner(bytes32 lockId) private view {
|
|
370
|
+
address owner = ZetoLockableStorage.layout().locks[lockId].owner;
|
|
371
|
+
if (owner != msg.sender) {
|
|
372
|
+
revert ILockableCapability.LockUnauthorized(
|
|
373
|
+
lockId,
|
|
374
|
+
owner,
|
|
375
|
+
msg.sender
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function _requireLockSpender(bytes32 lockId) private view {
|
|
381
|
+
address spender = ZetoLockableStorage.layout().locks[lockId].spender;
|
|
382
|
+
if (spender != msg.sender) {
|
|
383
|
+
revert ILockableCapability.LockUnauthorized(
|
|
384
|
+
lockId,
|
|
385
|
+
spender,
|
|
386
|
+
msg.sender
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function _consumeLock(
|
|
392
|
+
bytes32 lockId,
|
|
393
|
+
uint256[] memory lockedInputs,
|
|
394
|
+
bytes32 expectedHash,
|
|
395
|
+
bytes32 hashDomain,
|
|
396
|
+
IZetoLockableCapability.ZetoSpendLockArgs memory args
|
|
397
|
+
) private {
|
|
398
|
+
_useTxId(args.txId);
|
|
399
|
+
|
|
400
|
+
if (expectedHash != 0) {
|
|
401
|
+
bytes32 actualHash = _buildUnlockHash(
|
|
402
|
+
lockedInputs,
|
|
403
|
+
args.lockedOutputs,
|
|
404
|
+
args.outputs,
|
|
405
|
+
args.data,
|
|
406
|
+
hashDomain
|
|
407
|
+
);
|
|
408
|
+
if (actualHash != expectedHash) {
|
|
409
|
+
revert IZetoLockableCapability.InvalidUnlockHash(
|
|
410
|
+
expectedHash,
|
|
411
|
+
actualHash
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
_clearLockDelegates(lockedInputs);
|
|
417
|
+
delete ZetoLockableStorage.layout().locks[lockId];
|
|
418
|
+
|
|
419
|
+
IZetoLockHooks(address(this)).zetoLockTransferLocked(
|
|
420
|
+
lockId,
|
|
421
|
+
lockedInputs,
|
|
422
|
+
args.lockedOutputs,
|
|
423
|
+
args.outputs,
|
|
424
|
+
args.proof,
|
|
425
|
+
args.data
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
// Same msg.sender rationale as {createLock}: hook is external self-call.
|
|
429
|
+
if (args.lockedOutputs.length > 0) {
|
|
430
|
+
setLockDelegates(args.lockedOutputs, msg.sender);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function _clearLockDelegates(uint256[] memory utxos) private {
|
|
435
|
+
for (uint256 i = 0; i < utxos.length; ++i) {
|
|
436
|
+
if (utxos[i] != 0) {
|
|
437
|
+
delete ZetoLockableStorage.layout().utxoDelegates[utxos[i]];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function _buildUnlockHash(
|
|
443
|
+
uint256[] memory lockedInputs,
|
|
444
|
+
uint256[] memory lockedOutputs,
|
|
445
|
+
uint256[] memory outputs,
|
|
446
|
+
bytes memory data,
|
|
447
|
+
bytes32 domain
|
|
448
|
+
) private pure returns (bytes32) {
|
|
449
|
+
return
|
|
450
|
+
keccak256(
|
|
451
|
+
abi.encode(
|
|
452
|
+
domain,
|
|
453
|
+
keccak256(abi.encode(lockedInputs)),
|
|
454
|
+
keccak256(abi.encode(lockedOutputs)),
|
|
455
|
+
keccak256(abi.encode(outputs)),
|
|
456
|
+
keccak256(data)
|
|
457
|
+
)
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright © 2024 Kaleido, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
//
|
|
5
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
// you may not use this file except in compliance with the License.
|
|
7
|
+
// You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
// See the License for the specific language governing permissions and
|
|
15
|
+
// limitations under the License.
|
|
16
|
+
pragma solidity ^0.8.27;
|
|
17
|
+
|
|
18
|
+
/// @dev ERC-7201 (`erc7201:zeto.storage.ZetoLockable`): lock lifecycle state for
|
|
19
|
+
/// {ZetoLockableLib} and embedding token contracts.
|
|
20
|
+
library ZetoLockableStorage {
|
|
21
|
+
struct ZetoLockInfo {
|
|
22
|
+
address owner;
|
|
23
|
+
address spender;
|
|
24
|
+
bytes32 spendCommitment;
|
|
25
|
+
bytes32 cancelCommitment;
|
|
26
|
+
uint256[] lockedInputs;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
struct Layout {
|
|
30
|
+
mapping(bytes32 => ZetoLockInfo) locks;
|
|
31
|
+
mapping(address => mapping(bytes32 => bool)) txIds;
|
|
32
|
+
mapping(uint256 => address) utxoDelegates;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
bytes32 private constant STORAGE_LOCATION =
|
|
36
|
+
0x6f84b5947db308f6274c3bdf3450b8e85913b258c3b2e7abbddf0986236a4900;
|
|
37
|
+
|
|
38
|
+
function layout() internal pure returns (Layout storage $) {
|
|
39
|
+
assembly {
|
|
40
|
+
$.slot := STORAGE_LOCATION
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -28,8 +28,8 @@ import {IZetoStorage} from "./interfaces/IZetoStorage.sol";
|
|
|
28
28
|
/// (aka the sender is authorized to spend the input UTXOs)
|
|
29
29
|
/// - The input UTXOs and output UTXOs are valid in terms of obeying mass conservation rules
|
|
30
30
|
///
|
|
31
|
-
/// Inherits {ZetoLockable} so the
|
|
32
|
-
/// cancel lock lifecycle is shared with the fungible siblings via
|
|
31
|
+
/// Inherits {ZetoLockable} (linked {ZetoLockableLib}) so the
|
|
32
|
+
/// create/update/delegate/spend/cancel lock lifecycle is shared with the fungible siblings via
|
|
33
33
|
/// a single implementation. The two ZK-circuit-shaped hooks
|
|
34
34
|
/// ({_doLockTransition} and {_transferLocked}) are overridden here
|
|
35
35
|
/// to match the NF circuit's fixed 1-in/1-out shape — there is no
|
|
@@ -114,7 +114,7 @@ abstract contract ZetoNonFungible is ZetoLockable {
|
|
|
114
114
|
);
|
|
115
115
|
|
|
116
116
|
function _doLockTransition(
|
|
117
|
-
ZetoCreateLockArgs
|
|
117
|
+
ZetoCreateLockArgs calldata args
|
|
118
118
|
) internal override {
|
|
119
119
|
// NF createLock: spend exactly one unlocked input and produce
|
|
120
120
|
// exactly one locked output. There is no regular (unlocked)
|
|
@@ -152,19 +152,15 @@ abstract contract ZetoNonFungible is ZetoLockable {
|
|
|
152
152
|
|
|
153
153
|
processInputsAndOutputs(args.inputs, args.outputs, false);
|
|
154
154
|
processLockedOutputs(args.lockedOutputs);
|
|
155
|
-
// The freshly-locked output starts under the lock creator as
|
|
156
|
-
// both owner and spender; record that on the per-UTXO
|
|
157
|
-
// projection so {locked} can report it without a reverse lookup.
|
|
158
|
-
_setLockDelegates(args.lockedOutputs, msg.sender);
|
|
159
155
|
}
|
|
160
156
|
|
|
161
157
|
function _transferLocked(
|
|
162
158
|
bytes32 /* lockId */,
|
|
163
|
-
uint256[]
|
|
164
|
-
uint256[]
|
|
165
|
-
uint256[]
|
|
166
|
-
bytes
|
|
167
|
-
bytes
|
|
159
|
+
uint256[] calldata lockedInputs,
|
|
160
|
+
uint256[] calldata lockedOutputs,
|
|
161
|
+
uint256[] calldata outputs,
|
|
162
|
+
bytes calldata proof,
|
|
163
|
+
bytes calldata /* data */
|
|
168
164
|
) internal override {
|
|
169
165
|
// NF spend/cancel: consume exactly one locked input and produce
|
|
170
166
|
// exactly one output. The output may be either a regular
|
|
@@ -212,10 +208,6 @@ abstract contract ZetoNonFungible is ZetoLockable {
|
|
|
212
208
|
// unlocked path, once via the lockable path).
|
|
213
209
|
processInputsAndOutputs(lockedInputs, outputs, true);
|
|
214
210
|
processLockedOutputs(lockedOutputs);
|
|
215
|
-
// Any newly-locked output produced by this spend defaults to
|
|
216
|
-
// the current spender as its delegate. (When `lockedOutputs` is
|
|
217
|
-
// empty -- the common case -- this is a no-op.)
|
|
218
|
-
_setLockDelegates(lockedOutputs, msg.sender);
|
|
219
211
|
}
|
|
220
212
|
}
|
|
221
213
|
|
package/hardhat.config.ts
CHANGED
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
import { HardhatUserConfig, vars } from "hardhat/config";
|
|
18
18
|
import "@nomicfoundation/hardhat-toolbox";
|
|
19
19
|
import "@openzeppelin/hardhat-upgrades";
|
|
20
|
+
import "hardhat-contract-sizer";
|
|
20
21
|
import crypto from "crypto";
|
|
22
|
+
import { EIP170_EXEMPT_CONTRACTS } from "./config/eip170";
|
|
21
23
|
|
|
22
24
|
const keys = [
|
|
23
25
|
process.env.ETH_PRIVATE_KEY_1 || crypto.randomBytes(32).toString("hex"),
|
|
@@ -49,10 +51,17 @@ const config: HardhatUserConfig = {
|
|
|
49
51
|
paths: {
|
|
50
52
|
sources: "contracts"
|
|
51
53
|
},
|
|
54
|
+
contractSizer: {
|
|
55
|
+
alphaSort: true,
|
|
56
|
+
runOnCompile: true,
|
|
57
|
+
strict: true,
|
|
58
|
+
except: [...EIP170_EXEMPT_CONTRACTS],
|
|
59
|
+
},
|
|
52
60
|
networks: {
|
|
53
61
|
hardhat: {
|
|
54
|
-
//
|
|
55
|
-
//
|
|
62
|
+
// EIP-170 runtime limit is enforced for non-exempt tokens in deployZeto().
|
|
63
|
+
// Must be true here so exempt oversize implementations can deploy in tests:
|
|
64
|
+
// hardhat_reset does not reliably toggle allowUnlimitedContractSize (HH 2.26+).
|
|
56
65
|
allowUnlimitedContractSize: true,
|
|
57
66
|
},
|
|
58
67
|
besu: {
|
|
@@ -27,6 +27,11 @@ export const SmtLibModule = buildModule("SmtLib", (m) => {
|
|
|
27
27
|
return { smtLib, poseidon2, poseidon3, poseidon5, poseidon6 };
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
export const ZetoLockableLibModule = buildModule("ZetoLockableLib", (m) => {
|
|
31
|
+
const zetoLockableLib = m.library("ZetoLockableLib", []);
|
|
32
|
+
return { zetoLockableLib };
|
|
33
|
+
});
|
|
34
|
+
|
|
30
35
|
export const DepositVerifierModule = buildModule(
|
|
31
36
|
"Groth16Verifier_Deposit",
|
|
32
37
|
(m) => {
|
|
@@ -15,8 +15,14 @@
|
|
|
15
15
|
// limitations under the License.
|
|
16
16
|
|
|
17
17
|
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
|
18
|
+
import { ZetoLockableLibModule } from "../lib/deps";
|
|
18
19
|
|
|
19
20
|
export default buildModule("TenDecimals", (m) => {
|
|
20
|
-
const
|
|
21
|
+
const { zetoLockableLib } = m.useModule(ZetoLockableLibModule);
|
|
22
|
+
const tendecimals = m.contract("TenDecimals", [], {
|
|
23
|
+
libraries: {
|
|
24
|
+
ZetoLockableLib: zetoLockableLib,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
21
27
|
return { tendecimals };
|
|
22
28
|
});
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
DepositVerifierModule,
|
|
20
20
|
WithdrawVerifierModule,
|
|
21
21
|
BatchWithdrawVerifierModule,
|
|
22
|
+
ZetoLockableLibModule,
|
|
22
23
|
} from "./lib/deps";
|
|
23
24
|
|
|
24
25
|
const VerifierModule = buildModule("Groth16Verifier_Anon", (m) => {
|
|
@@ -32,6 +33,7 @@ const BatchVerifierModule = buildModule("Groth16Verifier_AnonBatch", (m) => {
|
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
export default buildModule("Zeto_Anon", (m) => {
|
|
36
|
+
const { zetoLockableLib } = m.useModule(ZetoLockableLibModule);
|
|
35
37
|
const { verifier } = m.useModule(VerifierModule);
|
|
36
38
|
const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
|
|
37
39
|
const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
|
|
@@ -45,5 +47,6 @@ export default buildModule("Zeto_Anon", (m) => {
|
|
|
45
47
|
verifier,
|
|
46
48
|
batchVerifier,
|
|
47
49
|
batchWithdrawVerifier,
|
|
50
|
+
zetoLockableLib,
|
|
48
51
|
};
|
|
49
52
|
});
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
DepositVerifierModule,
|
|
20
20
|
WithdrawVerifierModule,
|
|
21
21
|
BatchWithdrawVerifierModule,
|
|
22
|
+
ZetoLockableLibModule,
|
|
22
23
|
} from "./lib/deps";
|
|
23
24
|
|
|
24
25
|
const VerifierModule = buildModule("Groth16Verifier_Anon", (m) => {
|
|
@@ -45,6 +46,7 @@ const BatchBurnVerifierModule = buildModule(
|
|
|
45
46
|
);
|
|
46
47
|
|
|
47
48
|
export default buildModule("Zeto_AnonBurnable", (m) => {
|
|
49
|
+
const { zetoLockableLib } = m.useModule(ZetoLockableLibModule);
|
|
48
50
|
const { verifier } = m.useModule(VerifierModule);
|
|
49
51
|
const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
|
|
50
52
|
const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
|
|
@@ -62,5 +64,6 @@ export default buildModule("Zeto_AnonBurnable", (m) => {
|
|
|
62
64
|
batchVerifier,
|
|
63
65
|
batchWithdrawVerifier,
|
|
64
66
|
batchBurnVerifier,
|
|
67
|
+
zetoLockableLib,
|
|
65
68
|
};
|
|
66
69
|
});
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
DepositVerifierModule,
|
|
20
20
|
WithdrawVerifierModule,
|
|
21
21
|
BatchWithdrawVerifierModule,
|
|
22
|
+
ZetoLockableLibModule,
|
|
22
23
|
} from "./lib/deps";
|
|
23
24
|
|
|
24
25
|
const VerifierModule = buildModule("Groth16Verifier_AnonEnc", (m) => {
|
|
@@ -32,6 +33,7 @@ const BatchVerifierModule = buildModule("Groth16Verifier_AnonEncBatch", (m) => {
|
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
export default buildModule("Zeto_AnonEnc", (m) => {
|
|
36
|
+
const { zetoLockableLib } = m.useModule(ZetoLockableLibModule);
|
|
35
37
|
const { verifier } = m.useModule(VerifierModule);
|
|
36
38
|
const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
|
|
37
39
|
const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
|
|
@@ -45,5 +47,6 @@ export default buildModule("Zeto_AnonEnc", (m) => {
|
|
|
45
47
|
verifier,
|
|
46
48
|
batchVerifier,
|
|
47
49
|
batchWithdrawVerifier,
|
|
50
|
+
zetoLockableLib,
|
|
48
51
|
};
|
|
49
52
|
});
|