@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,72 +15,102 @@
15
15
  // limitations under the License.
16
16
  pragma solidity ^0.8.27;
17
17
 
18
- import {Groth16Verifier_Burn} from "../verifiers/verifier_burn.sol";
19
- import {Groth16Verifier_BurnBatch} from "../verifiers/verifier_burn_batch.sol";
20
- import {ZetoBase} from "./zeto_base.sol";
21
- import {Commonlib} from "./common.sol";
22
-
23
- uint256 constant BURN_INPUT_SIZE = 3;
24
- uint256 constant BATCH_BURN_INPUT_SIZE = 11;
18
+ import {IGroth16Verifier} from "./interfaces/IZetoVerifier.sol";
19
+ import {ZetoFungibleBase} from "./zeto_fungible_base.sol";
20
+ import {Commonlib} from "./common/common.sol";
25
21
 
26
22
  /// @title A feature implementation of a Zeto fungible token burn contract
27
23
  /// @author Kaleido, Inc.
28
- /// @dev Can be added to a Zeto fungible token contract to allow for burning of tokens.
29
- abstract contract ZetoFungibleBurnable is ZetoBase {
30
- Groth16Verifier_Burn internal _burnVerifier;
31
- Groth16Verifier_BurnBatch internal _batchBurnVerifier;
32
-
24
+ /// @dev Mixed-in alongside {ZetoFungibleBase} (the non-nullifier sibling of
25
+ /// {ZetoFungibleNullifier}) to add a {burn} entry point that destroys a
26
+ /// set of input UTXOs and produces a single output UTXO holding the
27
+ /// remainder. The burn proof attests that the inputs sum to
28
+ /// `output + burn_amount` while preserving owner-side range and
29
+ /// ownership invariants.
30
+ ///
31
+ /// The locking lifecycle is unaffected by burning: locked UTXOs cannot
32
+ /// be supplied as inputs because {validateInputs} (called from {_burn})
33
+ /// rejects locked entries via the underlying {BaseStorage} ledger.
34
+ abstract contract ZetoFungibleBurnable is ZetoFungibleBase {
35
+ /// @dev Internal-only so it can only be called from a derived
36
+ /// contract's own `initializer`-guarded entrypoint.
33
37
  function __ZetoFungibleBurnable_init(
34
- Groth16Verifier_Burn burnVerifier,
35
- Groth16Verifier_BurnBatch batchBurnVerifier
36
- ) public onlyInitializing {
37
- _burnVerifier = burnVerifier;
38
- _batchBurnVerifier = batchBurnVerifier;
38
+ IGroth16Verifier burnVerifier,
39
+ IGroth16Verifier batchBurnVerifier
40
+ ) internal onlyInitializing {
41
+ ZetoFungibleBurnableStorage.Layout storage $ = ZetoFungibleBurnableStorage
42
+ .layout();
43
+ $.burnVerifier = burnVerifier;
44
+ $.batchBurnVerifier = batchBurnVerifier;
39
45
  }
40
46
 
47
+ /**
48
+ * @dev Burn a set of inputs, leaving a single output UTXO that holds the
49
+ * remainder.
50
+ * @param inputs The input UTXOs to burn.
51
+ * @param output The single output UTXO holding the remainder.
52
+ * @param proof The Groth16 proof attesting to value conservation and
53
+ * owner-side range/ownership invariants.
54
+ * @param data Opaque data appended to the {UTXOBurn} event for off-chain
55
+ * consumers (e.g. correlation ids).
56
+ *
57
+ * Emits a {UTXOBurn} event.
58
+ */
41
59
  function burn(
42
- uint256[] memory inputs,
60
+ uint256[] calldata inputs,
43
61
  uint256 output,
44
62
  Commonlib.Proof calldata proof,
45
63
  bytes calldata data
46
64
  ) public virtual {
47
- // Check the proof
48
- if (inputs.length > 2) {
49
- // construct the public inputs for verifier
50
- uint256[BATCH_BURN_INPUT_SIZE] memory fixedSizeInputs;
51
- for (uint256 i = 0; i < inputs.length; i++) {
52
- fixedSizeInputs[i] = inputs[i];
53
- }
54
- fixedSizeInputs[BATCH_BURN_INPUT_SIZE - 1] = output;
55
- // Check the proof
56
- require(
57
- _batchBurnVerifier.verifyProof(
58
- proof.pA,
59
- proof.pB,
60
- proof.pC,
61
- fixedSizeInputs
62
- ),
63
- "Invalid proof"
64
- );
65
- } else {
66
- // construct the public inputs for verifier
67
- uint256[BURN_INPUT_SIZE] memory fixedSizeInputs;
68
- for (uint256 i = 0; i < inputs.length; i++) {
69
- fixedSizeInputs[i] = inputs[i];
70
- }
71
- fixedSizeInputs[BURN_INPUT_SIZE - 1] = output;
72
- // Check the proof
73
- require(
74
- _burnVerifier.verifyProof(
75
- proof.pA,
76
- proof.pB,
77
- proof.pC,
78
- fixedSizeInputs
79
- ),
80
- "Invalid proof"
81
- );
65
+ uint256[] memory publicInputs = new uint256[](inputs.length + 1);
66
+ for (uint256 i = 0; i < inputs.length; i++) {
67
+ publicInputs[i] = inputs[i];
68
+ }
69
+ publicInputs[inputs.length] = output;
70
+
71
+ ZetoFungibleBurnableStorage.Layout storage $ = ZetoFungibleBurnableStorage
72
+ .layout();
73
+ IGroth16Verifier verifier = (inputs.length > 2)
74
+ ? $.batchBurnVerifier
75
+ : $.burnVerifier;
76
+ if (!verifier.verify(proof.pA, proof.pB, proof.pC, publicInputs)) {
77
+ revert InvalidProof();
82
78
  }
83
79
 
84
80
  _burn(inputs, output, data);
85
81
  }
82
+
83
+ /// @dev Validate inputs/outputs, then transition state and emit the
84
+ /// {UTXOBurn} event. Marked virtual so concrete tokens can wrap
85
+ /// with additional bookkeeping if needed.
86
+ function _burn(
87
+ uint256[] memory inputs,
88
+ uint256 output,
89
+ bytes calldata data
90
+ ) internal virtual {
91
+ validateInputs(inputs, false);
92
+ uint256[] memory outputStates = new uint256[](1);
93
+ outputStates[0] = output;
94
+ validateOutputs(outputStates);
95
+ processInputs(inputs, false);
96
+ processOutputs(outputStates);
97
+ emit UTXOBurn(inputs, output, msg.sender, data);
98
+ }
99
+ }
100
+
101
+ /// @dev ERC-7201 (`erc7201:zeto.storage.ZetoFungibleBurnable`).
102
+ library ZetoFungibleBurnableStorage {
103
+ struct Layout {
104
+ IGroth16Verifier burnVerifier;
105
+ IGroth16Verifier batchBurnVerifier;
106
+ }
107
+
108
+ bytes32 private constant STORAGE_LOCATION =
109
+ 0x99d1a11894f655177598fbad145350332923053ddb779beb1721e5203b8acf00;
110
+
111
+ function layout() internal pure returns (Layout storage $) {
112
+ assembly {
113
+ $.slot := STORAGE_LOCATION
114
+ }
115
+ }
86
116
  }
@@ -15,30 +15,49 @@
15
15
  // limitations under the License.
16
16
  pragma solidity ^0.8.27;
17
17
 
18
- import {Groth16Verifier_BurnNullifier} from "../verifiers/verifier_burn_nullifier.sol";
19
- import {Groth16Verifier_BurnNullifierBatch} from "../verifiers/verifier_burn_nullifier_batch.sol";
20
- import {ZetoNullifier} from "./zeto_nullifier.sol";
21
- import {Commonlib} from "./common.sol";
18
+ import {IGroth16Verifier} from "./interfaces/IZetoVerifier.sol";
19
+ import {ZetoFungibleNullifier} from "./zeto_fungible_nullifier.sol";
20
+ import {Commonlib} from "./common/common.sol";
22
21
 
23
- uint256 constant BURN_INPUT_SIZE = 6;
24
- uint256 constant BATCH_BURN_INPUT_SIZE = 22;
25
-
26
- /// @title A feature implementation of a Zeto fungible token burn contract
22
+ /// @title A feature implementation of a Zeto fungible token burn contract for nullifier-based variants
27
23
  /// @author Kaleido, Inc.
28
- /// @dev Can be added to a Zeto fungible token contract to allow for burning of tokens.
29
- abstract contract ZetoFungibleBurnableWithNullifiers is ZetoNullifier {
30
- Groth16Verifier_BurnNullifier internal _burnVerifier;
31
- Groth16Verifier_BurnNullifierBatch internal _batchBurnVerifier;
32
-
33
- function __ZetoFungibleBurnableWithNullifiers_init(
34
- Groth16Verifier_BurnNullifier burnVerifier,
35
- Groth16Verifier_BurnNullifierBatch batchBurnVerifier
36
- ) public onlyInitializing {
37
- _burnVerifier = burnVerifier;
38
- _batchBurnVerifier = batchBurnVerifier;
24
+ /// @dev Mixed-in alongside {ZetoFungibleNullifier} to add a {burn} entry
25
+ /// point that destroys a set of input nullifiers and produces a single
26
+ /// output UTXO holding the remainder. The burn proof attests that the
27
+ /// pre-image UTXOs sum to `output + burn_amount` while preserving
28
+ /// owner-side range and ownership invariants and proving membership
29
+ /// against the historical SMT `root`.
30
+ ///
31
+ /// This is the nullifier-flow companion to {ZetoFungibleBurnable}.
32
+ /// Unlike that contract:
33
+ /// - inputs are nullifiers, not UTXOs;
34
+ /// - the SMT `root` is a separate `burn` argument (not encoded in the
35
+ /// proof bytes), so `burn` validates it explicitly;
36
+ /// - the locking lifecycle is unaffected by burning because
37
+ /// {NullifierStorage.validateInputs} (called from {_burn}) only
38
+ /// accepts unspent nullifiers, and locked UTXOs live in a disjoint
39
+ /// part of the ledger.
40
+ abstract contract ZetoFungibleBurnableNullifier is ZetoFungibleNullifier {
41
+ /// @dev Internal-only so it can only be called from a derived
42
+ /// contract's own `initializer`-guarded entrypoint.
43
+ function __ZetoFungibleBurnableNullifier_init(
44
+ IGroth16Verifier burnVerifier,
45
+ IGroth16Verifier batchBurnVerifier
46
+ ) internal onlyInitializing {
47
+ ZetoFungibleBurnableNullifierStorage.Layout
48
+ storage $ = ZetoFungibleBurnableNullifierStorage.layout();
49
+ $.burnVerifier = burnVerifier;
50
+ $.batchBurnVerifier = batchBurnVerifier;
39
51
  }
40
52
 
41
- function constructPublicInputs(
53
+ /**
54
+ * @dev Construct the burn-circuit public-inputs vector. Layout:
55
+ * [nullifiers..., root, enabled-flags..., output]
56
+ * where `enabled-flags[i]` is 1 if `nullifiers[i] != 0` else 0,
57
+ * mirroring the convention used in
58
+ * {Zeto_AnonNullifier.constructPublicInputs}.
59
+ */
60
+ function constructBurnPublicInputs(
42
61
  uint256[] memory nullifiers,
43
62
  uint256 output,
44
63
  uint256 root
@@ -46,78 +65,102 @@ abstract contract ZetoFungibleBurnableWithNullifiers is ZetoNullifier {
46
65
  publicInputs = new uint256[](nullifiers.length * 2 + 2);
47
66
  uint256 piIndex = 0;
48
67
 
49
- // copy input commitments
50
68
  for (uint256 i = 0; i < nullifiers.length; i++) {
51
69
  publicInputs[piIndex++] = nullifiers[i];
52
70
  }
53
-
54
- // copy root
55
71
  publicInputs[piIndex++] = root;
56
-
57
- // populate enables
58
72
  for (uint256 i = 0; i < nullifiers.length; i++) {
59
73
  publicInputs[piIndex++] = (nullifiers[i] == 0) ? 0 : 1;
60
74
  }
61
-
62
75
  publicInputs[piIndex] = output;
63
76
 
64
77
  return publicInputs;
65
78
  }
66
79
 
80
+ /**
81
+ * @dev Burn a set of nullifier-pre-image UTXOs, leaving a single output
82
+ * UTXO that holds the remainder.
83
+ * @param nullifiers The nullifiers of the input UTXOs being burned.
84
+ * @param output The single output UTXO holding the remainder.
85
+ * @param root The historical SMT root the proof is committed against.
86
+ * Validated against the on-chain root history before state
87
+ * transitions are applied.
88
+ * @param proof The Groth16 proof attesting to value conservation,
89
+ * owner-side range/ownership invariants, and membership of
90
+ * the input UTXOs in the SMT identified by `root`.
91
+ * @param data Opaque data appended to the {UTXOBurn} event for off-chain
92
+ * consumers (e.g. correlation ids).
93
+ *
94
+ * Emits a {UTXOBurn} event.
95
+ */
67
96
  function burn(
68
- uint256[] memory nullifiers,
97
+ uint256[] calldata nullifiers,
69
98
  uint256 output,
70
99
  uint256 root,
71
100
  Commonlib.Proof calldata proof,
72
101
  bytes calldata data
73
102
  ) public virtual {
74
- // Check the proof
75
- if (nullifiers.length > 2) {
76
- // construct the public inputs for verifier
77
- uint256[] memory publicInputs = constructPublicInputs(
78
- nullifiers,
79
- output,
80
- root
81
- );
82
- // construct the public inputs for verifier
83
- uint256[BATCH_BURN_INPUT_SIZE] memory fixedSizeInputs;
84
- for (uint256 i = 0; i < fixedSizeInputs.length; i++) {
85
- fixedSizeInputs[i] = publicInputs[i];
86
- }
87
- // Check the proof
88
- require(
89
- _batchBurnVerifier.verifyProof(
90
- proof.pA,
91
- proof.pB,
92
- proof.pC,
93
- fixedSizeInputs
94
- ),
95
- "Invalid proof"
96
- );
97
- } else {
98
- // construct the public inputs for verifier
99
- uint256[] memory publicInputs = constructPublicInputs(
100
- nullifiers,
101
- output,
102
- root
103
- );
104
- // construct the public inputs for verifier
105
- uint256[BURN_INPUT_SIZE] memory fixedSizeInputs;
106
- for (uint256 i = 0; i < fixedSizeInputs.length; i++) {
107
- fixedSizeInputs[i] = publicInputs[i];
108
- }
109
- // Check the proof
110
- require(
111
- _burnVerifier.verifyProof(
112
- proof.pA,
113
- proof.pB,
114
- proof.pC,
115
- fixedSizeInputs
116
- ),
117
- "Invalid proof"
118
- );
103
+ uint256[] memory publicInputs = constructBurnPublicInputs(
104
+ nullifiers,
105
+ output,
106
+ root
107
+ );
108
+
109
+ ZetoFungibleBurnableNullifierStorage.Layout
110
+ storage $ = ZetoFungibleBurnableNullifierStorage.layout();
111
+ IGroth16Verifier verifier = (nullifiers.length > 2)
112
+ ? $.batchBurnVerifier
113
+ : $.burnVerifier;
114
+ if (!verifier.verify(proof.pA, proof.pB, proof.pC, publicInputs)) {
115
+ revert InvalidProof();
119
116
  }
120
117
 
121
118
  _burn(nullifiers, output, root, data);
122
119
  }
120
+
121
+ /**
122
+ * @dev Validate inputs/outputs/root, then transition state and emit the
123
+ * {UTXOBurn} event. Marked virtual so concrete tokens can wrap
124
+ * with additional bookkeeping if needed.
125
+ *
126
+ * Unlike the historical implementation on `main`, this consumes
127
+ * the nullifiers via {processInputs} and inserts the output UTXO
128
+ * into the SMT via {processOutputs}; without those calls the
129
+ * burned UTXOs would remain spendable and the remainder UTXO
130
+ * would not be a member of the tree.
131
+ */
132
+ function _burn(
133
+ uint256[] memory nullifiers,
134
+ uint256 output,
135
+ uint256 root,
136
+ bytes calldata data
137
+ ) internal virtual {
138
+ validateInputs(nullifiers, false);
139
+ uint256[] memory outputStates = new uint256[](1);
140
+ outputStates[0] = output;
141
+ validateOutputs(outputStates);
142
+ validateRoot(root);
143
+
144
+ processInputs(nullifiers, false);
145
+ processOutputs(outputStates);
146
+
147
+ emit UTXOBurn(nullifiers, output, msg.sender, data);
148
+ }
149
+ }
150
+
151
+ /// @dev ERC-7201 (`erc7201:zeto.storage.ZetoFungibleBurnableNullifier`).
152
+ library ZetoFungibleBurnableNullifierStorage {
153
+ struct Layout {
154
+ IGroth16Verifier burnVerifier;
155
+ IGroth16Verifier batchBurnVerifier;
156
+ }
157
+
158
+ bytes32 private constant STORAGE_LOCATION =
159
+ 0x3ec1ff2ff5e1c50bcb38c7183cc1e67f5f6626ff8f32ea7dbc84147e4b393500;
160
+
161
+ function layout() internal pure returns (Layout storage $) {
162
+ assembly {
163
+ $.slot := STORAGE_LOCATION
164
+ }
165
+ }
123
166
  }
@@ -0,0 +1,167 @@
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 {Commonlib} from "./common/common.sol";
19
+ import {IZeto, MAX_SMT_DEPTH} from "./interfaces/IZeto.sol";
20
+ import {IZetoInitializable} from "./interfaces/IZetoInitializable.sol";
21
+ import {ZetoFungible} from "./zeto_fungible.sol";
22
+ import {IZetoStorage} from "./interfaces/IZetoStorage.sol";
23
+ import {NullifierStorage} from "./storage/nullifier.sol";
24
+
25
+ /// @title A sample base implementation of a Zeto based token contract with nullifiers
26
+ /// @author Kaleido, Inc.
27
+ /// @dev Implements common functionalities of Zeto based tokens using nullifiers
28
+ abstract contract ZetoFungibleNullifier is ZetoFungible {
29
+ function __ZetoFungibleNullifier_init(
30
+ string calldata name_,
31
+ string calldata symbol_,
32
+ address initialOwner,
33
+ IZetoInitializable.VerifiersInfo calldata verifiers
34
+ ) internal onlyInitializing {
35
+ IZetoStorage storage_ = new NullifierStorage();
36
+ __ZetoFungible_init(name_, symbol_, initialOwner, verifiers, storage_);
37
+ }
38
+
39
+ function constructPublicInputsForWithdraw(
40
+ uint256 amount,
41
+ uint256[] memory nullifiers,
42
+ uint256 output,
43
+ bytes memory proof
44
+ ) internal override returns (uint256[] memory, Commonlib.Proof memory) {
45
+ // Decode the proof to extract root and proof structure. Root
46
+ // validation is performed here (rather than in a separate
47
+ // pre-decode hop in {validateTransactionProposal}, see L-4) so the
48
+ // proof bytes are decoded exactly once per withdraw call.
49
+ (uint256 root, Commonlib.Proof memory proofStruct) = abi.decode(
50
+ proof,
51
+ (uint256, Commonlib.Proof)
52
+ );
53
+ validateRoot(root);
54
+
55
+ // Calculate the total size needed for public inputs
56
+ uint256 size = _calculateWithdrawPublicInputsSize(nullifiers);
57
+
58
+ // Create and populate the public inputs array
59
+ uint256[] memory publicInputs = new uint256[](size);
60
+ _fillWithdrawPublicInputs(
61
+ publicInputs,
62
+ amount,
63
+ nullifiers,
64
+ root,
65
+ output
66
+ );
67
+
68
+ return (publicInputs, proofStruct);
69
+ }
70
+
71
+ function _calculateWithdrawPublicInputsSize(
72
+ uint256[] memory nullifiers
73
+ ) internal pure returns (uint256 size) {
74
+ size = (nullifiers.length * 2) + 3; // nullifiers, enabled flags, amount, root, output
75
+ }
76
+
77
+ function _fillWithdrawPublicInputs(
78
+ uint256[] memory publicInputs,
79
+ uint256 amount,
80
+ uint256[] memory nullifiers,
81
+ uint256 root,
82
+ uint256 output
83
+ ) internal pure {
84
+ uint256 piIndex = 0;
85
+
86
+ // Copy withdrawal amount
87
+ piIndex = _fillWithdrawAmount(publicInputs, amount, piIndex);
88
+
89
+ // Copy nullifiers
90
+ piIndex = _fillWithdrawNullifiers(publicInputs, nullifiers, piIndex);
91
+
92
+ // Copy root
93
+ piIndex = _fillWithdrawRoot(publicInputs, root, piIndex);
94
+
95
+ // Populate enabled flags
96
+ piIndex = _fillWithdrawEnabledFlags(publicInputs, nullifiers, piIndex);
97
+
98
+ // Copy output commitment
99
+ _fillWithdrawOutput(publicInputs, output, piIndex);
100
+ }
101
+
102
+ function _fillWithdrawAmount(
103
+ uint256[] memory publicInputs,
104
+ uint256 amount,
105
+ uint256 startIndex
106
+ ) internal pure returns (uint256 nextIndex) {
107
+ publicInputs[startIndex] = amount;
108
+ return startIndex + 1;
109
+ }
110
+
111
+ function _fillWithdrawNullifiers(
112
+ uint256[] memory publicInputs,
113
+ uint256[] memory nullifiers,
114
+ uint256 startIndex
115
+ ) internal pure returns (uint256 nextIndex) {
116
+ uint256 piIndex = startIndex;
117
+ for (uint256 i = 0; i < nullifiers.length; i++) {
118
+ publicInputs[piIndex++] = nullifiers[i];
119
+ }
120
+ return piIndex;
121
+ }
122
+
123
+ function _fillWithdrawRoot(
124
+ uint256[] memory publicInputs,
125
+ uint256 root,
126
+ uint256 startIndex
127
+ ) internal pure returns (uint256 nextIndex) {
128
+ publicInputs[startIndex] = root;
129
+ return startIndex + 1;
130
+ }
131
+
132
+ function _fillWithdrawEnabledFlags(
133
+ uint256[] memory publicInputs,
134
+ uint256[] memory nullifiers,
135
+ uint256 startIndex
136
+ ) internal pure returns (uint256 nextIndex) {
137
+ uint256 piIndex = startIndex;
138
+ for (uint256 i = 0; i < nullifiers.length; i++) {
139
+ publicInputs[piIndex++] = (nullifiers[i] == 0) ? 0 : 1;
140
+ }
141
+ return piIndex;
142
+ }
143
+
144
+ function _fillWithdrawOutput(
145
+ uint256[] memory publicInputs,
146
+ uint256 output,
147
+ uint256 startIndex
148
+ ) internal pure {
149
+ publicInputs[startIndex] = output;
150
+ }
151
+ }
152
+
153
+ /// @dev ERC-7201 (`erc7201:zeto.storage.ZetoFungibleNullifier`).
154
+ library ZetoFungibleNullifierStorage {
155
+ struct Layout {
156
+ uint256 __reserved;
157
+ }
158
+
159
+ bytes32 private constant STORAGE_LOCATION =
160
+ 0xa9ddb64cb7b603a6a83651e5155a95c052516a16afcb7569681d49e92a59ab00;
161
+
162
+ function layout() internal pure returns (Layout storage $) {
163
+ assembly {
164
+ $.slot := STORAGE_LOCATION
165
+ }
166
+ }
167
+ }