@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
|
@@ -16,128 +16,37 @@
|
|
|
16
16
|
pragma solidity ^0.8.27;
|
|
17
17
|
|
|
18
18
|
import {IZetoLockableCapability} from "./interfaces/IZetoLockableCapability.sol";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import {IZetoLockHooks} from "./interfaces/IZetoLockHooks.sol";
|
|
20
|
+
import {ILockableCapability} from "./interfaces/ILockableCapability.sol";
|
|
21
|
+
import {ZetoCommon} from "./zeto_common.sol";
|
|
22
|
+
import {ZetoLockableLib} from "./zeto_lockable_lib.sol";
|
|
21
23
|
|
|
22
24
|
/// @title ZetoLockable
|
|
23
25
|
/// @author Kaleido, Inc.
|
|
24
|
-
/// @dev
|
|
25
|
-
/// lifecycle
|
|
26
|
-
///
|
|
27
|
-
///
|
|
28
|
-
///
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
///
|
|
38
|
-
/// Descendants are responsible for the two ZK-circuit-specific
|
|
39
|
-
/// hooks {_doLockTransition} and {_transferLocked}, which
|
|
40
|
-
/// serialize a Zeto state transition into the public-inputs
|
|
41
|
-
/// shape expected by their concrete circuit family. Fungible
|
|
42
|
-
/// tokens pad inputs/outputs to the next supported batch size
|
|
43
|
-
/// via {ZetoCommon.checkAndPadCommitments}; non-fungible tokens
|
|
44
|
-
/// operate on fixed 1-in/1-out arrays. Keeping the hooks
|
|
45
|
-
/// virtual in this mixin lets both flavours share every other
|
|
46
|
-
/// moving piece of the lock lifecycle.
|
|
47
|
-
abstract contract ZetoLockable is ZetoCommon, IZetoLockableCapability {
|
|
48
|
-
/// @dev Lock state and replay protection live in {ZetoLockableStorage}
|
|
49
|
-
/// (ERC-7201 namespace `zeto.storage.ZetoLockable`).
|
|
50
|
-
|
|
51
|
-
modifier lockActive(bytes32 lockId) {
|
|
52
|
-
if (ZetoLockableStorage.layout().locks[lockId].owner == address(0)) {
|
|
53
|
-
revert LockNotActive(lockId);
|
|
54
|
-
}
|
|
55
|
-
_;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
modifier onlyLockOwner(bytes32 lockId) {
|
|
59
|
-
address owner = ZetoLockableStorage.layout().locks[lockId].owner;
|
|
60
|
-
if (owner != msg.sender) {
|
|
61
|
-
revert LockUnauthorized(lockId, owner, msg.sender);
|
|
26
|
+
/// @dev Thin embedding layer for {IZetoLockableCapability}. Shared lock
|
|
27
|
+
/// lifecycle logic lives in the linked external {ZetoLockableLib};
|
|
28
|
+
/// this contract forwards interface entry points to the library and
|
|
29
|
+
/// exposes {IZetoLockHooks} callbacks for circuit-specific work in
|
|
30
|
+
/// descendants ({ZetoFungible}, {ZetoNonFungible}).
|
|
31
|
+
abstract contract ZetoLockable is
|
|
32
|
+
ZetoCommon,
|
|
33
|
+
IZetoLockableCapability,
|
|
34
|
+
IZetoLockHooks
|
|
35
|
+
{
|
|
36
|
+
modifier onlySelf() {
|
|
37
|
+
if (msg.sender != address(this)) {
|
|
38
|
+
revert NotSelf(msg.sender);
|
|
62
39
|
}
|
|
63
40
|
_;
|
|
64
41
|
}
|
|
65
42
|
|
|
66
|
-
modifier onlyLockSpender(bytes32 lockId) {
|
|
67
|
-
address spender = ZetoLockableStorage.layout().locks[lockId].spender;
|
|
68
|
-
if (spender != msg.sender) {
|
|
69
|
-
revert LockUnauthorized(lockId, spender, msg.sender);
|
|
70
|
-
}
|
|
71
|
-
_;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function _useTxId(bytes32 txId) internal {
|
|
75
|
-
if (ZetoLockableStorage.layout().txIds[msg.sender][txId]) {
|
|
76
|
-
revert DuplicateTransaction(txId);
|
|
77
|
-
}
|
|
78
|
-
ZetoLockableStorage.layout().txIds[msg.sender][txId] = true;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/// @dev Project the lock-level spender onto each UTXO in `utxos`.
|
|
82
|
-
/// Skips zero-padding entries so it is safe to pass calldata
|
|
83
|
-
/// arrays straight from the lock payloads.
|
|
84
|
-
function _setLockDelegates(
|
|
85
|
-
uint256[] memory utxos,
|
|
86
|
-
address spender
|
|
87
|
-
) internal {
|
|
88
|
-
for (uint256 i = 0; i < utxos.length; ++i) {
|
|
89
|
-
if (utxos[i] != 0) {
|
|
90
|
-
ZetoLockableStorage.layout().utxoDelegates[utxos[i]] = spender;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/// @dev Wipe the per-UTXO spender projection for each UTXO in
|
|
96
|
-
/// `utxos`. Called when a lock's inputs are consumed (spend or
|
|
97
|
-
/// cancel) so that the public {locked} view returns
|
|
98
|
-
/// `(false, address(0))` for them immediately, even if some
|
|
99
|
-
/// future code path were to resurrect the locked-state entry.
|
|
100
|
-
function _clearLockDelegates(uint256[] memory utxos) internal {
|
|
101
|
-
for (uint256 i = 0; i < utxos.length; ++i) {
|
|
102
|
-
if (utxos[i] != 0) {
|
|
103
|
-
delete ZetoLockableStorage.layout().utxoDelegates[utxos[i]];
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ------------------------------------------------------------------
|
|
109
|
-
// ILockableCapability lifecycle
|
|
110
|
-
// ------------------------------------------------------------------
|
|
111
|
-
|
|
112
43
|
/// @inheritdoc IZetoLockableCapability
|
|
113
44
|
function computeLockId(
|
|
114
45
|
bytes calldata createArgs
|
|
115
46
|
) external view override returns (bytes32) {
|
|
116
|
-
|
|
117
|
-
createArgs,
|
|
118
|
-
(ZetoCreateLockArgs)
|
|
119
|
-
);
|
|
120
|
-
return _computeLockId(args.txId);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function _computeLockId(bytes32 txId) internal view returns (bytes32) {
|
|
124
|
-
return keccak256(abi.encode(address(this), msg.sender, txId));
|
|
47
|
+
return ZetoLockableLib.computeLockId(createArgs);
|
|
125
48
|
}
|
|
126
49
|
|
|
127
|
-
/// @dev Domain-separation tag for spend-intent commitments.
|
|
128
|
-
/// Included in {_buildUnlockHash} so that a payload hashing to
|
|
129
|
-
/// a lock's {ZetoLockInfo.spendCommitment} cannot also hash to
|
|
130
|
-
/// its {ZetoLockInfo.cancelCommitment}, preventing a spender
|
|
131
|
-
/// from transposing a spend payload into {cancelLock} (or vice
|
|
132
|
-
/// versa) when one of the commitments is non-zero.
|
|
133
|
-
bytes32 private constant _SPEND_HASH_DOMAIN =
|
|
134
|
-
keccak256("Zeto.spendCommitment.v1");
|
|
135
|
-
|
|
136
|
-
/// @dev Domain-separation tag for cancel-intent commitments. See
|
|
137
|
-
/// {_SPEND_HASH_DOMAIN} for the rationale.
|
|
138
|
-
bytes32 private constant _CANCEL_HASH_DOMAIN =
|
|
139
|
-
keccak256("Zeto.cancelCommitment.v1");
|
|
140
|
-
|
|
141
50
|
/// @inheritdoc IZetoLockableCapability
|
|
142
51
|
function computeSpendHash(
|
|
143
52
|
uint256[] calldata lockedInputs,
|
|
@@ -146,12 +55,11 @@ abstract contract ZetoLockable is ZetoCommon, IZetoLockableCapability {
|
|
|
146
55
|
bytes calldata data
|
|
147
56
|
) external pure override returns (bytes32) {
|
|
148
57
|
return
|
|
149
|
-
|
|
58
|
+
ZetoLockableLib.computeSpendHash(
|
|
150
59
|
lockedInputs,
|
|
151
60
|
lockedOutputs,
|
|
152
61
|
outputs,
|
|
153
|
-
data
|
|
154
|
-
_SPEND_HASH_DOMAIN
|
|
62
|
+
data
|
|
155
63
|
);
|
|
156
64
|
}
|
|
157
65
|
|
|
@@ -163,451 +71,149 @@ abstract contract ZetoLockable is ZetoCommon, IZetoLockableCapability {
|
|
|
163
71
|
bytes calldata data
|
|
164
72
|
) external pure override returns (bytes32) {
|
|
165
73
|
return
|
|
166
|
-
|
|
74
|
+
ZetoLockableLib.computeCancelHash(
|
|
167
75
|
lockedInputs,
|
|
168
76
|
lockedOutputs,
|
|
169
77
|
outputs,
|
|
170
|
-
data
|
|
171
|
-
_CANCEL_HASH_DOMAIN
|
|
78
|
+
data
|
|
172
79
|
);
|
|
173
80
|
}
|
|
174
81
|
|
|
175
|
-
|
|
176
|
-
* @dev Create a new lock by spending unlocked inputs and producing
|
|
177
|
-
* the locked content. See {ILockableCapability.createLock} for
|
|
178
|
-
* the generic semantics; the Zeto-specific payload is
|
|
179
|
-
* {ZetoCreateLockArgs}.
|
|
180
|
-
*
|
|
181
|
-
* Emits {LockCreated} (generic) and {ZetoLockCreated}.
|
|
182
|
-
*/
|
|
82
|
+
/// @inheritdoc ILockableCapability
|
|
183
83
|
function createLock(
|
|
184
84
|
bytes calldata createArgs,
|
|
185
85
|
bytes32 spendCommitment,
|
|
186
86
|
bytes32 cancelCommitment,
|
|
187
87
|
bytes calldata data
|
|
188
88
|
) external override returns (bytes32) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
revert DuplicateLock(lockId);
|
|
197
|
-
}
|
|
198
|
-
_useTxId(args.txId);
|
|
199
|
-
|
|
200
|
-
_doLockTransition(args);
|
|
201
|
-
|
|
202
|
-
ZetoLockableStorage.ZetoLockInfo storage lock = ZetoLockableStorage
|
|
203
|
-
.layout()
|
|
204
|
-
.locks[lockId];
|
|
205
|
-
lock.owner = msg.sender;
|
|
206
|
-
lock.spender = msg.sender;
|
|
207
|
-
lock.spendCommitment = spendCommitment;
|
|
208
|
-
lock.cancelCommitment = cancelCommitment;
|
|
209
|
-
lock.lockedInputs = args.lockedOutputs;
|
|
210
|
-
|
|
211
|
-
emit LockCreated(
|
|
212
|
-
lockId,
|
|
213
|
-
msg.sender,
|
|
214
|
-
msg.sender,
|
|
215
|
-
spendCommitment,
|
|
216
|
-
cancelCommitment,
|
|
217
|
-
data
|
|
218
|
-
);
|
|
219
|
-
emit ZetoLockCreated(
|
|
220
|
-
args.txId,
|
|
221
|
-
lockId,
|
|
222
|
-
msg.sender,
|
|
223
|
-
args.inputs,
|
|
224
|
-
args.outputs,
|
|
225
|
-
args.lockedOutputs,
|
|
226
|
-
args.proof,
|
|
227
|
-
data
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
return lockId;
|
|
89
|
+
return
|
|
90
|
+
ZetoLockableLib.createLock(
|
|
91
|
+
createArgs,
|
|
92
|
+
spendCommitment,
|
|
93
|
+
cancelCommitment,
|
|
94
|
+
data
|
|
95
|
+
);
|
|
231
96
|
}
|
|
232
97
|
|
|
233
|
-
|
|
234
|
-
* @dev Update the spend/cancel commitments on an active,
|
|
235
|
-
* owner-controlled lock. Mirrors the previous prepareUnlock
|
|
236
|
-
* flow but with full-update semantics: both commitments are
|
|
237
|
-
* replaced atomically.
|
|
238
|
-
*
|
|
239
|
-
* Emits {LockUpdated} and {ZetoLockUpdated}.
|
|
240
|
-
*/
|
|
98
|
+
/// @inheritdoc ILockableCapability
|
|
241
99
|
function updateLock(
|
|
242
100
|
bytes32 lockId,
|
|
243
101
|
bytes calldata updateArgs,
|
|
244
102
|
bytes32 spendCommitment,
|
|
245
103
|
bytes32 cancelCommitment,
|
|
246
104
|
bytes calldata data
|
|
247
|
-
) external override
|
|
248
|
-
|
|
249
|
-
.layout()
|
|
250
|
-
.locks[lockId];
|
|
251
|
-
if (lock.spender != lock.owner) {
|
|
252
|
-
revert LockImmutable(lockId);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
ZetoUpdateLockArgs memory args = abi.decode(
|
|
256
|
-
updateArgs,
|
|
257
|
-
(ZetoUpdateLockArgs)
|
|
258
|
-
);
|
|
259
|
-
_useTxId(args.txId);
|
|
260
|
-
|
|
261
|
-
lock.spendCommitment = spendCommitment;
|
|
262
|
-
lock.cancelCommitment = cancelCommitment;
|
|
263
|
-
|
|
264
|
-
emit LockUpdated(
|
|
105
|
+
) external override {
|
|
106
|
+
ZetoLockableLib.updateLock(
|
|
265
107
|
lockId,
|
|
266
|
-
|
|
108
|
+
updateArgs,
|
|
267
109
|
spendCommitment,
|
|
268
110
|
cancelCommitment,
|
|
269
111
|
data
|
|
270
112
|
);
|
|
271
|
-
emit ZetoLockUpdated(args.txId, lockId, msg.sender, data);
|
|
272
113
|
}
|
|
273
114
|
|
|
274
|
-
|
|
275
|
-
* @dev Delegate spending authority for the lock. The per-UTXO
|
|
276
|
-
* spender projection used by the public {locked} view is also
|
|
277
|
-
* moved here so that subsequent observers see the new spender
|
|
278
|
-
* for every locked input belonging to this lock.
|
|
279
|
-
*
|
|
280
|
-
* Emits {LockDelegated} and {ZetoLockDelegated}.
|
|
281
|
-
*/
|
|
115
|
+
/// @inheritdoc ILockableCapability
|
|
282
116
|
function delegateLock(
|
|
283
117
|
bytes32 lockId,
|
|
284
118
|
bytes calldata delegateArgs,
|
|
285
119
|
address newSpender,
|
|
286
120
|
bytes calldata data
|
|
287
|
-
) external override
|
|
288
|
-
|
|
289
|
-
delegateArgs,
|
|
290
|
-
(ZetoDelegateLockArgs)
|
|
291
|
-
);
|
|
292
|
-
_useTxId(args.txId);
|
|
293
|
-
|
|
294
|
-
ZetoLockableStorage.ZetoLockInfo storage lock = ZetoLockableStorage
|
|
295
|
-
.layout()
|
|
296
|
-
.locks[lockId];
|
|
297
|
-
address previousSpender = lock.spender;
|
|
298
|
-
lock.spender = newSpender;
|
|
299
|
-
|
|
300
|
-
_setLockDelegates(lock.lockedInputs, newSpender);
|
|
301
|
-
|
|
302
|
-
emit LockDelegated(lockId, previousSpender, newSpender, data);
|
|
303
|
-
emit ZetoLockDelegated(
|
|
304
|
-
args.txId,
|
|
121
|
+
) external override {
|
|
122
|
+
ZetoLockableLib.delegateLock(
|
|
305
123
|
lockId,
|
|
306
|
-
|
|
124
|
+
delegateArgs,
|
|
307
125
|
newSpender,
|
|
308
126
|
data
|
|
309
127
|
);
|
|
310
128
|
}
|
|
311
129
|
|
|
312
|
-
|
|
313
|
-
* @dev Spend the lock by executing the committed unlock operation.
|
|
314
|
-
* If `spendCommitment` is non-zero, the supplied spendArgs MUST
|
|
315
|
-
* hash to it.
|
|
316
|
-
*
|
|
317
|
-
* Emits {LockSpent} and {ZetoLockSpent}.
|
|
318
|
-
*/
|
|
130
|
+
/// @inheritdoc ILockableCapability
|
|
319
131
|
function spendLock(
|
|
320
132
|
bytes32 lockId,
|
|
321
133
|
bytes calldata spendArgs,
|
|
322
134
|
bytes calldata data
|
|
323
|
-
) external
|
|
324
|
-
|
|
325
|
-
spendArgs,
|
|
326
|
-
(ZetoSpendLockArgs)
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
// Snapshot the locked content from storage. The caller is
|
|
330
|
-
// intentionally not allowed to substitute a different set of
|
|
331
|
-
// locked inputs.
|
|
332
|
-
uint256[] memory lockedInputs = ZetoLockableStorage
|
|
333
|
-
.layout()
|
|
334
|
-
.locks[lockId]
|
|
335
|
-
.lockedInputs;
|
|
336
|
-
bytes32 expectedHash = ZetoLockableStorage
|
|
337
|
-
.layout()
|
|
338
|
-
.locks[lockId]
|
|
339
|
-
.spendCommitment;
|
|
340
|
-
_consumeLock(
|
|
341
|
-
lockId,
|
|
342
|
-
lockedInputs,
|
|
343
|
-
expectedHash,
|
|
344
|
-
_SPEND_HASH_DOMAIN,
|
|
345
|
-
args
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
emit LockSpent(lockId, msg.sender, data);
|
|
349
|
-
emit ZetoLockSpent(
|
|
350
|
-
args.txId,
|
|
351
|
-
lockId,
|
|
352
|
-
msg.sender,
|
|
353
|
-
lockedInputs,
|
|
354
|
-
args.lockedOutputs,
|
|
355
|
-
args.outputs,
|
|
356
|
-
args.proof,
|
|
357
|
-
data
|
|
358
|
-
);
|
|
135
|
+
) external override {
|
|
136
|
+
ZetoLockableLib.spendLock(lockId, spendArgs, data);
|
|
359
137
|
}
|
|
360
138
|
|
|
361
|
-
|
|
362
|
-
* @dev Cancel the lock by executing the committed cancellation
|
|
363
|
-
* operation. If `cancelCommitment` is non-zero, the supplied
|
|
364
|
-
* cancelArgs MUST hash to it.
|
|
365
|
-
*
|
|
366
|
-
* Emits {LockCancelled} and {ZetoLockCancelled}.
|
|
367
|
-
*/
|
|
139
|
+
/// @inheritdoc ILockableCapability
|
|
368
140
|
function cancelLock(
|
|
369
141
|
bytes32 lockId,
|
|
370
142
|
bytes calldata cancelArgs,
|
|
371
143
|
bytes calldata data
|
|
372
|
-
) external
|
|
373
|
-
|
|
374
|
-
cancelArgs,
|
|
375
|
-
(ZetoSpendLockArgs)
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
uint256[] memory lockedInputs = ZetoLockableStorage
|
|
379
|
-
.layout()
|
|
380
|
-
.locks[lockId]
|
|
381
|
-
.lockedInputs;
|
|
382
|
-
bytes32 expectedHash = ZetoLockableStorage
|
|
383
|
-
.layout()
|
|
384
|
-
.locks[lockId]
|
|
385
|
-
.cancelCommitment;
|
|
386
|
-
_consumeLock(
|
|
387
|
-
lockId,
|
|
388
|
-
lockedInputs,
|
|
389
|
-
expectedHash,
|
|
390
|
-
_CANCEL_HASH_DOMAIN,
|
|
391
|
-
args
|
|
392
|
-
);
|
|
393
|
-
|
|
394
|
-
emit LockCancelled(lockId, msg.sender, data);
|
|
395
|
-
emit ZetoLockCancelled(
|
|
396
|
-
args.txId,
|
|
397
|
-
lockId,
|
|
398
|
-
msg.sender,
|
|
399
|
-
lockedInputs,
|
|
400
|
-
args.lockedOutputs,
|
|
401
|
-
args.outputs,
|
|
402
|
-
args.proof,
|
|
403
|
-
data
|
|
404
|
-
);
|
|
144
|
+
) external override {
|
|
145
|
+
ZetoLockableLib.cancelLock(lockId, cancelArgs, data);
|
|
405
146
|
}
|
|
406
147
|
|
|
407
|
-
|
|
408
|
-
// ILockableCapability views
|
|
409
|
-
// ------------------------------------------------------------------
|
|
410
|
-
|
|
148
|
+
/// @inheritdoc ILockableCapability
|
|
411
149
|
function getLock(
|
|
412
150
|
bytes32 lockId
|
|
413
|
-
) external view override
|
|
414
|
-
|
|
415
|
-
.layout()
|
|
416
|
-
.locks[lockId];
|
|
417
|
-
return
|
|
418
|
-
LockInfo({
|
|
419
|
-
owner: lock.owner,
|
|
420
|
-
spender: lock.spender,
|
|
421
|
-
spendCommitment: lock.spendCommitment,
|
|
422
|
-
cancelCommitment: lock.cancelCommitment
|
|
423
|
-
});
|
|
151
|
+
) external view override returns (LockInfo memory) {
|
|
152
|
+
return ZetoLockableLib.getLock(lockId);
|
|
424
153
|
}
|
|
425
154
|
|
|
155
|
+
/// @inheritdoc ILockableCapability
|
|
426
156
|
function isLockActive(
|
|
427
157
|
bytes32 lockId
|
|
428
158
|
) external view override returns (bool) {
|
|
429
|
-
return
|
|
159
|
+
return ZetoLockableLib.isLockActive(lockId);
|
|
430
160
|
}
|
|
431
161
|
|
|
162
|
+
/// @inheritdoc ILockableCapability
|
|
432
163
|
function getLockContent(
|
|
433
164
|
bytes32 lockId
|
|
434
|
-
) external view override
|
|
435
|
-
return
|
|
436
|
-
abi.encode(ZetoLockableStorage.layout().locks[lockId].lockedInputs);
|
|
165
|
+
) external view override returns (bytes memory content) {
|
|
166
|
+
return ZetoLockableLib.getLockContent(lockId);
|
|
437
167
|
}
|
|
438
168
|
|
|
439
169
|
/// @inheritdoc IZetoLockableCapability
|
|
440
170
|
function getLockedInputs(
|
|
441
171
|
bytes32 lockId
|
|
442
|
-
)
|
|
443
|
-
|
|
444
|
-
view
|
|
445
|
-
override
|
|
446
|
-
lockActive(lockId)
|
|
447
|
-
returns (uint256[] memory lockedInputs)
|
|
448
|
-
{
|
|
449
|
-
return ZetoLockableStorage.layout().locks[lockId].lockedInputs;
|
|
172
|
+
) external view override returns (uint256[] memory lockedInputs) {
|
|
173
|
+
return ZetoLockableLib.getLockedInputs(lockId);
|
|
450
174
|
}
|
|
451
175
|
|
|
452
|
-
|
|
453
|
-
* @dev Override of {ZetoCommon.locked} that fills in the per-UTXO
|
|
454
|
-
* spender from the {_utxoDelegates} projection. The storage
|
|
455
|
-
* layer only tells us "is this locked"; the spender lives at
|
|
456
|
-
* this layer because the lock model itself does.
|
|
457
|
-
*
|
|
458
|
-
* Returns `(false, address(0))` for any UTXO that is not
|
|
459
|
-
* currently locked-unspent, including UTXOs whose lock has
|
|
460
|
-
* been spent or cancelled (since both the storage layer's
|
|
461
|
-
* locked-set is cleared and `_utxoDelegates[X]` is wiped in
|
|
462
|
-
* the consume path).
|
|
463
|
-
*/
|
|
176
|
+
/// @inheritdoc ZetoCommon
|
|
464
177
|
function locked(uint256 utxo) public view override returns (bool, address) {
|
|
465
|
-
|
|
466
|
-
return (false, address(0));
|
|
467
|
-
}
|
|
468
|
-
return (true, ZetoLockableStorage.layout().utxoDelegates[utxo]);
|
|
178
|
+
return ZetoLockableLib.locked(utxo);
|
|
469
179
|
}
|
|
470
180
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
* apply the resulting state transition. Implementations differ
|
|
478
|
-
* between fungible (padding to the next supported batch size)
|
|
479
|
-
* and non-fungible (fixed 1-in/1-out) tokens, so this hook is
|
|
480
|
-
* left abstract. They MUST:
|
|
481
|
-
*
|
|
482
|
-
* 1. {validateTransactionProposal}(args.inputs, args.outputs,
|
|
483
|
-
* args.lockedOutputs, args.proof, false).
|
|
484
|
-
* 2. {constructPublicInputs}(...) and
|
|
485
|
-
* {verifyProof}(...) for the unlocked-inputs flavour.
|
|
486
|
-
* 3. {processInputsAndOutputs}(args.inputs, args.outputs,
|
|
487
|
-
* false).
|
|
488
|
-
* 4. {processLockedOutputs}(args.lockedOutputs).
|
|
489
|
-
* 5. {_setLockDelegates}(args.lockedOutputs, msg.sender).
|
|
490
|
-
*/
|
|
491
|
-
function _doLockTransition(ZetoCreateLockArgs memory args) internal virtual;
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* @dev Apply the ZK-verified state transition that consumes a
|
|
495
|
-
* lock's `lockedInputs`. See {_doLockTransition} for the
|
|
496
|
-
* rationale for keeping this abstract.
|
|
497
|
-
*/
|
|
498
|
-
function _transferLocked(
|
|
499
|
-
bytes32 lockId,
|
|
500
|
-
uint256[] memory lockedInputs,
|
|
501
|
-
uint256[] memory lockedOutputs,
|
|
502
|
-
uint256[] memory outputs,
|
|
503
|
-
bytes memory proof,
|
|
504
|
-
bytes memory data
|
|
505
|
-
) internal virtual;
|
|
181
|
+
/// @inheritdoc IZetoLockHooks
|
|
182
|
+
function zetoLockDoLockTransition(
|
|
183
|
+
ZetoCreateLockArgs calldata args
|
|
184
|
+
) external override onlySelf {
|
|
185
|
+
_doLockTransition(args);
|
|
186
|
+
}
|
|
506
187
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
*
|
|
510
|
-
* `lockedInputs` MUST be the lock's storage-pinned content
|
|
511
|
-
* (read by the caller from `_locks[lockId].lockedInputs`). The
|
|
512
|
-
* spender does not get to choose which UTXOs are consumed: that
|
|
513
|
-
* decision was made at {createLock} time. This is what makes
|
|
514
|
-
* "spending lock A consumes lock A's UTXOs" a hard contract
|
|
515
|
-
* invariant.
|
|
516
|
-
*/
|
|
517
|
-
function _consumeLock(
|
|
188
|
+
/// @inheritdoc IZetoLockHooks
|
|
189
|
+
function zetoLockTransferLocked(
|
|
518
190
|
bytes32 lockId,
|
|
519
|
-
uint256[]
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (expectedHash != 0) {
|
|
527
|
-
bytes32 actualHash = _buildUnlockHash(
|
|
528
|
-
lockedInputs,
|
|
529
|
-
args.lockedOutputs,
|
|
530
|
-
args.outputs,
|
|
531
|
-
args.data,
|
|
532
|
-
hashDomain
|
|
533
|
-
);
|
|
534
|
-
if (actualHash != expectedHash) {
|
|
535
|
-
revert InvalidUnlockHash(expectedHash, actualHash);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Tear down all lock-spender state BEFORE performing the state
|
|
540
|
-
// transition, so any reentrant lookup observes both
|
|
541
|
-
// `_locks[lockId]` and the per-UTXO `_utxoDelegates` projection
|
|
542
|
-
// as cleared.
|
|
543
|
-
_clearLockDelegates(lockedInputs);
|
|
544
|
-
delete ZetoLockableStorage.layout().locks[lockId];
|
|
545
|
-
|
|
191
|
+
uint256[] calldata lockedInputs,
|
|
192
|
+
uint256[] calldata lockedOutputs,
|
|
193
|
+
uint256[] calldata outputs,
|
|
194
|
+
bytes calldata proof,
|
|
195
|
+
bytes calldata data
|
|
196
|
+
) external override onlySelf {
|
|
546
197
|
_transferLocked(
|
|
547
198
|
lockId,
|
|
548
199
|
lockedInputs,
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
200
|
+
lockedOutputs,
|
|
201
|
+
outputs,
|
|
202
|
+
proof,
|
|
203
|
+
data
|
|
553
204
|
);
|
|
554
205
|
}
|
|
555
206
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
/// separation tag (see {_SPEND_HASH_DOMAIN} /
|
|
560
|
-
/// {_CANCEL_HASH_DOMAIN}) so that the two commitments live in
|
|
561
|
-
/// disjoint hash spaces.
|
|
562
|
-
///
|
|
563
|
-
/// Each variable-length component is hashed as
|
|
564
|
-
/// `abi.encode(...)` rather than `abi.encodePacked(...)` to
|
|
565
|
-
/// avoid the cross-array length-ambiguity that `encodePacked`
|
|
566
|
-
/// introduces for dynamic arguments (e.g. `[1, 2]` and `[12]`
|
|
567
|
-
/// collide under `encodePacked` but not under `encode`).
|
|
568
|
-
function _buildUnlockHash(
|
|
569
|
-
uint256[] memory lockedInputs,
|
|
570
|
-
uint256[] memory lockedOutputs,
|
|
571
|
-
uint256[] memory outputs,
|
|
572
|
-
bytes memory data,
|
|
573
|
-
bytes32 domain
|
|
574
|
-
) internal pure returns (bytes32) {
|
|
575
|
-
return
|
|
576
|
-
keccak256(
|
|
577
|
-
abi.encode(
|
|
578
|
-
domain,
|
|
579
|
-
keccak256(abi.encode(lockedInputs)),
|
|
580
|
-
keccak256(abi.encode(lockedOutputs)),
|
|
581
|
-
keccak256(abi.encode(outputs)),
|
|
582
|
-
keccak256(data)
|
|
583
|
-
)
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/// @dev ERC-7201 (`erc7201:zeto.storage.ZetoLockable`): lock lifecycle state for
|
|
589
|
-
/// this abstract contract.
|
|
590
|
-
library ZetoLockableStorage {
|
|
591
|
-
struct ZetoLockInfo {
|
|
592
|
-
address owner;
|
|
593
|
-
address spender;
|
|
594
|
-
bytes32 spendCommitment;
|
|
595
|
-
bytes32 cancelCommitment;
|
|
596
|
-
uint256[] lockedInputs;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
struct Layout {
|
|
600
|
-
mapping(bytes32 => ZetoLockInfo) locks;
|
|
601
|
-
mapping(address => mapping(bytes32 => bool)) txIds;
|
|
602
|
-
mapping(uint256 => address) utxoDelegates;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
bytes32 private constant STORAGE_LOCATION =
|
|
606
|
-
0x6f84b5947db308f6274c3bdf3450b8e85913b258c3b2e7abbddf0986236a4900;
|
|
207
|
+
function _doLockTransition(
|
|
208
|
+
ZetoCreateLockArgs calldata args
|
|
209
|
+
) internal virtual;
|
|
607
210
|
|
|
608
|
-
function
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
211
|
+
function _transferLocked(
|
|
212
|
+
bytes32 lockId,
|
|
213
|
+
uint256[] calldata lockedInputs,
|
|
214
|
+
uint256[] calldata lockedOutputs,
|
|
215
|
+
uint256[] calldata outputs,
|
|
216
|
+
bytes calldata proof,
|
|
217
|
+
bytes calldata data
|
|
218
|
+
) internal virtual;
|
|
613
219
|
}
|