@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.
Files changed (50) hide show
  1. package/README.md +6 -0
  2. package/config/eip170.ts +30 -0
  3. package/contracts/lib/interfaces/IZetoLockHooks.sol +42 -0
  4. package/contracts/lib/zeto_fungible.sol +9 -17
  5. package/contracts/lib/zeto_lockable.sol +85 -479
  6. package/contracts/lib/zeto_lockable_lib.sol +460 -0
  7. package/contracts/lib/zeto_lockable_storage.sol +43 -0
  8. package/contracts/lib/zeto_non_fungible.sol +8 -16
  9. package/hardhat.config.ts +11 -2
  10. package/ignition/modules/lib/deps.ts +5 -0
  11. package/ignition/modules/test/tendecimals.ts +7 -1
  12. package/ignition/modules/zeto_anon.ts +3 -0
  13. package/ignition/modules/zeto_anon_burnable.ts +3 -0
  14. package/ignition/modules/zeto_anon_enc.ts +3 -0
  15. package/ignition/modules/zeto_anon_enc_nullifier.ts +3 -0
  16. package/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +3 -0
  17. package/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts +3 -0
  18. package/ignition/modules/zeto_anon_nullifier.ts +3 -0
  19. package/ignition/modules/zeto_anon_nullifier_burnable.ts +3 -0
  20. package/ignition/modules/zeto_anon_nullifier_kyc.ts +3 -0
  21. package/ignition/modules/zeto_anon_nullifier_qurrency.ts +3 -0
  22. package/ignition/modules/zeto_nf_anon.ts +3 -1
  23. package/ignition/modules/zeto_nf_anon_nullifier.ts +3 -2
  24. package/package.json +4 -2
  25. package/scripts/lib/zeto_libraries.ts +47 -0
  26. package/scripts/tokens/Zeto_Anon.ts +3 -0
  27. package/scripts/tokens/Zeto_AnonBurnable.ts +3 -0
  28. package/scripts/tokens/Zeto_AnonEnc.ts +3 -0
  29. package/scripts/tokens/Zeto_AnonEncNullifier.ts +6 -5
  30. package/scripts/tokens/Zeto_AnonEncNullifierKyc.ts +6 -5
  31. package/scripts/tokens/Zeto_AnonEncNullifierNonRepudiation.ts +6 -5
  32. package/scripts/tokens/Zeto_AnonNullifier.ts +6 -5
  33. package/scripts/tokens/Zeto_AnonNullifierBurnable.ts +6 -5
  34. package/scripts/tokens/Zeto_AnonNullifierKyc.ts +6 -5
  35. package/scripts/tokens/Zeto_AnonNullifierQurrency.ts +6 -7
  36. package/scripts/tokens/Zeto_NfAnon.ts +3 -1
  37. package/scripts/tokens/Zeto_NfAnonNullifier.ts +6 -6
  38. package/test/factory.ts +10 -1
  39. package/test/lib/deploy.ts +2 -0
  40. package/test/lib/eip170.ts +23 -0
  41. package/test/usdc-shielding.ts +0 -1
  42. package/test/zeto_anon_enc.ts +0 -1
  43. package/test/zeto_anon_enc_nullifier.ts +3 -1
  44. package/test/zeto_anon_enc_nullifier_kyc.ts +3 -1
  45. package/test/zeto_anon_enc_nullifier_non_repudiation.ts +3 -1
  46. package/test/zeto_anon_nullifier.ts +0 -1
  47. package/test/zeto_anon_nullifier_kyc.ts +3 -1
  48. package/test/zeto_anon_nullifier_qurrency.ts +3 -1
  49. package/test/zeto_nf_anon.ts +0 -1
  50. 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 create/update/delegate/spend/
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 memory args
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[] memory lockedInputs,
164
- uint256[] memory lockedOutputs,
165
- uint256[] memory outputs,
166
- bytes memory proof,
167
- bytes memory /* data */
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
- // a small number of verifiers are bigger than the default 24576 bytes
55
- // so we need to allow unlimited contract size to avoid test errors
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 tendecimals = m.contract("TenDecimals", []);
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
  });