@lfdecentralizedtrust/zeto-contracts 0.2.1 → 0.5.0

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 (155) hide show
  1. package/README.md +70 -162
  2. package/contracts/factory.sol +30 -34
  3. package/contracts/factory_upgradeable.sol +160 -0
  4. package/contracts/lib/common/util.sol +40 -0
  5. package/contracts/lib/interfaces/ILockableCapability.sol +318 -0
  6. package/contracts/lib/interfaces/{izeto.sol → IZeto.sol} +13 -1
  7. package/contracts/lib/interfaces/{izeto_initializable.sol → IZetoInitializable.sol} +14 -12
  8. package/contracts/lib/interfaces/{izeto_kyc.sol → IZetoKyc.sol} +3 -3
  9. package/contracts/lib/interfaces/IZetoLockableCapability.sol +237 -0
  10. package/contracts/lib/interfaces/IZetoStorage.sol +106 -0
  11. package/contracts/lib/interfaces/{izeto_lockable.sol → IZetoVerifier.sol} +8 -21
  12. package/contracts/lib/registry.sol +77 -27
  13. package/contracts/lib/storage/base.sol +210 -0
  14. package/contracts/lib/storage/nullifier.sol +166 -0
  15. package/contracts/lib/zeto_common.sol +277 -28
  16. package/contracts/lib/zeto_fungible.sol +451 -33
  17. package/contracts/lib/zeto_fungible_base.sol +69 -0
  18. package/contracts/lib/zeto_fungible_burn.sol +83 -53
  19. package/contracts/lib/zeto_fungible_burn_nullifier.sol +115 -72
  20. package/contracts/lib/zeto_fungible_nullifier.sol +167 -0
  21. package/contracts/lib/zeto_lockable.sol +613 -0
  22. package/contracts/lib/zeto_non_fungible.sol +236 -0
  23. package/contracts/lib/zeto_non_fungible_base.sol +61 -0
  24. package/contracts/lib/zeto_non_fungible_nullifier.sol +61 -0
  25. package/contracts/test/escrow1.sol +90 -34
  26. package/contracts/test/escrow2.sol +64 -29
  27. package/contracts/test/qurrency.sol +10 -2
  28. package/contracts/test/smt.sol +8 -3
  29. package/contracts/test/tendecimals.sol +14 -12
  30. package/contracts/verifiers/impl/anon.sol +189 -0
  31. package/contracts/verifiers/impl/anon_batch.sol +301 -0
  32. package/contracts/verifiers/impl/anon_enc.sol +266 -0
  33. package/contracts/verifiers/impl/anon_enc_batch.sol +602 -0
  34. package/contracts/verifiers/impl/anon_enc_nullifier.sol +287 -0
  35. package/contracts/verifiers/impl/anon_enc_nullifier_batch.sol +679 -0
  36. package/contracts/verifiers/impl/anon_enc_nullifier_kyc.sol +294 -0
  37. package/contracts/verifiers/impl/anon_enc_nullifier_kyc_batch.sol +686 -0
  38. package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation.sol +413 -0
  39. package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation_batch.sol +1141 -0
  40. package/contracts/verifiers/impl/anon_nullifier_kyc_transfer.sol +217 -0
  41. package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked.sol +196 -0
  42. package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked_batch.sol +308 -0
  43. package/contracts/verifiers/impl/anon_nullifier_kyc_transfer_batch.sol +385 -0
  44. package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer.sol +497 -0
  45. package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer_batch.sol +1001 -0
  46. package/contracts/verifiers/{verifier_anon_nullifier_transferLocked.sol → impl/anon_nullifier_transfer.sol} +12 -19
  47. package/contracts/verifiers/{verifier_anon_nullifier_transferLocked_batch.sol → impl/anon_nullifier_transfer_batch.sol} +44 -51
  48. package/contracts/verifiers/impl/burn.sol +182 -0
  49. package/contracts/verifiers/impl/burn_batch.sol +238 -0
  50. package/contracts/verifiers/impl/burn_nullifier.sol +203 -0
  51. package/contracts/verifiers/impl/burn_nullifier_batch.sol +315 -0
  52. package/contracts/verifiers/impl/deposit.sol +182 -0
  53. package/contracts/verifiers/impl/deposit_kyc.sol +189 -0
  54. package/contracts/verifiers/impl/nf_anon.sol +175 -0
  55. package/contracts/verifiers/{verifier_nf_anon_nullifier_transferLocked.sol → impl/nf_anon_nullifier_transfer.sol} +6 -13
  56. package/contracts/verifiers/impl/withdraw.sol +189 -0
  57. package/contracts/verifiers/impl/withdraw_batch.sol +245 -0
  58. package/contracts/verifiers/impl/withdraw_nullifier.sol +210 -0
  59. package/contracts/verifiers/impl/withdraw_nullifier_batch.sol +322 -0
  60. package/contracts/verifiers/verifier_anon.sol +31 -186
  61. package/contracts/verifiers/verifier_anon_batch.sol +31 -298
  62. package/contracts/verifiers/verifier_anon_enc.sol +31 -263
  63. package/contracts/verifiers/verifier_anon_enc_batch.sol +31 -599
  64. package/contracts/verifiers/verifier_anon_enc_nullifier.sol +31 -284
  65. package/contracts/verifiers/verifier_anon_enc_nullifier_batch.sol +33 -676
  66. package/contracts/verifiers/verifier_anon_enc_nullifier_kyc.sol +31 -291
  67. package/contracts/verifiers/verifier_anon_enc_nullifier_kyc_batch.sol +33 -683
  68. package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation.sol +33 -410
  69. package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation_batch.sol +33 -1138
  70. package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer.sol +33 -214
  71. package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked.sol +33 -221
  72. package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked_batch.sol +33 -389
  73. package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer_batch.sol +33 -382
  74. package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer.sol +33 -221
  75. package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer_batch.sol +33 -389
  76. package/contracts/verifiers/verifier_anon_nullifier_transfer.sol +33 -207
  77. package/contracts/verifiers/verifier_anon_nullifier_transfer_batch.sol +33 -375
  78. package/contracts/verifiers/verifier_burn.sol +31 -179
  79. package/contracts/verifiers/verifier_burn_batch.sol +31 -235
  80. package/contracts/verifiers/verifier_burn_nullifier.sol +31 -200
  81. package/contracts/verifiers/verifier_burn_nullifier_batch.sol +31 -312
  82. package/contracts/verifiers/verifier_deposit.sol +31 -179
  83. package/contracts/verifiers/verifier_deposit_kyc.sol +34 -0
  84. package/contracts/verifiers/verifier_nf_anon.sol +31 -172
  85. package/contracts/verifiers/verifier_nf_anon_nullifier_transfer.sol +33 -179
  86. package/contracts/verifiers/verifier_withdraw.sol +31 -186
  87. package/contracts/verifiers/verifier_withdraw_batch.sol +31 -242
  88. package/contracts/verifiers/verifier_withdraw_nullifier.sol +31 -207
  89. package/contracts/verifiers/verifier_withdraw_nullifier_batch.sol +33 -319
  90. package/contracts/zeto_anon.sol +77 -231
  91. package/contracts/zeto_anon_burnable.sol +56 -12
  92. package/contracts/zeto_anon_enc.sol +93 -190
  93. package/contracts/zeto_anon_enc_nullifier.sol +249 -195
  94. package/contracts/zeto_anon_enc_nullifier_kyc.sol +51 -232
  95. package/contracts/zeto_anon_enc_nullifier_non_repudiation.sol +231 -238
  96. package/contracts/zeto_anon_nullifier.sol +164 -298
  97. package/contracts/zeto_anon_nullifier_burnable.sol +68 -18
  98. package/contracts/zeto_anon_nullifier_kyc.sol +40 -345
  99. package/contracts/zeto_anon_nullifier_qurrency.sol +218 -360
  100. package/contracts/zeto_nf_anon.sol +55 -130
  101. package/contracts/zeto_nf_anon_nullifier.sol +90 -152
  102. package/hardhat.config.ts +9 -3
  103. package/ignition/modules/lib/deps.ts +9 -6
  104. package/ignition/modules/zeto_anon_burnable.ts +8 -7
  105. package/ignition/modules/zeto_anon_enc_nullifier.ts +33 -1
  106. package/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +2 -2
  107. package/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts +2 -1
  108. package/ignition/modules/zeto_anon_nullifier.ts +30 -5
  109. package/ignition/modules/zeto_anon_nullifier_burnable.ts +52 -17
  110. package/ignition/modules/zeto_anon_nullifier_kyc.ts +27 -12
  111. package/ignition/modules/zeto_anon_nullifier_qurrency.ts +2 -1
  112. package/ignition/modules/zeto_nf_anon_nullifier.ts +16 -12
  113. package/package.json +4 -2
  114. package/scripts/deploy_cloneable.ts +19 -10
  115. package/scripts/deploy_upgradeable.ts +9 -5
  116. package/scripts/tokens/Zeto_Anon.ts +3 -3
  117. package/scripts/tokens/Zeto_AnonBurnable.ts +1 -1
  118. package/scripts/tokens/Zeto_AnonEnc.ts +12 -3
  119. package/scripts/tokens/Zeto_AnonEncNullifier.ts +7 -3
  120. package/scripts/tokens/Zeto_AnonEncNullifierKyc.ts +1 -1
  121. package/scripts/tokens/Zeto_AnonEncNullifierNonRepudiation.ts +3 -1
  122. package/scripts/tokens/Zeto_AnonNullifier.ts +3 -1
  123. package/scripts/tokens/Zeto_AnonNullifierBurnable.ts +3 -1
  124. package/scripts/tokens/Zeto_AnonNullifierKyc.ts +1 -1
  125. package/scripts/tokens/Zeto_AnonNullifierQurrency.ts +3 -1
  126. package/scripts/tokens/Zeto_NfAnon.ts +2 -2
  127. package/scripts/tokens/Zeto_NfAnonNullifier.ts +3 -2
  128. package/test/factory.ts +46 -73
  129. package/test/factory_upgradeable.ts +311 -0
  130. package/test/lib/anon_nullifier_helpers.ts +89 -0
  131. package/test/lib/anon_zeto_helpers.ts +76 -0
  132. package/test/lib/deploy.ts +3 -2
  133. package/test/lib/utils.ts +74 -35
  134. package/test/test/escrow1.ts +185 -58
  135. package/test/test/escrow2.ts +200 -107
  136. package/test/test/qurrency.ts +13 -33
  137. package/test/usdc-shielding.ts +39 -26
  138. package/test/utils.ts +144 -21
  139. package/test/zeto_anon.ts +956 -465
  140. package/test/zeto_anon_enc.ts +881 -142
  141. package/test/zeto_anon_enc_nullifier.ts +847 -38
  142. package/test/zeto_anon_enc_nullifier_kyc.ts +120 -181
  143. package/test/zeto_anon_enc_nullifier_non_repudiation.ts +77 -46
  144. package/test/zeto_anon_nullifier.ts +1212 -953
  145. package/test/zeto_anon_nullifier_kyc.ts +674 -655
  146. package/test/zeto_anon_nullifier_qurrency.ts +452 -306
  147. package/test/zeto_nf_anon.ts +737 -137
  148. package/test/zeto_nf_anon_nullifier.ts +876 -246
  149. package/contracts/lib/zeto_base.sol +0 -293
  150. package/contracts/lib/zeto_fungible_withdraw.sol +0 -132
  151. package/contracts/lib/zeto_fungible_withdraw_nullifier.sol +0 -144
  152. package/contracts/lib/zeto_nullifier.sol +0 -334
  153. package/contracts/zkDvP.sol_ +0 -273
  154. package/test/zkDvP.ts_ +0 -455
  155. /package/contracts/lib/{common.sol → common/common.sol} +0 -0
@@ -15,155 +15,80 @@
15
15
  // limitations under the License.
16
16
  pragma solidity ^0.8.27;
17
17
 
18
- import {IZeto} from "./lib/interfaces/izeto.sol";
19
- import {Groth16Verifier_NfAnon} from "./verifiers/verifier_nf_anon.sol";
20
- import {ZetoBase} from "./lib/zeto_base.sol";
21
- import {Commonlib} from "./lib/common.sol";
22
- import {IZetoInitializable} from "./lib/interfaces/izeto_initializable.sol";
18
+ import {ZetoNonFungibleBase} from "./lib/zeto_non_fungible_base.sol";
19
+ import {Commonlib} from "./lib/common/common.sol";
20
+ import {IZetoInitializable} from "./lib/interfaces/IZetoInitializable.sol";
23
21
  import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
24
22
 
25
- /// @title A sample implementation of a Zeto based non-fungible token with anonymity and no encryption
23
+ /// @title A sample implementation of a Zeto based non-fungible token with
24
+ /// anonymity (no encryption, no nullifier history masking).
26
25
  /// @author Kaleido, Inc.
26
+ /// @notice Decimals: this token uses **4** decimals, inherited from
27
+ /// {ZetoCommon.decimals}. NFT integrators rarely consult
28
+ /// `decimals()` but it is preserved for ABI symmetry with the
29
+ /// fungible siblings.
27
30
  /// @dev The proof has the following statements:
28
- /// - The sender owns the private key whose public key is part of the pre-image of the input UTXOs commitments
29
- /// (aka the sender is authorized to spend the input UTXOs)
30
- /// - The input UTXOs and output UTXOs are valid in terms of obeying mass conservation rules
31
- contract Zeto_NfAnon is IZeto, IZetoInitializable, ZetoBase, UUPSUpgradeable {
32
- Groth16Verifier_NfAnon internal _verifier;
31
+ /// - The sender owns the private key whose public key is part of the pre-image of the input UTXO commitment
32
+ /// - The output UTXO commitment is well-formed
33
+ ///
34
+ /// Single-input/single-output transfer. The lock lifecycle is
35
+ /// intentionally omitted in this revival; see {ZetoNonFungible}.
36
+ contract Zeto_NfAnon is ZetoNonFungibleBase, UUPSUpgradeable {
37
+ /// @dev Lock the implementation contract on construction.
38
+ /// @custom:oz-upgrades-unsafe-allow constructor
39
+ constructor() {
40
+ _disableInitializers();
41
+ }
33
42
 
34
43
  function initialize(
35
- string memory name,
36
- string memory symbol,
44
+ string calldata name,
45
+ string calldata symbol,
37
46
  address initialOwner,
38
47
  IZetoInitializable.VerifiersInfo calldata verifiers
39
48
  ) public initializer {
40
- __ZetoBase_init(name, symbol, initialOwner);
41
- _verifier = (Groth16Verifier_NfAnon)(verifiers.verifier);
49
+ __ZetoNonFungibleBase_init(name, symbol, initialOwner, verifiers);
42
50
  }
43
51
 
44
52
  function _authorizeUpgrade(address) internal override onlyOwner {}
45
53
 
46
- /**
47
- * @dev the main function of the contract.
48
- *
49
- * @param input The UTXO to be spent by the transaction.
50
- * @param output The new UTXO to generate, for future transactions to spend.
51
- * @param proof A zero knowledge proof that the submitter is authorized to spend the inputs, and
52
- * that the outputs are valid in terms of obeying mass conservation rules.
53
- *
54
- * Emits a {UTXOTransfer} event.
55
- */
56
- function transfer(
57
- uint256 input,
58
- uint256 output,
59
- Commonlib.Proof calldata proof,
60
- bytes calldata data
61
- ) public returns (bool) {
62
- uint256[] memory inputs = new uint256[](1);
63
- inputs[0] = input;
64
- uint256[] memory outputs = new uint256[](1);
65
- outputs[0] = output;
66
- uint256[] memory lockedOutputs;
67
- validateTransactionProposal(inputs, outputs, lockedOutputs, false);
68
-
69
- // construct the public inputs
70
- uint256[2] memory publicInputs;
71
- publicInputs[0] = input;
72
- publicInputs[1] = output;
73
-
74
- // Check the proof
75
- require(
76
- _verifier.verifyProof(proof.pA, proof.pB, proof.pC, publicInputs),
77
- "Invalid proof"
78
- );
79
-
80
- processInputsAndOutputs(inputs, outputs, lockedOutputs, false);
81
-
82
- emit UTXOTransfer(inputs, outputs, msg.sender, data);
83
- return true;
84
- }
85
-
86
- /**
87
- * @dev the main function of the contract.
88
- *
89
- * @param input The UTXO to be spent by the transaction.
90
- * @param output The new UTXO to generate, for future transactions to spend.
91
- * @param proof A zero knowledge proof that the submitter is authorized to spend the inputs, and
92
- * that the outputs are valid in terms of obeying mass conservation rules.
93
- *
94
- * Emits a {UTXOTransfer} event.
95
- */
96
- function transferLocked(
97
- uint256 input,
98
- uint256 output,
99
- Commonlib.Proof calldata proof,
100
- bytes calldata data
101
- ) public returns (bool) {
102
- uint256[] memory inputs = new uint256[](1);
103
- inputs[0] = input;
104
- uint256[] memory outputs = new uint256[](1);
105
- outputs[0] = output;
106
- uint256[] memory lockedOutputs;
107
- validateTransactionProposal(inputs, outputs, lockedOutputs, true);
108
-
109
- // construct the public inputs
110
- uint256[2] memory publicInputs;
111
- publicInputs[0] = input;
112
- publicInputs[1] = output;
113
-
114
- // Check the proof
115
- require(
116
- _verifier.verifyProof(proof.pA, proof.pB, proof.pC, publicInputs),
117
- "Invalid proof"
54
+ /// @dev Public-inputs layout: [input, output]. `inputsLocked` is
55
+ /// ignored because this token does not implement the lock
56
+ /// lifecycle.
57
+ function constructPublicInputs(
58
+ uint256[] memory inputs,
59
+ uint256[] memory outputs,
60
+ bytes memory proof,
61
+ bool /* inputsLocked */
62
+ )
63
+ internal
64
+ pure
65
+ override
66
+ returns (uint256[] memory, Commonlib.Proof memory)
67
+ {
68
+ Commonlib.Proof memory proofStruct = abi.decode(
69
+ proof,
70
+ (Commonlib.Proof)
118
71
  );
72
+ uint256[] memory publicInputs = new uint256[](2);
73
+ publicInputs[0] = inputs[0];
74
+ publicInputs[1] = outputs[0];
119
75
 
120
- processInputsAndOutputs(inputs, outputs, lockedOutputs, true);
121
-
122
- emit UTXOTransfer(inputs, outputs, msg.sender, data);
123
- return true;
76
+ return (publicInputs, proofStruct);
124
77
  }
78
+ }
125
79
 
126
- function mint(uint256[] memory utxos, bytes calldata data) public {
127
- _mint(utxos, data);
80
+ /// @dev ERC-7201 (`erc7201:zeto.storage.Zeto_NfAnon`).
81
+ library Zeto_NfAnonStorage {
82
+ struct Layout {
83
+ uint256 __reserved;
128
84
  }
129
85
 
130
- function lock(
131
- uint256 input,
132
- uint256 lockedOutput,
133
- Commonlib.Proof calldata proof,
134
- address delegate,
135
- bytes calldata data
136
- ) public {
137
- uint256[] memory inputs = new uint256[](1);
138
- inputs[0] = input;
139
- uint256[] memory outputs;
140
- uint256[] memory lockedOutputs = new uint256[](1);
141
- lockedOutputs[0] = lockedOutput;
142
- validateTransactionProposal(inputs, outputs, lockedOutputs, false);
143
-
144
- // construct the public inputs
145
- uint256[2] memory publicInputs;
146
- publicInputs[0] = input;
147
- publicInputs[1] = lockedOutput;
148
-
149
- // Check the proof
150
- require(
151
- _verifier.verifyProof(proof.pA, proof.pB, proof.pC, publicInputs),
152
- "Invalid proof"
153
- );
154
-
155
- processInputsAndOutputs(inputs, outputs, lockedOutputs, false);
156
-
157
- // lock the intended outputs
158
- _lock(inputs, outputs, lockedOutputs, delegate, data);
159
- }
86
+ bytes32 private constant STORAGE_LOCATION =
87
+ 0xafcd749646d899cf78964151db8f542448d05467330245e10728559327401500;
160
88
 
161
- function unlock(
162
- uint256 input,
163
- uint256 output,
164
- Commonlib.Proof calldata proof,
165
- bytes calldata data
166
- ) public {
167
- transferLocked(input, output, proof, data);
89
+ function layout() internal pure returns (Layout storage $) {
90
+ assembly {
91
+ $.slot := STORAGE_LOCATION
92
+ }
168
93
  }
169
94
  }
@@ -15,177 +15,115 @@
15
15
  // limitations under the License.
16
16
  pragma solidity ^0.8.27;
17
17
 
18
- import {IZeto} from "./lib/interfaces/izeto.sol";
19
- import {Groth16Verifier_NfAnonNullifierTransfer} from "./verifiers/verifier_nf_anon_nullifier_transfer.sol";
20
- import {Groth16Verifier_NfAnonNullifierTransferLocked} from "./verifiers/verifier_nf_anon_nullifier_transferLocked.sol";
21
- import {ZetoNullifier} from "./lib/zeto_nullifier.sol";
22
- import {Commonlib} from "./lib/common.sol";
23
- import {IZetoInitializable} from "./lib/interfaces/izeto_initializable.sol";
18
+ import {ZetoNonFungibleNullifier} from "./lib/zeto_non_fungible_nullifier.sol";
19
+ import {Commonlib} from "./lib/common/common.sol";
20
+ import {IZetoInitializable} from "./lib/interfaces/IZetoInitializable.sol";
24
21
  import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
25
22
 
26
- /// @title A sample implementation of a Zeto based non-fungible token with anonymity and history masking
23
+ /// @title A sample implementation of a Zeto based non-fungible token with
24
+ /// anonymity and history masking via nullifiers.
27
25
  /// @author Kaleido, Inc.
26
+ /// @notice Decimals: this token uses **4** decimals, inherited from
27
+ /// {ZetoCommon.decimals}. NFT integrators rarely consult
28
+ /// `decimals()` but it is preserved for ABI symmetry with the
29
+ /// fungible siblings.
28
30
  /// @dev The proof has the following statements:
29
- /// - each value in the output commitments must be a positive number in the range 0 ~ (2\*\*40 - 1)
30
- /// - the sum of the nullified values match the sum of output values
31
- /// - the hashes in the input and output match the hash(value, salt, owner public key) formula
32
- /// - the sender possesses the private BabyJubjub key, whose public key is part of the pre-image of the input commitment hashes, which match the corresponding nullifiers
33
- /// - the nullifiers represent input commitments that are included in a Sparse Merkle Tree represented by the root hash
34
- contract Zeto_NfAnonNullifier is
35
- IZeto,
36
- IZetoInitializable,
37
- ZetoNullifier,
38
- UUPSUpgradeable
39
- {
40
- Groth16Verifier_NfAnonNullifierTransfer _verifier;
41
- Groth16Verifier_NfAnonNullifierTransferLocked _lockVerifier;
31
+ /// - The sender owns the private key whose public key is part of the pre-image of the input UTXO commitment
32
+ /// (which corresponds to the nullifier).
33
+ /// - The nullifier represents an input commitment included in a Sparse Merkle Tree represented by the root hash.
34
+ /// - The output UTXO commitment is well-formed.
35
+ ///
36
+ /// Two public-inputs layouts are produced depending on whether the
37
+ /// transition is consuming an unlocked input (a nullifier proven
38
+ /// against the unlocked-commitments SMT) or a locked input (a raw
39
+ /// UTXO hash whose locked status is verified by the storage layer
40
+ /// itself):
41
+ /// - unlocked: `[nullifier, root, output]` -> verified by
42
+ /// {Groth16Verifier_NfAnonNullifierTransfer}.
43
+ /// - locked: `[input, output]` -> verified by the
44
+ /// simpler {Groth16Verifier_NfAnon}.
45
+ /// This mirrors how {Zeto_AnonNullifier} reuses the non-nullifier
46
+ /// verifier for its lock path. The locked branch does not require
47
+ /// a Merkle inclusion proof because the contract has already
48
+ /// validated the input via {NullifierStorage.validateInputs} (it
49
+ /// defers to {BaseStorage.validateInputs} when `inputsLocked`).
50
+ contract Zeto_NfAnonNullifier is ZetoNonFungibleNullifier, UUPSUpgradeable {
51
+ /// @dev Lock the implementation contract on construction.
52
+ /// @custom:oz-upgrades-unsafe-allow constructor
53
+ constructor() {
54
+ _disableInitializers();
55
+ }
42
56
 
43
57
  function initialize(
44
- string memory name,
45
- string memory symbol,
58
+ string calldata name,
59
+ string calldata symbol,
46
60
  address initialOwner,
47
61
  IZetoInitializable.VerifiersInfo calldata verifiers
48
62
  ) public initializer {
49
- __ZetoNullifier_init(name, symbol, initialOwner);
50
- _verifier = (Groth16Verifier_NfAnonNullifierTransfer)(
51
- verifiers.verifier
52
- );
53
- _lockVerifier = (Groth16Verifier_NfAnonNullifierTransferLocked)(
54
- verifiers.lockVerifier
55
- );
63
+ __ZetoNonFungibleNullifier_init(name, symbol, initialOwner, verifiers);
56
64
  }
57
65
 
58
66
  function _authorizeUpgrade(address) internal override onlyOwner {}
59
67
 
60
- /**
61
- * @dev the main function of the contract.
62
- *
63
- * @param nullifier A nullifier that are secretly bound to the UTXO to be spent by the transaction.
64
- * @param output new UTXO to generate, for future transactions to spend.
65
- * @param root The root hash of the Sparse Merkle Tree that contains the nullifier.
66
- * @param proof A zero knowledge proof that the submitter is authorized to spend the inputs, and
67
- * that the outputs are valid in terms of obeying mass conservation rules.
68
- *
69
- * Emits a {UTXOTransfer} event.
70
- */
71
- function transfer(
72
- uint256 nullifier,
73
- uint256 output,
74
- uint256 root,
75
- Commonlib.Proof calldata proof,
76
- bytes calldata data
77
- ) public returns (bool) {
78
- uint256[] memory nullifiers = new uint256[](1);
79
- nullifiers[0] = nullifier;
80
- uint256[] memory outputs = new uint256[](1);
81
- outputs[0] = output;
82
- validateTransactionProposal(nullifiers, outputs, root, false);
83
- checkProof(nullifiers, outputs, root, proof);
84
- uint256[] memory empty;
85
- processInputsAndOutputs(nullifiers, outputs, empty, address(0));
86
-
87
- emit UTXOTransfer(nullifiers, outputs, msg.sender, data);
88
- return true;
89
- }
90
-
91
- /**
92
- * @dev the main function of the contract.
93
- *
94
- * @param nullifier A nullifier that are secretly bound to the UTXO to be spent by the transaction.
95
- * @param output new UTXO to generate, for future transactions to spend.
96
- * @param root The root hash of the Sparse Merkle Tree that contains the nullifier.
97
- * @param proof A zero knowledge proof that the submitter is authorized to spend the inputs, and
98
- * that the outputs are valid in terms of obeying mass conservation rules.
99
- *
100
- * Emits a {UTXOTransfer} event.
101
- */
102
- function transferLocked(
103
- uint256 nullifier,
104
- uint256 output,
105
- uint256 root,
106
- Commonlib.Proof calldata proof,
107
- bytes calldata data
108
- ) public returns (bool) {
109
- uint256[] memory nullifiers = new uint256[](1);
110
- nullifiers[0] = nullifier;
111
- uint256[] memory outputs = new uint256[](1);
112
- outputs[0] = output;
113
- validateTransactionProposal(nullifiers, outputs, root, true);
114
- checkProofLocked(nullifiers, outputs, root, proof);
115
- uint256[] memory empty;
116
- processInputsAndOutputs(nullifiers, outputs, empty, address(0));
117
-
118
- emit UTXOTransfer(nullifiers, outputs, msg.sender, data);
119
- return true;
120
- }
121
-
122
- function mint(uint256[] memory utxos, bytes calldata data) public {
123
- _mint(utxos, data);
124
- }
125
-
126
- function lock(
127
- uint256 nullifier,
128
- uint256 lockedOutput,
129
- uint256 root,
130
- Commonlib.Proof calldata proof,
131
- address delegate,
132
- bytes calldata data
133
- ) public {
134
- uint256[] memory nullifiers = new uint256[](1);
135
- nullifiers[0] = nullifier;
136
- uint256[] memory lockedOutputs = new uint256[](1);
137
- lockedOutputs[0] = lockedOutput;
138
- validateTransactionProposal(nullifiers, lockedOutputs, root, false);
139
- checkProof(nullifiers, lockedOutputs, root, proof);
140
-
141
- processNullifiers(nullifiers);
142
-
143
- // lock the intended outputs
144
- uint256[] memory outputs;
145
- _lock(nullifiers, outputs, lockedOutputs, delegate, data);
146
- }
147
-
148
- function checkProof(
149
- uint256[] memory nullifiers,
68
+ /// @dev Public-inputs layout depends on `inputsLocked`:
69
+ /// - !inputsLocked: `[nullifier, root, output]` paired with the
70
+ /// `nf_anon_nullifier_transfer` circuit. The proof carries
71
+ /// `(uint256 root, Commonlib.Proof)`; the root is decoded
72
+ /// and validated here so that `validateTransactionProposal`
73
+ /// does not need to pre-decode the proof.
74
+ /// - inputsLocked: `[input, output]` paired with the simpler
75
+ /// `nf_anon` circuit. No root: the storage layer has
76
+ /// already verified the input is in `_lockedUtxos`, so we
77
+ /// do not re-prove inclusion in a locked-SMT. The proof
78
+ /// payload is just `Commonlib.Proof`.
79
+ function constructPublicInputs(
80
+ uint256[] memory inputs,
150
81
  uint256[] memory outputs,
151
- uint256 root,
152
- Commonlib.Proof calldata proof
153
- ) internal view {
154
- // construct the public inputs
155
- uint256[3] memory publicInputs;
156
- publicInputs[0] = nullifiers[0];
82
+ bytes memory proof,
83
+ bool inputsLocked
84
+ )
85
+ internal
86
+ view
87
+ override
88
+ returns (uint256[] memory, Commonlib.Proof memory)
89
+ {
90
+ if (inputsLocked) {
91
+ Commonlib.Proof memory proofStruct = abi.decode(
92
+ proof,
93
+ (Commonlib.Proof)
94
+ );
95
+ uint256[] memory publicInputs = new uint256[](2);
96
+ publicInputs[0] = inputs[0];
97
+ publicInputs[1] = outputs[0];
98
+ return (publicInputs, proofStruct);
99
+ }
100
+
101
+ (uint256 root, Commonlib.Proof memory proofStruct) = abi.decode(
102
+ proof,
103
+ (uint256, Commonlib.Proof)
104
+ );
105
+ validateRoot(root);
106
+
107
+ uint256[] memory publicInputs = new uint256[](3);
108
+ publicInputs[0] = inputs[0];
157
109
  publicInputs[1] = root;
158
110
  publicInputs[2] = outputs[0];
111
+ return (publicInputs, proofStruct);
112
+ }
113
+ }
159
114
 
160
- // Check the proof
161
- require(
162
- _verifier.verifyProof(proof.pA, proof.pB, proof.pC, publicInputs),
163
- "Invalid proof"
164
- );
115
+ /// @dev ERC-7201 (`erc7201:zeto.storage.Zeto_NfAnonNullifier`).
116
+ library Zeto_NfAnonNullifierStorage {
117
+ struct Layout {
118
+ uint256 __reserved;
165
119
  }
166
120
 
167
- function checkProofLocked(
168
- uint256[] memory nullifiers,
169
- uint256[] memory outputs,
170
- uint256 root,
171
- Commonlib.Proof calldata proof
172
- ) internal view {
173
- // construct the public inputs
174
- uint256[4] memory publicInputs;
175
- publicInputs[0] = nullifiers[0];
176
- publicInputs[1] = uint256(uint160(msg.sender));
177
- publicInputs[2] = root;
178
- publicInputs[3] = outputs[0];
121
+ bytes32 private constant STORAGE_LOCATION =
122
+ 0xb42e743e63c75bf1f2c05ab181e1371afad8f4114706ec6cfc12171a24893700;
179
123
 
180
- // Check the proof
181
- require(
182
- _lockVerifier.verifyProof(
183
- proof.pA,
184
- proof.pB,
185
- proof.pC,
186
- publicInputs
187
- ),
188
- "Invalid proof"
189
- );
124
+ function layout() internal pure returns (Layout storage $) {
125
+ assembly {
126
+ $.slot := STORAGE_LOCATION
127
+ }
190
128
  }
191
129
  }
package/hardhat.config.ts CHANGED
@@ -20,8 +20,8 @@ import "@openzeppelin/hardhat-upgrades";
20
20
  import crypto from "crypto";
21
21
 
22
22
  const keys = [
23
- crypto.randomBytes(32).toString("hex"),
24
- crypto.randomBytes(32).toString("hex"),
23
+ process.env.ETH_PRIVATE_KEY_1 || crypto.randomBytes(32).toString("hex"),
24
+ process.env.ETH_PRIVATE_KEY_2 || crypto.randomBytes(32).toString("hex"),
25
25
  crypto.randomBytes(32).toString("hex"),
26
26
  crypto.randomBytes(32).toString("hex"),
27
27
  crypto.randomBytes(32).toString("hex"),
@@ -41,14 +41,20 @@ const config: HardhatUserConfig = {
41
41
  settings: {
42
42
  optimizer: {
43
43
  enabled: true,
44
- runs: 1000,
44
+ runs: 25,
45
45
  },
46
+ viaIR: true,
46
47
  },
47
48
  },
48
49
  paths: {
49
50
  sources: "contracts"
50
51
  },
51
52
  networks: {
53
+ 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
56
+ allowUnlimitedContractSize: true,
57
+ },
52
58
  besu: {
53
59
  url: "http://localhost:8545",
54
60
  accounts: keys,
@@ -23,12 +23,7 @@ export const SmtLibModule = buildModule("SmtLib", (m) => {
23
23
  const poseidon3 = m.library("Poseidon3", PoseidonArtifact(3));
24
24
  const poseidon5 = m.library("Poseidon5", PoseidonArtifact(5));
25
25
  const poseidon6 = m.library("Poseidon6", PoseidonArtifact(6));
26
- const smtLib = m.contract("SmtLib", [], {
27
- libraries: {
28
- PoseidonUnit2L: poseidon2,
29
- PoseidonUnit3L: poseidon3,
30
- },
31
- });
26
+ const smtLib = m.contract("SmtLib", []);
32
27
  return { smtLib, poseidon2, poseidon3, poseidon5, poseidon6 };
33
28
  });
34
29
 
@@ -40,6 +35,14 @@ export const DepositVerifierModule = buildModule(
40
35
  },
41
36
  );
42
37
 
38
+ export const DepositKycVerifierModule = buildModule(
39
+ "Groth16Verifier_DepositKyc",
40
+ (m) => {
41
+ const verifier = m.contract("Groth16Verifier_DepositKyc", []);
42
+ return { verifier };
43
+ },
44
+ );
45
+
43
46
  export const WithdrawNullifierVerifierModule = buildModule(
44
47
  "Groth16Verifier_WithdrawNullifier",
45
48
  (m) => {
@@ -36,10 +36,13 @@ const BurnVerifierModule = buildModule("Groth16Verifier_Burn", (m) => {
36
36
  return { verifier };
37
37
  });
38
38
 
39
- const BatchBurnVerifierModule = buildModule("Groth16Verifier_BurnBatch", (m) => {
40
- const verifier = m.contract("Groth16Verifier_BurnBatch", []);
41
- return { verifier };
42
- });
39
+ const BatchBurnVerifierModule = buildModule(
40
+ "Groth16Verifier_BurnBatch",
41
+ (m) => {
42
+ const verifier = m.contract("Groth16Verifier_BurnBatch", []);
43
+ return { verifier };
44
+ },
45
+ );
43
46
 
44
47
  export default buildModule("Zeto_AnonBurnable", (m) => {
45
48
  const { verifier } = m.useModule(VerifierModule);
@@ -50,9 +53,7 @@ export default buildModule("Zeto_AnonBurnable", (m) => {
50
53
  BatchWithdrawVerifierModule,
51
54
  );
52
55
  const { verifier: burnVerifier } = m.useModule(BurnVerifierModule);
53
- const { verifier: batchBurnVerifier } = m.useModule(
54
- BatchBurnVerifierModule,
55
- );
56
+ const { verifier: batchBurnVerifier } = m.useModule(BatchBurnVerifierModule);
56
57
  return {
57
58
  depositVerifier,
58
59
  withdrawVerifier,
@@ -35,10 +35,39 @@ const BatchVerifierModule = buildModule(
35
35
  },
36
36
  );
37
37
 
38
+ // Locked-input transfers reuse the plain {Groth16Verifier_AnonEnc} verifier
39
+ // (the same one the non-nullifier {Zeto_AnonEnc} token uses for its
40
+ // regular transfers), NOT a nullifier-aware verifier.
41
+ //
42
+ // Rationale: under the new ILockableCapability storage, locked UTXOs are
43
+ // kept in a flat per-lock mapping keyed by lockId — there is no SMT for
44
+ // locked UTXOs and the locked-input proof has no nullifier history to
45
+ // bind against. {Zeto_AnonEncNullifier.constructPublicInputs(...,
46
+ // inputsLocked = true)} reflects this by emitting public inputs as
47
+ // `[ecdhPublicKey, encryptedValues, inputCommitments, outputCommitments,
48
+ // encryptionNonce]` — exactly what `Groth16Verifier_AnonEnc` expects.
49
+ // The encryption witness (ECDH key + encrypted blob) still applies because
50
+ // the receiver still needs data availability for the post-spend outputs.
51
+ const LockVerifierModule = buildModule("Groth16Verifier_AnonEnc", (m) => {
52
+ const verifier = m.contract("Groth16Verifier_AnonEnc", []);
53
+ return { verifier };
54
+ });
55
+
56
+ // Batched (10-in / 10-out) twin of {LockVerifierModule}. Same rationale.
57
+ const BatchLockVerifierModule = buildModule(
58
+ "Groth16Verifier_AnonEncBatch",
59
+ (m) => {
60
+ const verifier = m.contract("Groth16Verifier_AnonEncBatch", []);
61
+ return { verifier };
62
+ },
63
+ );
64
+
38
65
  export default buildModule("Zeto_AnonEncNullifier", (m) => {
39
- const { smtLib, poseidon3 } = m.useModule(SmtLibModule);
66
+ const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule);
40
67
  const { verifier } = m.useModule(VerifierModule);
68
+ const { verifier: lockVerifier } = m.useModule(LockVerifierModule);
41
69
  const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
70
+ const { verifier: batchLockVerifier } = m.useModule(BatchLockVerifierModule);
42
71
  const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
43
72
  const { verifier: withdrawVerifier } = m.useModule(
44
73
  WithdrawNullifierVerifierModule,
@@ -51,9 +80,12 @@ export default buildModule("Zeto_AnonEncNullifier", (m) => {
51
80
  depositVerifier,
52
81
  withdrawVerifier,
53
82
  verifier,
83
+ lockVerifier,
54
84
  batchVerifier,
85
+ batchLockVerifier,
55
86
  batchWithdrawVerifier,
56
87
  smtLib,
88
+ poseidon2,
57
89
  poseidon3,
58
90
  };
59
91
  });
@@ -17,7 +17,7 @@
17
17
  import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
18
18
  import {
19
19
  SmtLibModule,
20
- DepositVerifierModule,
20
+ DepositKycVerifierModule,
21
21
  WithdrawNullifierVerifierModule,
22
22
  BatchWithdrawNullifierVerifierModule,
23
23
  } from "./lib/deps";
@@ -42,7 +42,7 @@ export default buildModule("Zeto_AnonEncNullifierKyc", (m) => {
42
42
  const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule);
43
43
  const { verifier } = m.useModule(VerifierModule);
44
44
  const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
45
- const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
45
+ const { verifier: depositVerifier } = m.useModule(DepositKycVerifierModule);
46
46
  const { verifier: withdrawVerifier } = m.useModule(
47
47
  WithdrawNullifierVerifierModule,
48
48
  );
@@ -45,7 +45,7 @@ const BatchVerifierModule = buildModule(
45
45
  );
46
46
 
47
47
  export default buildModule("Zeto_AnonEncNullifierNonRepudiation", (m) => {
48
- const { smtLib, poseidon3 } = m.useModule(SmtLibModule);
48
+ const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule);
49
49
  const { verifier } = m.useModule(VerifierModule);
50
50
  const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
51
51
  const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
@@ -62,6 +62,7 @@ export default buildModule("Zeto_AnonEncNullifierNonRepudiation", (m) => {
62
62
  batchVerifier,
63
63
  batchWithdrawVerifier,
64
64
  smtLib,
65
+ poseidon2,
65
66
  poseidon3,
66
67
  };
67
68
  });