@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,61 +15,479 @@
15
15
  // limitations under the License.
16
16
  pragma solidity ^0.8.27;
17
17
 
18
- import {Groth16Verifier_Deposit} from "../verifiers/verifier_deposit.sol";
19
- import {Commonlib} from "./common.sol";
18
+ import {IGroth16Verifier} from "./interfaces/IZetoVerifier.sol";
19
+ import {IZetoInitializable} from "./interfaces/IZetoInitializable.sol";
20
+ import {IZetoLockableCapability} from "./interfaces/IZetoLockableCapability.sol";
21
+ import {Commonlib} from "./common/common.sol";
20
22
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
21
- import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
23
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
24
+ import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
25
+ import {ZetoLockable} from "./zeto_lockable.sol";
26
+ import {IZetoStorage} from "./interfaces/IZetoStorage.sol";
22
27
 
23
28
  /// @title A sample implementation of a base Zeto fungible token contract
24
29
  /// @author Kaleido, Inc.
25
- /// @dev Defines the verifier library for checking UTXOs against a claimed value.
26
- abstract contract ZetoFungible is OwnableUpgradeable {
27
- // _depositVerifier library for checking UTXOs against a claimed value.
28
- // this can be used in the optional deposit calls to verify that
29
- // the UTXOs match the deposited value
30
- Groth16Verifier_Deposit internal _depositVerifier;
30
+ /// @dev Adds the ERC20-backed deposit/withdraw lifecycle and a multi-input
31
+ /// {transfer} flow on top of the lock-aware {ZetoLockable} base.
32
+ ///
33
+ /// The lock-lifecycle plumbing (state, modifiers, external entry
34
+ /// points, views) lives in {ZetoLockable}; this contract supplies
35
+ /// only the two ZK-circuit-shaped hooks
36
+ /// ({_doLockTransition} and {_transferLocked}) that pad
37
+ /// inputs/outputs to the next supported batch size before
38
+ /// {constructPublicInputs}.
39
+ ///
40
+ /// Inherits {ReentrancyGuardUpgradeable} so that {deposit} and
41
+ /// {withdraw}, which perform external ERC20 transfers, are
42
+ /// protected from reentrant calls (defense-in-depth on top of the
43
+ /// checks-effects-interactions ordering enforced inside those
44
+ /// functions).
45
+ abstract contract ZetoFungible is ZetoLockable, ReentrancyGuardUpgradeable {
46
+ using SafeERC20 for IERC20;
31
47
 
32
- error WithdrawArrayTooLarge(uint256 maxAllowed);
48
+ /// @dev Emitted when {setERC20} successfully binds the backing
49
+ /// ERC20. Indexed by the new token address so subscribers can
50
+ /// filter.
51
+ event ERC20Set(address indexed erc20);
33
52
 
34
- IERC20 internal _erc20;
53
+ /// @dev Thrown when {setERC20} is called with the zero address.
54
+ error ZeroERC20Address();
35
55
 
56
+ /// @dev Thrown when {setERC20} is called after the backing ERC20
57
+ /// has already been bound. {setERC20} is one-shot: once a
58
+ /// Zeto contract is paired with an ERC20, that pairing is
59
+ /// permanent for the lifetime of the proxy. This protects
60
+ /// depositors from having their backing token rug-pulled out
61
+ /// from under existing UTXOs.
62
+ error ERC20AlreadySet(address current);
63
+
64
+ /// @dev Deposit / withdraw verifiers and ERC20 pairing live in
65
+ /// {ZetoFungibleStorage} (ERC-7201 namespace `zeto.storage.ZetoFungible`).
66
+
67
+ /// @dev Initializer should only ever be called from a derived
68
+ /// contract's own `initializer`-guarded entrypoint, so the
69
+ /// `internal` visibility prevents external poking of the
70
+ /// lifecycle on a partially-initialized proxy.
36
71
  function __ZetoFungible_init(
37
- Groth16Verifier_Deposit depositVerifier
38
- ) public onlyInitializing {
39
- _depositVerifier = depositVerifier;
72
+ string calldata name_,
73
+ string calldata symbol_,
74
+ address initialOwner,
75
+ IZetoInitializable.VerifiersInfo calldata verifiers,
76
+ IZetoStorage storage_
77
+ ) internal onlyInitializing {
78
+ __ZetoCommon_init(name_, symbol_, initialOwner, verifiers, storage_);
79
+ __ReentrancyGuard_init();
80
+ ZetoFungibleStorage.Layout storage $ = ZetoFungibleStorage.layout();
81
+ $.depositVerifier = verifiers.depositVerifier;
82
+ $.withdrawVerifier = verifiers.withdrawVerifier;
83
+ $.batchWithdrawVerifier = verifiers.batchWithdrawVerifier;
40
84
  }
41
85
 
86
+ /**
87
+ * @dev Bind the ERC20 token that this Zeto contract will interact
88
+ * with for {deposit} and {withdraw}. One-shot: a subsequent
89
+ * call reverts with {ERC20AlreadySet}.
90
+ *
91
+ * Rationale: depositors trust that the asset backing their
92
+ * UTXOs is stable for the lifetime of the contract. Letting
93
+ * the owner swap the backing ERC20 after deposits exist would
94
+ * let them silently change what `withdraw` pays out. Making
95
+ * this one-shot removes that vector entirely while still
96
+ * allowing the operator to wire up the pairing post-deployment.
97
+ *
98
+ * @param erc20 The ERC20 token to be used. Must be non-zero.
99
+ *
100
+ * Emits {ERC20Set}.
101
+ */
42
102
  function setERC20(IERC20 erc20) public onlyOwner {
43
- _erc20 = erc20;
103
+ if (address(erc20) == address(0)) {
104
+ revert ZeroERC20Address();
105
+ }
106
+ ZetoFungibleStorage.Layout storage $ = ZetoFungibleStorage.layout();
107
+ if (address($.erc20Token) != address(0)) {
108
+ revert ERC20AlreadySet(address($.erc20Token));
109
+ }
110
+ $.erc20Token = erc20;
111
+ emit ERC20Set(address(erc20));
44
112
  }
45
113
 
46
- function _deposit(
47
- uint256 amount,
48
- uint256[] memory outputs,
49
- Commonlib.Proof calldata proof
114
+ /**
115
+ * @dev the main function of the contract, which transfers values from one account (represented by Babyjubjub public keys)
116
+ * to one or more receiver accounts (also represented by Babyjubjub public keys). One of the two nullifiers may be zero
117
+ * if the transaction only needs one UTXO to be spent. Equally one of the two outputs may be zero if the transaction
118
+ * only needs to create one new UTXO.
119
+ *
120
+ * @param inputs Array of nullifiers that are secretly bound to UTXOs to be spent by the transaction.
121
+ * @param outputs Array of new UTXOs to generate, for future transactions to spend.
122
+ * @param proof A zero knowledge proof that the submitter is authorized to spend the inputs, and
123
+ * that the outputs are valid in terms of obeying mass conservation rules.
124
+ *
125
+ * Emits a {UTXOTransfer} event.
126
+ */
127
+ function transfer(
128
+ uint256[] calldata inputs,
129
+ uint256[] calldata outputs,
130
+ bytes calldata proof,
131
+ bytes calldata data
50
132
  ) public virtual {
133
+ uint256[] memory lockedOutputs;
134
+ validateTransactionProposal(
135
+ inputs,
136
+ outputs,
137
+ lockedOutputs,
138
+ proof,
139
+ false
140
+ );
141
+ // Check and pad commitments
142
+ (
143
+ uint256[] memory paddedInputs,
144
+ uint256[] memory paddedOutputs
145
+ ) = checkAndPadCommitments(inputs, outputs);
146
+ // construct the public inputs for the proof verification
147
+ (
148
+ uint256[] memory publicInputs,
149
+ Commonlib.Proof memory proofStruct
150
+ ) = constructPublicInputs(paddedInputs, paddedOutputs, proof, false);
151
+ bool isBatch = (inputs.length > 2 || outputs.length > 2);
152
+ verifyProof(proofStruct, publicInputs, isBatch, false);
153
+ processInputsAndOutputs(paddedInputs, paddedOutputs, false);
154
+
155
+ emitTransferEvent(inputs, outputs, proof, data);
156
+ }
157
+
158
+ // ------------------------------------------------------------------
159
+ // ZetoLockable hook overrides — fungible flavour pads inputs and
160
+ // outputs to the next supported batch size before constructing the
161
+ // public-inputs array.
162
+ // ------------------------------------------------------------------
163
+
164
+ function _doLockTransition(
165
+ ZetoCreateLockArgs memory args
166
+ ) internal override {
167
+ validateTransactionProposal(
168
+ args.inputs,
169
+ args.outputs,
170
+ args.lockedOutputs,
171
+ args.proof,
172
+ false
173
+ );
174
+
175
+ // Combine the locked outputs and the regular outputs because the
176
+ // circuits do not differentiate them.
177
+ uint256[] memory allOutputs = new uint256[](
178
+ args.lockedOutputs.length + args.outputs.length
179
+ );
180
+ for (uint256 i = 0; i < args.lockedOutputs.length; i++) {
181
+ allOutputs[i] = args.lockedOutputs[i];
182
+ }
183
+ for (uint256 i = 0; i < args.outputs.length; i++) {
184
+ allOutputs[args.lockedOutputs.length + i] = args.outputs[i];
185
+ }
186
+
187
+ (
188
+ uint256[] memory paddedInputs,
189
+ uint256[] memory paddedOutputs
190
+ ) = checkAndPadCommitments(args.inputs, allOutputs);
191
+
192
+ (
193
+ uint256[] memory publicInputs,
194
+ Commonlib.Proof memory proofStruct
195
+ ) = constructPublicInputs(
196
+ paddedInputs,
197
+ paddedOutputs,
198
+ args.proof,
199
+ false
200
+ );
201
+
202
+ bool isBatch = (paddedInputs.length > 2 ||
203
+ args.outputs.length > 2 ||
204
+ args.lockedOutputs.length > 2);
205
+ verifyProof(proofStruct, publicInputs, isBatch, false);
206
+
207
+ processInputsAndOutputs(paddedInputs, args.outputs, false);
208
+ processLockedOutputs(args.lockedOutputs);
209
+ // The freshly-locked outputs all start under the lock creator
210
+ // as both owner and spender; record that on the per-UTXO
211
+ // projection so {locked} can report it without a reverse lookup.
212
+ _setLockDelegates(args.lockedOutputs, msg.sender);
213
+ }
214
+
215
+ function _transferLocked(
216
+ bytes32 /* lockId */,
217
+ uint256[] memory lockedInputs,
218
+ uint256[] memory lockedOutputs,
219
+ uint256[] memory outputs,
220
+ bytes memory proof,
221
+ bytes memory /* data */
222
+ ) internal override {
223
+ validateTransactionProposal(
224
+ lockedInputs,
225
+ outputs,
226
+ lockedOutputs,
227
+ proof,
228
+ true
229
+ );
230
+ // combine the locked outputs and the outputs, because the
231
+ // circuits do not care about the difference between locked and
232
+ // unlocked outputs
233
+ uint256[] memory allOutputs = new uint256[](
234
+ lockedOutputs.length + outputs.length
235
+ );
236
+ for (uint256 i = 0; i < lockedOutputs.length; i++) {
237
+ allOutputs[i] = lockedOutputs[i];
238
+ }
239
+ for (uint256 i = 0; i < outputs.length; i++) {
240
+ allOutputs[lockedOutputs.length + i] = outputs[i];
241
+ }
242
+ // Check and pad inputs and outputs based on the max size
243
+ (
244
+ uint256[] memory paddedInputs,
245
+ uint256[] memory paddedOutputs
246
+ ) = checkAndPadCommitments(lockedInputs, allOutputs);
247
+ // construct the public inputs for the proof verification
248
+ (
249
+ uint256[] memory publicInputs,
250
+ Commonlib.Proof memory proofStruct
251
+ ) = constructPublicInputs(paddedInputs, paddedOutputs, proof, true);
252
+ bool isBatch = (lockedInputs.length > 2 || allOutputs.length > 2);
253
+ verifyProof(proofStruct, publicInputs, isBatch, true);
254
+ processInputsAndOutputs(paddedInputs, paddedOutputs, true);
255
+ processLockedOutputs(lockedOutputs);
256
+ // Any newly-locked outputs produced by this spend default to the
257
+ // current spender as their delegate. (When `lockedOutputs` is
258
+ // empty -- the common case -- this is a no-op.)
259
+ _setLockDelegates(lockedOutputs, msg.sender);
260
+ }
261
+
262
+ /**
263
+ * @dev Deposit ERC20 tokens into the Zeto contract.
264
+ *
265
+ * @param amount The amount of ERC20 tokens to be deposited.
266
+ * @param outputs The UTXOs to be minted.
267
+ * @param proof The proof of the deposit.
268
+ * @param data Additional data to be passed to the deposit function.
269
+ *
270
+ * Emits a {UTXOMint} event.
271
+ */
272
+ function deposit(
273
+ uint256 amount,
274
+ uint256[] calldata outputs,
275
+ bytes calldata proof,
276
+ bytes calldata data
277
+ ) public nonReentrant {
278
+ // ---- Checks ----
279
+ validateOutputs(outputs);
280
+
51
281
  // verifies that the output UTXOs match the claimed value
52
282
  // to be deposited
283
+ (
284
+ uint256[] memory publicInputs,
285
+ Commonlib.Proof memory proofStruct
286
+ ) = constructPublicInputsForDeposit(amount, outputs, proof);
287
+ if (
288
+ !ZetoFungibleStorage.layout().depositVerifier.verify(
289
+ proofStruct.pA,
290
+ proofStruct.pB,
291
+ proofStruct.pC,
292
+ publicInputs
293
+ )
294
+ ) {
295
+ revert InvalidProof();
296
+ }
297
+
298
+ // ---- Effects ----
299
+ // Mint the UTXOs (commits the new outputs to storage and emits
300
+ // {UTXOMint}) before the external ERC20 call. This ensures that
301
+ // any reentrant call into this contract triggered by the ERC20
302
+ // transfer observes the new outputs as already-committed and
303
+ // cannot replay them.
304
+ _mint(outputs, data);
305
+
306
+ // ---- Interactions ----
307
+ // SafeERC20 handles non-standard tokens that return no value on
308
+ // success and reverts cleanly when the underlying call fails or
309
+ // returns false.
310
+ ZetoFungibleStorage.layout().erc20Token.safeTransferFrom(
311
+ msg.sender,
312
+ address(this),
313
+ amount
314
+ );
315
+ }
316
+
317
+ /**
318
+ * @dev Withdraw ERC20 tokens from the Zeto contract.
319
+ *
320
+ * @param amount The amount of ERC20 tokens to be withdrawn.
321
+ * @param inputs The UTXOs to be spent.
322
+ * @param output The UTXO to be minted.
323
+ * @param proof The proof of the withdrawal.
324
+ * @param data Additional data to be passed to the withdrawal
325
+ * function.
326
+ *
327
+ * Emits a {UTXOWithdraw} event.
328
+ */
329
+ function withdraw(
330
+ uint256 amount,
331
+ uint256[] calldata inputs,
332
+ uint256 output,
333
+ bytes calldata proof,
334
+ bytes calldata data
335
+ ) public nonReentrant {
336
+ uint256[] memory outputs = new uint256[](1);
337
+ outputs[0] = output;
338
+ uint256[] memory lockedOutputs;
339
+
340
+ // ---- Checks ----
341
+ validateTransactionProposal(
342
+ inputs,
343
+ outputs,
344
+ lockedOutputs,
345
+ proof,
346
+ false
347
+ );
348
+ // Check and pad inputs and outputs based on the max size
349
+ (
350
+ uint256[] memory paddedInputs,
351
+ uint256[] memory paddedOutputs
352
+ ) = checkAndPadCommitments(inputs, outputs);
353
+ (
354
+ uint256[] memory publicInputs,
355
+ Commonlib.Proof memory proofStruct
356
+ ) = constructPublicInputsForWithdraw(
357
+ amount,
358
+ paddedInputs,
359
+ output,
360
+ proof
361
+ );
362
+ ZetoFungibleStorage.Layout storage $ = ZetoFungibleStorage.layout();
363
+ IGroth16Verifier verifier = (inputs.length > 2)
364
+ ? $.batchWithdrawVerifier
365
+ : $.withdrawVerifier;
366
+ if (
367
+ !verifier.verify(
368
+ proofStruct.pA,
369
+ proofStruct.pB,
370
+ proofStruct.pC,
371
+ publicInputs
372
+ )
373
+ ) {
374
+ revert InvalidProof();
375
+ }
376
+
377
+ // ---- Effects ----
378
+ // Mark the input nullifiers as spent and commit the
379
+ // change-output before performing the external ERC20 transfer.
380
+ // Following checks-effects-interactions ensures a callback-style
381
+ // ERC20 cannot re-enter and double-spend the same nullifiers.
382
+ processInputsAndOutputs(paddedInputs, paddedOutputs, false);
383
+
384
+ // ---- Interactions ----
385
+ // SafeERC20 handles non-standard tokens that return no value on
386
+ // success and reverts cleanly when the underlying call fails or
387
+ // returns false.
388
+ ZetoFungibleStorage.layout().erc20Token.safeTransfer(
389
+ msg.sender,
390
+ amount
391
+ );
392
+
393
+ // Emitted after the transfer so that the on-chain event order
394
+ // remains ERC20.Transfer → UTXOWithdraw, matching listeners and
395
+ // tests built before the CEI reorder. Event emission is a pure
396
+ // log and does not affect security; the nullifier state was
397
+ // already committed above.
398
+ emit UTXOWithdraw(amount, inputs, output, msg.sender, data);
399
+ }
400
+
401
+ function emitTransferEvent(
402
+ uint256[] memory inputs,
403
+ uint256[] memory outputs,
404
+ bytes memory proof,
405
+ bytes memory data
406
+ ) internal virtual {
407
+ emit UTXOTransfer(inputs, outputs, msg.sender, data);
408
+ }
409
+
410
+ // this is a utility function that constructs the public inputs for a proof of a deposit() call.
411
+ // specific implementations of this function may be overridden by each token implementation
412
+ function constructPublicInputsForDeposit(
413
+ uint256 amount,
414
+ uint256[] memory outputs,
415
+ bytes memory proof
416
+ ) public virtual returns (uint256[] memory, Commonlib.Proof memory) {
417
+ Commonlib.Proof memory proofStruct = abi.decode(
418
+ proof,
419
+ (Commonlib.Proof)
420
+ );
53
421
  // construct the public inputs
54
- uint256[3] memory publicInputs;
422
+ uint256[] memory extra = extraInputsForDeposit();
423
+ uint256[] memory publicInputs = new uint256[](3 + extra.length);
55
424
  publicInputs[0] = amount;
56
425
  publicInputs[1] = outputs[0];
57
426
  publicInputs[2] = outputs[1];
427
+ for (uint256 i = 0; i < extra.length; i++) {
428
+ publicInputs[3 + i] = extra[i];
429
+ }
58
430
 
59
- // Check the proof
60
- require(
61
- _depositVerifier.verifyProof(
62
- proof.pA,
63
- proof.pB,
64
- proof.pC,
65
- publicInputs
66
- ),
67
- "Invalid proof"
68
- );
431
+ return (publicInputs, proofStruct);
432
+ }
433
+
434
+ function extraInputsForDeposit()
435
+ internal
436
+ view
437
+ virtual
438
+ returns (uint256[] memory)
439
+ {
440
+ return new uint256[](0);
441
+ }
69
442
 
70
- require(
71
- _erc20.transferFrom(msg.sender, address(this), amount),
72
- "Failed to transfer ERC20 tokens"
443
+ // this is a utility function that constructs the public inputs for a proof of a withdraw() call.
444
+ // specific implementations of this function may be overridden by each token implementation
445
+ function constructPublicInputsForWithdraw(
446
+ uint256 amount,
447
+ uint256[] memory inputs,
448
+ uint256 output,
449
+ bytes memory proof
450
+ ) internal virtual returns (uint256[] memory, Commonlib.Proof memory) {
451
+ Commonlib.Proof memory proofStruct = abi.decode(
452
+ proof,
453
+ (Commonlib.Proof)
73
454
  );
455
+ uint256 size = (inputs.length + 1 + 1); // inputs, output, and amount
456
+
457
+ uint256[] memory publicInputs = new uint256[](size);
458
+ uint256 piIndex = 0;
459
+
460
+ // copy output amount
461
+ publicInputs[piIndex++] = amount;
462
+
463
+ // copy input commitments
464
+ for (uint256 i = 0; i < inputs.length; i++) {
465
+ publicInputs[piIndex++] = inputs[i];
466
+ }
467
+
468
+ // copy output commitment
469
+ publicInputs[piIndex++] = output;
470
+
471
+ return (publicInputs, proofStruct);
472
+ }
473
+ }
474
+
475
+ /// @dev ERC-7201 (`erc7201:zeto.storage.ZetoFungible`): deposit/withdraw verifiers
476
+ /// and ERC20 pairing for this abstract contract.
477
+ library ZetoFungibleStorage {
478
+ struct Layout {
479
+ IGroth16Verifier depositVerifier;
480
+ IGroth16Verifier withdrawVerifier;
481
+ IGroth16Verifier batchWithdrawVerifier;
482
+ IERC20 erc20Token;
483
+ }
484
+
485
+ bytes32 private constant STORAGE_LOCATION =
486
+ 0x502fd05a4212f6b786ff372127438db29bf1c676e6268e59ca1feb2fae998d00;
487
+
488
+ function layout() internal pure returns (Layout storage $) {
489
+ assembly {
490
+ $.slot := STORAGE_LOCATION
491
+ }
74
492
  }
75
493
  }
@@ -0,0 +1,69 @@
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 {ZetoFungible} from "./zeto_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 fungible token contract
24
+ /// WITHOUT nullifiers. Each UTXO's spending status is explicitly tracked
25
+ /// in the {BaseStorage} ledger.
26
+ /// @author Kaleido, Inc.
27
+ /// @dev Sibling of {ZetoFungibleNullifier}. The two differ only in the storage
28
+ /// backend wired into {ZetoFungible}: this contract uses {BaseStorage},
29
+ /// which records `(utxo => UTXOStatus)` for both regular and locked UTXOs;
30
+ /// the nullifier sibling uses {NullifierStorage}, which records spent
31
+ /// nullifiers in a Sparse Merkle Tree.
32
+ ///
33
+ /// The locking lifecycle (createLock / updateLock / delegateLock /
34
+ /// spendLock / cancelLock) is inherited unchanged from {ZetoFungible}
35
+ /// and is therefore identical across both backends. The only contract
36
+ /// that differs between the two siblings is the leaf token (e.g.
37
+ /// Zeto_Anon vs Zeto_AnonNullifier), which supplies the
38
+ /// circuit-specific `constructPublicInputs` implementation.
39
+ ///
40
+ /// Future non-nullifier-only state belongs in {ZetoFungibleBaseStorage}
41
+ /// below — append fields there on upgrade instead of `__gap`.
42
+ abstract contract ZetoFungibleBase is ZetoFungible {
43
+ function __ZetoFungibleBase_init(
44
+ string calldata name_,
45
+ string calldata symbol_,
46
+ address initialOwner,
47
+ IZetoInitializable.VerifiersInfo calldata verifiers
48
+ ) internal onlyInitializing {
49
+ IZetoStorage storage_ = new BaseStorage();
50
+ __ZetoFungible_init(name_, symbol_, initialOwner, verifiers, storage_);
51
+ }
52
+ }
53
+
54
+ /// @dev ERC-7201 (`erc7201:zeto.storage.ZetoFungibleBase`): reserved for
55
+ /// non-nullifier fungible leaf extensions.
56
+ library ZetoFungibleBaseStorage {
57
+ struct Layout {
58
+ uint256 __reserved;
59
+ }
60
+
61
+ bytes32 private constant STORAGE_LOCATION =
62
+ 0x06a87037c58e5288ef9ee2ed0d7efb7c68b30748ac0ed4d5eb8700eb60966f00;
63
+
64
+ function layout() internal pure returns (Layout storage $) {
65
+ assembly {
66
+ $.slot := STORAGE_LOCATION
67
+ }
68
+ }
69
+ }