@lfdecentralizedtrust/zeto-contracts 0.2.2 → 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 +76 -162
- package/config/eip170.ts +30 -0
- package/contracts/factory.sol +30 -34
- package/contracts/factory_upgradeable.sol +11 -7
- package/contracts/lib/common/util.sol +40 -0
- package/contracts/lib/interfaces/ILockableCapability.sol +318 -0
- package/contracts/lib/interfaces/{izeto.sol → IZeto.sol} +13 -1
- package/contracts/lib/interfaces/{izeto_initializable.sol → IZetoInitializable.sol} +14 -12
- package/contracts/lib/interfaces/{izeto_kyc.sol → IZetoKyc.sol} +3 -3
- package/contracts/lib/interfaces/IZetoLockHooks.sol +42 -0
- package/contracts/lib/interfaces/IZetoLockableCapability.sol +237 -0
- package/contracts/lib/interfaces/IZetoStorage.sol +106 -0
- package/contracts/lib/interfaces/{izeto_lockable.sol → IZetoVerifier.sol} +8 -21
- package/contracts/lib/registry.sol +74 -26
- package/contracts/lib/storage/base.sol +210 -0
- package/contracts/lib/storage/nullifier.sol +166 -0
- package/contracts/lib/zeto_common.sol +277 -28
- package/contracts/lib/zeto_fungible.sol +443 -33
- package/contracts/lib/zeto_fungible_base.sol +69 -0
- package/contracts/lib/zeto_fungible_burn.sol +83 -53
- package/contracts/lib/zeto_fungible_burn_nullifier.sol +115 -72
- package/contracts/lib/zeto_fungible_nullifier.sol +167 -0
- package/contracts/lib/zeto_lockable.sol +219 -0
- 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 +228 -0
- package/contracts/lib/zeto_non_fungible_base.sol +61 -0
- package/contracts/lib/zeto_non_fungible_nullifier.sol +61 -0
- package/contracts/test/escrow1.sol +90 -34
- package/contracts/test/escrow2.sol +64 -29
- package/contracts/test/qurrency.sol +10 -2
- package/contracts/test/smt.sol +6 -1
- package/contracts/test/tendecimals.sol +14 -12
- package/contracts/verifiers/impl/anon.sol +189 -0
- package/contracts/verifiers/impl/anon_batch.sol +301 -0
- package/contracts/verifiers/impl/anon_enc.sol +266 -0
- package/contracts/verifiers/impl/anon_enc_batch.sol +602 -0
- package/contracts/verifiers/impl/anon_enc_nullifier.sol +287 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_batch.sol +679 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_kyc.sol +294 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_kyc_batch.sol +686 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation.sol +413 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation_batch.sol +1141 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transfer.sol +217 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked.sol +196 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked_batch.sol +308 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transfer_batch.sol +385 -0
- package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer.sol +497 -0
- package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer_batch.sol +1001 -0
- package/contracts/verifiers/{verifier_anon_nullifier_transferLocked.sol → impl/anon_nullifier_transfer.sol} +12 -19
- package/contracts/verifiers/{verifier_anon_nullifier_transferLocked_batch.sol → impl/anon_nullifier_transfer_batch.sol} +44 -51
- package/contracts/verifiers/impl/burn.sol +182 -0
- package/contracts/verifiers/impl/burn_batch.sol +238 -0
- package/contracts/verifiers/impl/burn_nullifier.sol +203 -0
- package/contracts/verifiers/impl/burn_nullifier_batch.sol +315 -0
- package/contracts/verifiers/impl/deposit.sol +182 -0
- package/contracts/verifiers/impl/deposit_kyc.sol +189 -0
- package/contracts/verifiers/impl/nf_anon.sol +175 -0
- package/contracts/verifiers/{verifier_nf_anon_nullifier_transferLocked.sol → impl/nf_anon_nullifier_transfer.sol} +6 -13
- package/contracts/verifiers/impl/withdraw.sol +189 -0
- package/contracts/verifiers/impl/withdraw_batch.sol +245 -0
- package/contracts/verifiers/impl/withdraw_nullifier.sol +210 -0
- package/contracts/verifiers/impl/withdraw_nullifier_batch.sol +322 -0
- package/contracts/verifiers/verifier_anon.sol +31 -186
- package/contracts/verifiers/verifier_anon_batch.sol +31 -298
- package/contracts/verifiers/verifier_anon_enc.sol +31 -263
- package/contracts/verifiers/verifier_anon_enc_batch.sol +31 -599
- package/contracts/verifiers/verifier_anon_enc_nullifier.sol +31 -284
- package/contracts/verifiers/verifier_anon_enc_nullifier_batch.sol +33 -676
- package/contracts/verifiers/verifier_anon_enc_nullifier_kyc.sol +31 -291
- package/contracts/verifiers/verifier_anon_enc_nullifier_kyc_batch.sol +33 -683
- package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation.sol +33 -410
- package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation_batch.sol +33 -1138
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer.sol +33 -214
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked.sol +33 -221
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked_batch.sol +33 -389
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer_batch.sol +33 -382
- package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer.sol +33 -221
- package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer_batch.sol +33 -389
- package/contracts/verifiers/verifier_anon_nullifier_transfer.sol +33 -207
- package/contracts/verifiers/verifier_anon_nullifier_transfer_batch.sol +33 -375
- package/contracts/verifiers/verifier_burn.sol +31 -179
- package/contracts/verifiers/verifier_burn_batch.sol +31 -235
- package/contracts/verifiers/verifier_burn_nullifier.sol +31 -200
- package/contracts/verifiers/verifier_burn_nullifier_batch.sol +31 -312
- package/contracts/verifiers/verifier_deposit.sol +31 -179
- package/contracts/verifiers/verifier_deposit_kyc.sol +34 -0
- package/contracts/verifiers/verifier_nf_anon.sol +31 -172
- package/contracts/verifiers/verifier_nf_anon_nullifier_transfer.sol +33 -179
- package/contracts/verifiers/verifier_withdraw.sol +31 -186
- package/contracts/verifiers/verifier_withdraw_batch.sol +31 -242
- package/contracts/verifiers/verifier_withdraw_nullifier.sol +31 -207
- package/contracts/verifiers/verifier_withdraw_nullifier_batch.sol +33 -319
- package/contracts/zeto_anon.sol +77 -231
- package/contracts/zeto_anon_burnable.sol +56 -12
- package/contracts/zeto_anon_enc.sol +93 -190
- package/contracts/zeto_anon_enc_nullifier.sol +249 -195
- package/contracts/zeto_anon_enc_nullifier_kyc.sol +51 -232
- package/contracts/zeto_anon_enc_nullifier_non_repudiation.sol +231 -238
- package/contracts/zeto_anon_nullifier.sol +164 -298
- package/contracts/zeto_anon_nullifier_burnable.sol +68 -18
- package/contracts/zeto_anon_nullifier_kyc.sol +40 -345
- package/contracts/zeto_anon_nullifier_qurrency.sol +217 -361
- package/contracts/zeto_nf_anon.sol +55 -130
- package/contracts/zeto_nf_anon_nullifier.sol +90 -152
- package/hardhat.config.ts +18 -3
- package/ignition/modules/lib/deps.ts +13 -0
- package/ignition/modules/test/tendecimals.ts +7 -1
- package/ignition/modules/zeto_anon.ts +3 -0
- package/ignition/modules/zeto_anon_burnable.ts +11 -7
- package/ignition/modules/zeto_anon_enc.ts +3 -0
- package/ignition/modules/zeto_anon_enc_nullifier.ts +34 -0
- package/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +5 -2
- package/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts +3 -0
- package/ignition/modules/zeto_anon_nullifier.ts +31 -4
- package/ignition/modules/zeto_anon_nullifier_burnable.ts +53 -16
- package/ignition/modules/zeto_anon_nullifier_kyc.ts +30 -12
- 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 +17 -12
- package/package.json +7 -3
- package/scripts/deploy_cloneable.ts +19 -10
- package/scripts/deploy_upgradeable.ts +9 -5
- package/scripts/lib/zeto_libraries.ts +47 -0
- package/scripts/tokens/Zeto_Anon.ts +6 -3
- package/scripts/tokens/Zeto_AnonBurnable.ts +4 -1
- package/scripts/tokens/Zeto_AnonEnc.ts +15 -3
- package/scripts/tokens/Zeto_AnonEncNullifier.ts +11 -8
- package/scripts/tokens/Zeto_AnonEncNullifierKyc.ts +7 -6
- package/scripts/tokens/Zeto_AnonEncNullifierNonRepudiation.ts +7 -6
- package/scripts/tokens/Zeto_AnonNullifier.ts +7 -6
- package/scripts/tokens/Zeto_AnonNullifierBurnable.ts +7 -6
- package/scripts/tokens/Zeto_AnonNullifierKyc.ts +7 -6
- package/scripts/tokens/Zeto_AnonNullifierQurrency.ts +7 -8
- package/scripts/tokens/Zeto_NfAnon.ts +5 -3
- package/scripts/tokens/Zeto_NfAnonNullifier.ts +7 -7
- package/test/factory.ts +55 -73
- package/test/lib/anon_nullifier_helpers.ts +89 -0
- package/test/lib/anon_zeto_helpers.ts +76 -0
- package/test/lib/deploy.ts +5 -2
- package/test/lib/eip170.ts +23 -0
- package/test/lib/utils.ts +74 -35
- package/test/test/escrow1.ts +185 -58
- package/test/test/escrow2.ts +200 -107
- package/test/test/qurrency.ts +13 -33
- package/test/usdc-shielding.ts +39 -27
- package/test/utils.ts +144 -21
- package/test/zeto_anon.ts +956 -465
- package/test/zeto_anon_enc.ts +881 -143
- package/test/zeto_anon_enc_nullifier.ts +850 -39
- package/test/zeto_anon_enc_nullifier_kyc.ts +123 -182
- package/test/zeto_anon_enc_nullifier_non_repudiation.ts +80 -47
- package/test/zeto_anon_nullifier.ts +1212 -954
- package/test/zeto_anon_nullifier_kyc.ts +677 -656
- package/test/zeto_anon_nullifier_qurrency.ts +455 -307
- package/test/zeto_nf_anon.ts +737 -138
- package/test/zeto_nf_anon_nullifier.ts +876 -247
- package/contracts/lib/zeto_base.sol +0 -293
- package/contracts/lib/zeto_fungible_withdraw.sol +0 -132
- package/contracts/lib/zeto_fungible_withdraw_nullifier.sol +0 -144
- package/contracts/lib/zeto_nullifier.sol +0 -340
- package/contracts/zkDvP.sol_ +0 -273
- package/test/zkDvP.ts_ +0 -455
- /package/contracts/lib/{common.sol → common/common.sol} +0 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
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 {ZetoLockable} from "./zeto_lockable.sol";
|
|
19
|
+
import {Commonlib} from "./common/common.sol";
|
|
20
|
+
import {IZetoInitializable} from "./interfaces/IZetoInitializable.sol";
|
|
21
|
+
import {IZetoStorage} from "./interfaces/IZetoStorage.sol";
|
|
22
|
+
|
|
23
|
+
/// @title A sample base implementation of a Zeto based non-fungible
|
|
24
|
+
/// token contract.
|
|
25
|
+
/// @author Kaleido, Inc.
|
|
26
|
+
/// @dev The proof has the following statements:
|
|
27
|
+
/// - The sender owns the private key whose public key is part of the pre-image of the input UTXOs commitments
|
|
28
|
+
/// (aka the sender is authorized to spend the input UTXOs)
|
|
29
|
+
/// - The input UTXOs and output UTXOs are valid in terms of obeying mass conservation rules
|
|
30
|
+
///
|
|
31
|
+
/// Inherits {ZetoLockable} (linked {ZetoLockableLib}) so the
|
|
32
|
+
/// create/update/delegate/spend/cancel lock lifecycle is shared with the fungible siblings via
|
|
33
|
+
/// a single implementation. The two ZK-circuit-shaped hooks
|
|
34
|
+
/// ({_doLockTransition} and {_transferLocked}) are overridden here
|
|
35
|
+
/// to match the NF circuit's fixed 1-in/1-out shape — there is no
|
|
36
|
+
/// input/output padding and no batch verifier path.
|
|
37
|
+
abstract contract ZetoNonFungible is ZetoLockable {
|
|
38
|
+
function __ZetoNonFungible_init(
|
|
39
|
+
string calldata name_,
|
|
40
|
+
string calldata symbol_,
|
|
41
|
+
address initialOwner,
|
|
42
|
+
IZetoInitializable.VerifiersInfo calldata verifiers,
|
|
43
|
+
IZetoStorage storage_
|
|
44
|
+
) internal onlyInitializing {
|
|
45
|
+
__ZetoCommon_init(name_, symbol_, initialOwner, verifiers, storage_);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @dev Single-input/single-output transfer.
|
|
50
|
+
*
|
|
51
|
+
* @param input The UTXO to be spent by the transaction.
|
|
52
|
+
* @param output The new UTXO to generate, for future transactions
|
|
53
|
+
* to spend.
|
|
54
|
+
* @param proof A zero knowledge proof that the submitter is
|
|
55
|
+
* authorized to spend the input, and that the output is valid
|
|
56
|
+
* in terms of obeying mass conservation rules.
|
|
57
|
+
* @param data Opaque data appended to the {UTXOTransfer} event for
|
|
58
|
+
* off-chain consumers.
|
|
59
|
+
*
|
|
60
|
+
* Emits a {UTXOTransfer} event.
|
|
61
|
+
*/
|
|
62
|
+
function transfer(
|
|
63
|
+
uint256 input,
|
|
64
|
+
uint256 output,
|
|
65
|
+
bytes calldata proof,
|
|
66
|
+
bytes calldata data
|
|
67
|
+
) public {
|
|
68
|
+
uint256[] memory inputs = new uint256[](1);
|
|
69
|
+
inputs[0] = input;
|
|
70
|
+
uint256[] memory outputs = new uint256[](1);
|
|
71
|
+
outputs[0] = output;
|
|
72
|
+
uint256[] memory lockedOutputs;
|
|
73
|
+
|
|
74
|
+
validateTransactionProposal(
|
|
75
|
+
inputs,
|
|
76
|
+
outputs,
|
|
77
|
+
lockedOutputs,
|
|
78
|
+
proof,
|
|
79
|
+
false
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
(
|
|
83
|
+
uint256[] memory publicInputs,
|
|
84
|
+
Commonlib.Proof memory proofStruct
|
|
85
|
+
) = constructPublicInputs(inputs, outputs, proof, false);
|
|
86
|
+
verifyProof(proofStruct, publicInputs, false, false);
|
|
87
|
+
processInputsAndOutputs(inputs, outputs, false);
|
|
88
|
+
emit UTXOTransfer(inputs, outputs, msg.sender, data);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ------------------------------------------------------------------
|
|
92
|
+
// ZetoLockable hook overrides — non-fungible flavour.
|
|
93
|
+
//
|
|
94
|
+
// The NF circuits are fixed 1-in/1-out (or 1-in/1-locked-out for
|
|
95
|
+
// create-lock, 1-locked-in/1-out for spend/cancel), so there is no
|
|
96
|
+
// padding step and the batch-verifier path is never selected.
|
|
97
|
+
//
|
|
98
|
+
// Caller-supplied `args` are validated by ZetoLockable to be
|
|
99
|
+
// {ZetoCreateLockArgs}/{ZetoSpendLockArgs}; this hook trusts that
|
|
100
|
+
// shape and additionally enforces that the array sizes match the
|
|
101
|
+
// NF circuit (single input plus a single locked or unlocked
|
|
102
|
+
// output).
|
|
103
|
+
// ------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
/// @dev Thrown when an NF lock-lifecycle call is invoked with an
|
|
106
|
+
/// input/output array shape that does not match the fixed
|
|
107
|
+
/// 1-in/1-out circuit. Catching this at the contract layer
|
|
108
|
+
/// gives a typed revert before the proof verifier would fail
|
|
109
|
+
/// with a generic {InvalidProof}.
|
|
110
|
+
error InvalidNonFungibleArity(
|
|
111
|
+
uint256 inputs,
|
|
112
|
+
uint256 outputs,
|
|
113
|
+
uint256 lockedOutputs
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
function _doLockTransition(
|
|
117
|
+
ZetoCreateLockArgs calldata args
|
|
118
|
+
) internal override {
|
|
119
|
+
// NF createLock: spend exactly one unlocked input and produce
|
|
120
|
+
// exactly one locked output. There is no regular (unlocked)
|
|
121
|
+
// output for NF lock creation — a single token cannot be split.
|
|
122
|
+
if (
|
|
123
|
+
args.inputs.length != 1 ||
|
|
124
|
+
args.outputs.length != 0 ||
|
|
125
|
+
args.lockedOutputs.length != 1
|
|
126
|
+
) {
|
|
127
|
+
revert InvalidNonFungibleArity(
|
|
128
|
+
args.inputs.length,
|
|
129
|
+
args.outputs.length,
|
|
130
|
+
args.lockedOutputs.length
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
validateTransactionProposal(
|
|
135
|
+
args.inputs,
|
|
136
|
+
args.outputs,
|
|
137
|
+
args.lockedOutputs,
|
|
138
|
+
args.proof,
|
|
139
|
+
false
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// The circuit treats the locked output as the "output"; combine
|
|
143
|
+
// for the public-inputs construction.
|
|
144
|
+
uint256[] memory allOutputs = new uint256[](1);
|
|
145
|
+
allOutputs[0] = args.lockedOutputs[0];
|
|
146
|
+
|
|
147
|
+
(
|
|
148
|
+
uint256[] memory publicInputs,
|
|
149
|
+
Commonlib.Proof memory proofStruct
|
|
150
|
+
) = constructPublicInputs(args.inputs, allOutputs, args.proof, false);
|
|
151
|
+
verifyProof(proofStruct, publicInputs, false, false);
|
|
152
|
+
|
|
153
|
+
processInputsAndOutputs(args.inputs, args.outputs, false);
|
|
154
|
+
processLockedOutputs(args.lockedOutputs);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function _transferLocked(
|
|
158
|
+
bytes32 /* lockId */,
|
|
159
|
+
uint256[] calldata lockedInputs,
|
|
160
|
+
uint256[] calldata lockedOutputs,
|
|
161
|
+
uint256[] calldata outputs,
|
|
162
|
+
bytes calldata proof,
|
|
163
|
+
bytes calldata /* data */
|
|
164
|
+
) internal override {
|
|
165
|
+
// NF spend/cancel: consume exactly one locked input and produce
|
|
166
|
+
// exactly one output. The output may be either a regular
|
|
167
|
+
// unlocked UTXO (the typical spend case) or a fresh locked UTXO
|
|
168
|
+
// (e.g. re-lock under a new spender via cancel-then-create).
|
|
169
|
+
if (
|
|
170
|
+
lockedInputs.length != 1 ||
|
|
171
|
+
lockedOutputs.length + outputs.length != 1
|
|
172
|
+
) {
|
|
173
|
+
revert InvalidNonFungibleArity(
|
|
174
|
+
lockedInputs.length,
|
|
175
|
+
outputs.length,
|
|
176
|
+
lockedOutputs.length
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
validateTransactionProposal(
|
|
181
|
+
lockedInputs,
|
|
182
|
+
outputs,
|
|
183
|
+
lockedOutputs,
|
|
184
|
+
proof,
|
|
185
|
+
true
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// The circuit treats locked and unlocked outputs identically, so
|
|
189
|
+
// both feed `constructPublicInputs` via the same single-element
|
|
190
|
+
// `allOutputs` array.
|
|
191
|
+
uint256[] memory allOutputs = new uint256[](1);
|
|
192
|
+
allOutputs[0] = lockedOutputs.length == 1
|
|
193
|
+
? lockedOutputs[0]
|
|
194
|
+
: outputs[0];
|
|
195
|
+
|
|
196
|
+
(
|
|
197
|
+
uint256[] memory publicInputs,
|
|
198
|
+
Commonlib.Proof memory proofStruct
|
|
199
|
+
) = constructPublicInputs(lockedInputs, allOutputs, proof, true);
|
|
200
|
+
verifyProof(proofStruct, publicInputs, false, true);
|
|
201
|
+
|
|
202
|
+
// Storage-side bookkeeping keeps the two ledgers strictly
|
|
203
|
+
// disjoint: the unlocked output (if any) goes through
|
|
204
|
+
// `processOutputs`, the locked output (if any) goes through
|
|
205
|
+
// `processLockedOutputs`. Mirroring `_doLockTransition` here
|
|
206
|
+
// avoids the locked-output being recorded as both unlocked and
|
|
207
|
+
// locked, which would let it be spent twice (once via the
|
|
208
|
+
// unlocked path, once via the lockable path).
|
|
209
|
+
processInputsAndOutputs(lockedInputs, outputs, true);
|
|
210
|
+
processLockedOutputs(lockedOutputs);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// @dev ERC-7201 (`erc7201:zeto.storage.ZetoNonFungible`).
|
|
215
|
+
library ZetoNonFungibleStorage {
|
|
216
|
+
struct Layout {
|
|
217
|
+
uint256 __reserved;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
bytes32 private constant STORAGE_LOCATION =
|
|
221
|
+
0x70f918cef12f78aa293f0474363ccfa2a5e8bbdc86def1d4dd7d861fb8e6f600;
|
|
222
|
+
|
|
223
|
+
function layout() internal pure returns (Layout storage $) {
|
|
224
|
+
assembly {
|
|
225
|
+
$.slot := STORAGE_LOCATION
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
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 {IZetoInitializable} from "./interfaces/IZetoInitializable.sol";
|
|
19
|
+
import {ZetoNonFungible} from "./zeto_non_fungible.sol";
|
|
20
|
+
import {IZetoStorage} from "./interfaces/IZetoStorage.sol";
|
|
21
|
+
import {BaseStorage} from "./storage/base.sol";
|
|
22
|
+
|
|
23
|
+
/// @title A sample base implementation of a Zeto based non-fungible token contract
|
|
24
|
+
/// without using nullifiers. Each UTXO's spending status is explicitly tracked.
|
|
25
|
+
/// @author Kaleido, Inc.
|
|
26
|
+
/// @dev Wires {BaseStorage} into {ZetoNonFungible} for non-nullifier
|
|
27
|
+
/// flavours of Zeto NF tokens. See {ZetoNonFungible} for the
|
|
28
|
+
/// rationale behind the omitted lock lifecycle.
|
|
29
|
+
abstract contract ZetoNonFungibleBase is ZetoNonFungible {
|
|
30
|
+
function __ZetoNonFungibleBase_init(
|
|
31
|
+
string calldata name_,
|
|
32
|
+
string calldata symbol_,
|
|
33
|
+
address initialOwner,
|
|
34
|
+
IZetoInitializable.VerifiersInfo calldata verifiers
|
|
35
|
+
) internal onlyInitializing {
|
|
36
|
+
IZetoStorage storage_ = new BaseStorage();
|
|
37
|
+
__ZetoNonFungible_init(
|
|
38
|
+
name_,
|
|
39
|
+
symbol_,
|
|
40
|
+
initialOwner,
|
|
41
|
+
verifiers,
|
|
42
|
+
storage_
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// @dev ERC-7201 (`erc7201:zeto.storage.ZetoNonFungibleBase`).
|
|
48
|
+
library ZetoNonFungibleBaseStorage {
|
|
49
|
+
struct Layout {
|
|
50
|
+
uint256 __reserved;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
bytes32 private constant STORAGE_LOCATION =
|
|
54
|
+
0x3b5aad0db95258b4583c7272ce26730b4879c3622cde60d71e7f2a14bdd3ae00;
|
|
55
|
+
|
|
56
|
+
function layout() internal pure returns (Layout storage $) {
|
|
57
|
+
assembly {
|
|
58
|
+
$.slot := STORAGE_LOCATION
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
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 {IZetoInitializable} from "./interfaces/IZetoInitializable.sol";
|
|
19
|
+
import {ZetoNonFungible} from "./zeto_non_fungible.sol";
|
|
20
|
+
import {IZetoStorage} from "./interfaces/IZetoStorage.sol";
|
|
21
|
+
import {NullifierStorage} from "./storage/nullifier.sol";
|
|
22
|
+
|
|
23
|
+
/// @title A sample base implementation of a Zeto based non-fungible token
|
|
24
|
+
/// contract with nullifier-based history masking.
|
|
25
|
+
/// @author Kaleido, Inc.
|
|
26
|
+
/// @dev Wires {NullifierStorage} into {ZetoNonFungible} for the nullifier
|
|
27
|
+
/// flavours of Zeto NF tokens. See {ZetoNonFungible} for the
|
|
28
|
+
/// rationale behind the omitted lock lifecycle.
|
|
29
|
+
abstract contract ZetoNonFungibleNullifier is ZetoNonFungible {
|
|
30
|
+
function __ZetoNonFungibleNullifier_init(
|
|
31
|
+
string calldata name_,
|
|
32
|
+
string calldata symbol_,
|
|
33
|
+
address initialOwner,
|
|
34
|
+
IZetoInitializable.VerifiersInfo calldata verifiers
|
|
35
|
+
) internal onlyInitializing {
|
|
36
|
+
IZetoStorage storage_ = new NullifierStorage();
|
|
37
|
+
__ZetoNonFungible_init(
|
|
38
|
+
name_,
|
|
39
|
+
symbol_,
|
|
40
|
+
initialOwner,
|
|
41
|
+
verifiers,
|
|
42
|
+
storage_
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// @dev ERC-7201 (`erc7201:zeto.storage.ZetoNonFungibleNullifier`).
|
|
48
|
+
library ZetoNonFungibleNullifierStorage {
|
|
49
|
+
struct Layout {
|
|
50
|
+
uint256 __reserved;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
bytes32 private constant STORAGE_LOCATION =
|
|
54
|
+
0x02976bb09873e8949dece591ee756708ea74c24901fa2000868b92802a9a5200;
|
|
55
|
+
|
|
56
|
+
function layout() internal pure returns (Layout storage $) {
|
|
57
|
+
assembly {
|
|
58
|
+
$.slot := STORAGE_LOCATION
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -15,16 +15,50 @@
|
|
|
15
15
|
// limitations under the License.
|
|
16
16
|
pragma solidity ^0.8.27;
|
|
17
17
|
|
|
18
|
-
import {Commonlib} from "../lib/common.sol";
|
|
18
|
+
import {Commonlib} from "../lib/common/common.sol";
|
|
19
19
|
import {Zeto_Anon} from "../zeto_anon.sol";
|
|
20
|
-
|
|
20
|
+
import {ILockableCapability} from "../lib/interfaces/ILockableCapability.sol";
|
|
21
|
+
import {IZetoLockableCapability} from "../lib/interfaces/IZetoLockableCapability.sol";
|
|
21
22
|
|
|
22
|
-
/// @title
|
|
23
|
+
/// @title zkEscrow1
|
|
23
24
|
/// @author Kaleido, Inc.
|
|
24
|
-
/// @
|
|
25
|
+
/// @notice Sample on-chain escrow built on top of {Zeto_Anon} that brokers a
|
|
26
|
+
/// payment between a payer and a payee using the new
|
|
27
|
+
/// {ILockableCapability} lifecycle.
|
|
28
|
+
///
|
|
29
|
+
/// Lifecycle of a single escrowed payment:
|
|
30
|
+
/// 1. The payer calls {Zeto_Anon.createLock} to lock one or more
|
|
31
|
+
/// UTXOs they own. The payer is initially both the lock owner
|
|
32
|
+
/// and spender.
|
|
33
|
+
/// 2. The payer calls {Zeto_Anon.delegateLock} naming this escrow
|
|
34
|
+
/// as the new spender. From this point on, only this escrow
|
|
35
|
+
/// can spend or cancel the lock.
|
|
36
|
+
/// 3. The payer (or anyone) calls {initiatePayment} on this
|
|
37
|
+
/// escrow with the lockId and the proposed unlocked output
|
|
38
|
+
/// UTXOs. The escrow asserts it is the current spender of the
|
|
39
|
+
/// lock before recording the payment.
|
|
40
|
+
/// 4. An authorised approver calls {approvePayment} with a ZK
|
|
41
|
+
/// proof attesting that `(lockedInputs, outputs)` is a valid
|
|
42
|
+
/// transition under {Zeto_Anon}'s circuit. The escrow stores
|
|
43
|
+
/// the proof but does not yet consume the lock.
|
|
44
|
+
/// 5. Anyone (typically the payee) calls {completePayment}. The
|
|
45
|
+
/// escrow forwards the stored proof to {Zeto_Anon.spendLock},
|
|
46
|
+
/// which consumes the locked inputs and mints the unlocked
|
|
47
|
+
/// outputs to their recipients in a single atomic step.
|
|
48
|
+
///
|
|
49
|
+
/// Notes on the new lock model:
|
|
50
|
+
/// * The escrow does NOT choose the locked inputs at approval
|
|
51
|
+
/// or completion time. They are pinned by `_locks[lockId]`
|
|
52
|
+
/// inside {Zeto_Anon} at create-time, so "spending lock A
|
|
53
|
+
/// always consumes lock A's UTXOs" is a hard contract
|
|
54
|
+
/// invariant rather than a soft hash check on user-supplied
|
|
55
|
+
/// arrays.
|
|
56
|
+
/// * The escrow likewise does not choose `lockedOutputs` (it
|
|
57
|
+
/// always sends them empty); the spend completes by minting
|
|
58
|
+
/// plain unlocked UTXOs to the recipients.
|
|
25
59
|
contract zkEscrow1 {
|
|
26
60
|
enum PaymentStatus {
|
|
27
|
-
UNKNOWN, //
|
|
61
|
+
UNKNOWN, // default value for empty payment slots
|
|
28
62
|
INITIATED,
|
|
29
63
|
APPROVED,
|
|
30
64
|
COMPLETED,
|
|
@@ -32,10 +66,10 @@ contract zkEscrow1 {
|
|
|
32
66
|
}
|
|
33
67
|
|
|
34
68
|
struct Payment {
|
|
35
|
-
|
|
69
|
+
bytes32 lockId;
|
|
36
70
|
uint256[] outputs;
|
|
37
71
|
PaymentStatus status;
|
|
38
|
-
|
|
72
|
+
bytes proof;
|
|
39
73
|
}
|
|
40
74
|
|
|
41
75
|
mapping(uint256 => Payment) public payments;
|
|
@@ -45,7 +79,7 @@ contract zkEscrow1 {
|
|
|
45
79
|
|
|
46
80
|
event PaymentInitiated(
|
|
47
81
|
uint256 paymentId,
|
|
48
|
-
|
|
82
|
+
bytes32 lockId,
|
|
49
83
|
uint256[] outputs,
|
|
50
84
|
bytes data
|
|
51
85
|
);
|
|
@@ -56,35 +90,41 @@ contract zkEscrow1 {
|
|
|
56
90
|
zeto = Zeto_Anon(zetoAddress);
|
|
57
91
|
}
|
|
58
92
|
|
|
93
|
+
/// @notice Record a new escrowed payment against an existing lock.
|
|
94
|
+
/// @dev The escrow MUST already be the current spender of the lock
|
|
95
|
+
/// (the payer must have called {Zeto_Anon.delegateLock} naming
|
|
96
|
+
/// this contract as the new spender before calling here).
|
|
97
|
+
/// The locked inputs themselves are not stored on the
|
|
98
|
+
/// payment; they are read fresh from {Zeto_Anon.getLockedInputs}
|
|
99
|
+
/// at approval and completion time so the escrow always
|
|
100
|
+
/// operates on the lock's storage-pinned content.
|
|
59
101
|
function initiatePayment(
|
|
60
|
-
|
|
102
|
+
bytes32 lockId,
|
|
61
103
|
uint256[] memory outputs,
|
|
62
104
|
bytes calldata data
|
|
63
105
|
) public {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
address delegate;
|
|
67
|
-
(locked, delegate) = zeto.locked(lockedInputs[i]);
|
|
68
|
-
require(locked, "Input not locked");
|
|
69
|
-
require(
|
|
70
|
-
delegate == address(this),
|
|
71
|
-
"Input not locked to this contract"
|
|
72
|
-
);
|
|
73
|
-
}
|
|
106
|
+
ILockableCapability.LockInfo memory info = zeto.getLock(lockId);
|
|
107
|
+
require(info.spender == address(this), "Escrow is not the spender");
|
|
74
108
|
inflightCount++;
|
|
75
|
-
|
|
109
|
+
bytes memory emptyProof;
|
|
76
110
|
payments[inflightCount] = Payment(
|
|
77
|
-
|
|
111
|
+
lockId,
|
|
78
112
|
outputs,
|
|
79
113
|
PaymentStatus.INITIATED,
|
|
80
114
|
emptyProof
|
|
81
115
|
);
|
|
82
|
-
emit PaymentInitiated(inflightCount,
|
|
116
|
+
emit PaymentInitiated(inflightCount, lockId, outputs, data);
|
|
83
117
|
}
|
|
84
118
|
|
|
119
|
+
/// @notice Verify the spend ZK proof for an initiated payment and
|
|
120
|
+
/// move the payment into the APPROVED state.
|
|
121
|
+
/// @dev The verification mirrors what {Zeto_Anon._transferLocked}
|
|
122
|
+
/// would do later (same {checkAndPadCommitments} + locked-input
|
|
123
|
+
/// verifier path), so a successful approval here guarantees
|
|
124
|
+
/// that {completePayment} will not revert on proof grounds.
|
|
85
125
|
function approvePayment(
|
|
86
126
|
uint256 paymentId,
|
|
87
|
-
|
|
127
|
+
bytes memory proof,
|
|
88
128
|
bytes calldata data
|
|
89
129
|
) public {
|
|
90
130
|
Payment storage payment = payments[paymentId];
|
|
@@ -92,12 +132,18 @@ contract zkEscrow1 {
|
|
|
92
132
|
payment.status == PaymentStatus.INITIATED,
|
|
93
133
|
"Payment not initiated"
|
|
94
134
|
);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
135
|
+
uint256[] memory lockedInputs = zeto.getLockedInputs(payment.lockId);
|
|
136
|
+
(
|
|
137
|
+
uint256[] memory paddedInputs,
|
|
138
|
+
uint256[] memory paddedOutputs
|
|
139
|
+
) = zeto.checkAndPadCommitments(lockedInputs, payment.outputs);
|
|
99
140
|
require(
|
|
100
|
-
zeto.
|
|
141
|
+
zeto.constructPublicSignalsAndVerifyProof(
|
|
142
|
+
paddedInputs,
|
|
143
|
+
paddedOutputs,
|
|
144
|
+
proof,
|
|
145
|
+
true
|
|
146
|
+
),
|
|
101
147
|
"Invalid proof"
|
|
102
148
|
);
|
|
103
149
|
payment.proof = proof;
|
|
@@ -105,18 +151,28 @@ contract zkEscrow1 {
|
|
|
105
151
|
emit PaymentApproved(paymentId, data);
|
|
106
152
|
}
|
|
107
153
|
|
|
154
|
+
/// @notice Forward the approved spend proof to {Zeto_Anon.spendLock}.
|
|
155
|
+
/// @dev Encodes the {IZetoLockableCapability.ZetoSpendLockArgs}
|
|
156
|
+
/// payload from `payment` and invokes `spendLock(lockId, ...)`
|
|
157
|
+
/// as the lock's current spender. After this call returns the
|
|
158
|
+
/// lock is consumed, the locked UTXOs are marked spent on the
|
|
159
|
+
/// underlying Zeto storage, and the unlocked outputs are
|
|
160
|
+
/// emitted to their recipients.
|
|
108
161
|
function completePayment(uint256 paymentId, bytes calldata data) public {
|
|
109
162
|
Payment storage payment = payments[paymentId];
|
|
110
163
|
require(
|
|
111
164
|
payment.status == PaymentStatus.APPROVED,
|
|
112
165
|
"Payment not approved"
|
|
113
166
|
);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
167
|
+
IZetoLockableCapability.ZetoSpendLockArgs
|
|
168
|
+
memory spendArgs = IZetoLockableCapability.ZetoSpendLockArgs({
|
|
169
|
+
txId: bytes32(paymentId),
|
|
170
|
+
lockedOutputs: new uint256[](0),
|
|
171
|
+
outputs: payment.outputs,
|
|
172
|
+
proof: payment.proof,
|
|
173
|
+
data: ""
|
|
174
|
+
});
|
|
175
|
+
zeto.spendLock(payment.lockId, abi.encode(spendArgs), "");
|
|
120
176
|
payment.status = PaymentStatus.COMPLETED;
|
|
121
177
|
emit PaymentCompleted(paymentId, data);
|
|
122
178
|
}
|