@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,7 +15,12 @@
15
15
  // limitations under the License.
16
16
 
17
17
  import { ethers, network } from "hardhat";
18
- import { ContractTransactionReceipt, Signer, BigNumberish } from "ethers";
18
+ import {
19
+ ContractTransactionReceipt,
20
+ Signer,
21
+ BigNumberish,
22
+ AbiCoder,
23
+ } from "ethers";
19
24
  import { expect } from "chai";
20
25
  import {
21
26
  loadCircuit,
@@ -46,8 +51,10 @@ import {
46
51
  } from "./lib/utils";
47
52
  import {
48
53
  loadProvingKeys,
49
- prepareDepositProof,
54
+ prepareDepositKycProof,
50
55
  prepareNullifierWithdrawProof,
56
+ encodeToBytesForDeposit,
57
+ encodeToBytesForWithdraw,
51
58
  } from "./utils";
52
59
  import { deployZeto } from "./lib/deploy";
53
60
  const poseidonHash = Poseidon.poseidon4;
@@ -91,11 +98,17 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
91
98
 
92
99
  ({ deployer, zeto, erc20 } = await deployZeto("Zeto_AnonEncNullifierKyc"));
93
100
 
94
- const tx2 = await zeto.connect(deployer).register(Alice.babyJubPublicKey, '0x');
101
+ const tx2 = await zeto
102
+ .connect(deployer)
103
+ .register(Alice.babyJubPublicKey, "0x");
95
104
  const result1 = await tx2.wait();
96
- const tx3 = await zeto.connect(deployer).register(Bob.babyJubPublicKey, '0x');
105
+ const tx3 = await zeto
106
+ .connect(deployer)
107
+ .register(Bob.babyJubPublicKey, "0x");
97
108
  const result2 = await tx3.wait();
98
- const tx4 = await zeto.connect(deployer).register(Charlie.babyJubPublicKey, '0x');
109
+ const tx4 = await zeto
110
+ .connect(deployer)
111
+ .register(Charlie.babyJubPublicKey, "0x");
99
112
  const result3 = await tx4.wait();
100
113
 
101
114
  const storage1 = new InMemoryDB(str2Bytes("alice"));
@@ -225,12 +238,13 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
225
238
 
226
239
  const signerAddress = await Alice.signer.getAddress();
227
240
  const events = parseUTXOEvents(zeto, result.txResult!);
228
- expect(events[0].submitter).to.equal(signerAddress);
229
- expect(events[0].inputs).to.deep.equal(nullifiers.map((n) => n.hash));
241
+ const event = events[0];
242
+ expect(event.submitter).to.equal(signerAddress);
243
+ expect(event.inputs).to.deep.equal(nullifiers.map((n) => n.hash));
230
244
 
231
- const incomingUTXOs: any = events[0].outputs;
245
+ const incomingUTXOs: any = event.outputs;
232
246
 
233
- const ecdhPublicKey = events[0].ecdhPublicKey;
247
+ const ecdhPublicKey = event.ecdhPublicKey;
234
248
  // check the non-empty output hashes are correct
235
249
  for (let i = 0; i < outputUtxos.length; i++) {
236
250
  const utxoOwner = outputOwners[i];
@@ -239,9 +253,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
239
253
  ecdhPublicKey,
240
254
  );
241
255
  const plainText = poseidonDecrypt(
242
- events[0].encryptedValues.slice(4 * i, 4 * i + 4),
256
+ event.encryptedValues.slice(4 * i, 4 * i + 4),
243
257
  sharedKey,
244
- events[0].encryptionNonce,
258
+ event.encryptionNonce,
245
259
  2,
246
260
  );
247
261
  expect(plainText).to.deep.equal(
@@ -259,11 +273,6 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
259
273
  await smtUnregistered.add(incomingUTXOs[i], incomingUTXOs[i]);
260
274
  }
261
275
 
262
- // check empty values, salt and hashes are empty
263
- for (let i = outputUtxos.length; i < 10; i++) {
264
- expect(incomingUTXOs[i]).to.equal(0);
265
- }
266
-
267
276
  // mint sufficient balance in Zeto contract address for Alice to withdraw
268
277
  const mintTx = await erc20.connect(deployer).mint(zeto, 3);
269
278
  await mintTx.wait();
@@ -316,8 +325,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
316
325
  3,
317
326
  _withdrawNullifiers,
318
327
  withdrawCommitments[0],
319
- root.bigInt(),
320
- withdrawEncodedProof,
328
+ encodeToBytesForWithdraw(root.bigInt(), withdrawEncodedProof),
321
329
  "0x",
322
330
  );
323
331
  await tx.wait();
@@ -339,13 +347,29 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
339
347
 
340
348
  utxo100 = newUTXO(100, Alice);
341
349
  utxo0 = newUTXO(0, Alice);
342
- const { outputCommitments, encodedProof } = await prepareDepositProof(
350
+ const identitiesRoot = await smtKyc.root();
351
+ const proof3 = await smtKyc.generateCircomVerifierProof(
352
+ kycHash(Alice.babyJubPublicKey),
353
+ identitiesRoot,
354
+ );
355
+ const identitiesMerkleProofs = [
356
+ proof3.siblings.map((s) => s.bigInt()),
357
+ proof3.siblings.map((s) => s.bigInt()),
358
+ ];
359
+ const { outputCommitments, encodedProof } = await prepareDepositKycProof(
343
360
  Alice,
344
361
  [utxo100, utxo0],
362
+ identitiesRoot.bigInt(),
363
+ identitiesMerkleProofs,
345
364
  );
346
365
  const tx2 = await zeto
347
366
  .connect(Alice.signer)
348
- .deposit(100, outputCommitments, encodedProof, "0x");
367
+ .deposit(
368
+ 100,
369
+ outputCommitments,
370
+ encodeToBytesForDeposit(encodedProof),
371
+ "0x",
372
+ );
349
373
  await tx2.wait();
350
374
 
351
375
  await smtAlice.add(utxo100.hash, utxo100.hash);
@@ -444,20 +468,21 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
444
468
  // Bob parses the UTXOs from the onchain event
445
469
  const signerAddress = await Alice.signer.getAddress();
446
470
  const events = parseUTXOEvents(zeto, result2.txResult!);
447
- expect(events[0].submitter).to.equal(signerAddress);
448
- expect(events[0].inputs).to.deep.equal([nullifier1.hash, nullifier2.hash]);
449
- expect(events[0].outputs).to.deep.equal([_utxo3.hash, utxo4.hash]);
450
- await smtBob.add(events[0].outputs[0], events[0].outputs[0]);
451
- await smtBob.add(events[0].outputs[1], events[0].outputs[1]);
452
-
453
- const ecdhPublicKey = events[0].ecdhPublicKey;
471
+ const event = events[0];
472
+ expect(event.submitter).to.equal(signerAddress);
473
+ expect(event.inputs).to.deep.equal([nullifier1.hash, nullifier2.hash]);
474
+ expect(event.outputs).to.deep.equal([_utxo3.hash, utxo4.hash]);
475
+ await smtBob.add(event.outputs[0], event.outputs[0]);
476
+ await smtBob.add(event.outputs[1], event.outputs[1]);
477
+
478
+ const ecdhPublicKey = event.ecdhPublicKey;
454
479
  // Bob reconstructs the shared key using his private key and ephemeral public key
455
480
 
456
481
  const sharedKey = genEcdhSharedKey(Bob.babyJubPrivateKey, ecdhPublicKey);
457
482
  const plainText = poseidonDecrypt(
458
- events[0].encryptedValues.slice(0, 4),
483
+ event.encryptedValues.slice(0, 4),
459
484
  sharedKey,
460
- events[0].encryptionNonce,
485
+ event.encryptionNonce,
461
486
  2,
462
487
  );
463
488
  expect(plainText).to.deep.equal(result2.expectedPlainText.slice(0, 2));
@@ -522,8 +547,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
522
547
 
523
548
  // Alice gets the new UTXOs from the onchain event and keeps the local SMT in sync
524
549
  const events = parseUTXOEvents(zeto, result.txResult!);
525
- await smtAlice.add(events[0].outputs[0], events[0].outputs[0]);
526
- await smtAlice.add(events[0].outputs[1], events[0].outputs[1]);
550
+ const event = events[0];
551
+ await smtAlice.add(event.outputs[0], event.outputs[0]);
552
+ await smtAlice.add(event.outputs[1], event.outputs[1]);
527
553
  }).timeout(600000);
528
554
 
529
555
  it("Alice withdraws her UTXOs to ERC20 tokens should succeed", async function () {
@@ -564,8 +590,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
564
590
  80,
565
591
  nullifiers,
566
592
  outputCommitments[0],
567
- root.bigInt(),
568
- encodedProof,
593
+ encodeToBytesForWithdraw(root.bigInt(), encodedProof),
569
594
  "0x",
570
595
  );
571
596
  await tx.wait();
@@ -583,8 +608,30 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
583
608
  describe("unregistered user cases", function () {
584
609
  let unregisteredUtxo100: UTXO;
585
610
  let unregisteredUtxo0: UTXO;
611
+ let smtKycUnregistered: Merkletree;
612
+
613
+ before(async function () {
614
+ const storage5 = new InMemoryDB(str2Bytes("unregisteredKyc"));
615
+ smtKycUnregistered = new Merkletree(storage5, true, 10);
616
+
617
+ // add the unregistered user to the local KYC SMT, but not to the onchain SMT
618
+ await smtKycUnregistered.add(
619
+ kycHash(unregistered.babyJubPublicKey),
620
+ kycHash(unregistered.babyJubPublicKey),
621
+ );
622
+ });
623
+
624
+ it("deposit by an unregistered user should fail", async function () {
625
+ const identitiesRoot = await smtKycUnregistered.root();
626
+ const proof3 = await smtKycUnregistered.generateCircomVerifierProof(
627
+ kycHash(unregistered.babyJubPublicKey),
628
+ identitiesRoot,
629
+ );
630
+ const identitiesMerkleProofs = [
631
+ proof3.siblings.map((s) => s.bigInt()),
632
+ proof3.siblings.map((s) => s.bigInt()),
633
+ ];
586
634
 
587
- it("deposit by an unregistered user should succeed", async function () {
588
635
  const tx = await erc20
589
636
  .connect(deployer)
590
637
  .mint(unregistered.ethAddress, 100);
@@ -596,150 +643,22 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
596
643
 
597
644
  unregisteredUtxo100 = newUTXO(100, unregistered);
598
645
  unregisteredUtxo0 = newUTXO(0, unregistered);
599
- const { outputCommitments, encodedProof } = await prepareDepositProof(
646
+ const { outputCommitments, encodedProof } = await prepareDepositKycProof(
600
647
  unregistered,
601
648
  [unregisteredUtxo100, unregisteredUtxo0],
649
+ identitiesRoot.bigInt(),
650
+ identitiesMerkleProofs,
602
651
  );
603
- const tx2 = await zeto
604
- .connect(unregistered.signer)
605
- .deposit(100, outputCommitments, encodedProof, "0x");
606
- await tx2.wait();
607
-
608
- // Alice tracks the UTXO inside the SMT
609
- await smtAlice.add(unregisteredUtxo100.hash, unregisteredUtxo100.hash);
610
- await smtAlice.add(unregisteredUtxo0.hash, unregisteredUtxo0.hash);
611
- // Bob also locally tracks the UTXOs inside the SMT
612
- await smtBob.add(unregisteredUtxo100.hash, unregisteredUtxo100.hash);
613
- await smtBob.add(unregisteredUtxo0.hash, unregisteredUtxo0.hash);
614
- });
615
-
616
- it("transfer from an unregistered user should fail", async function () {
617
- // catch up the local SMT for the unregistered user
618
- await smtUnregistered.add(utxo100.hash, utxo100.hash);
619
- await smtUnregistered.add(utxo0.hash, utxo0.hash);
620
- await smtUnregistered.add(utxo1.hash, utxo1.hash);
621
- await smtUnregistered.add(utxo2.hash, utxo2.hash);
622
- await smtUnregistered.add(_utxo3.hash, _utxo3.hash);
623
- await smtUnregistered.add(utxo4.hash, utxo4.hash);
624
- await smtUnregistered.add(utxo6.hash, utxo6.hash);
625
- await smtUnregistered.add(utxo7.hash, utxo7.hash);
626
- await smtUnregistered.add(
627
- withdrawChangeUTXO.hash,
628
- withdrawChangeUTXO.hash,
629
- );
630
- await smtUnregistered.add(
631
- unregisteredUtxo100.hash,
632
- unregisteredUtxo100.hash,
633
- );
634
- await smtUnregistered.add(unregisteredUtxo0.hash, unregisteredUtxo0.hash);
635
- const utxosRoot = await smtUnregistered.root();
636
-
637
- const nullifier = newNullifier(unregisteredUtxo100, unregistered);
638
- const output1 = newUTXO(100, Bob);
639
- const output2 = newUTXO(0, unregistered);
640
- const proof = await smtUnregistered.generateCircomVerifierProof(
641
- unregisteredUtxo100.hash,
642
- utxosRoot,
643
- );
644
- const merkleProofs = [
645
- proof.siblings.map((s) => s.bigInt()),
646
- proof.siblings.map((s) => s.bigInt()),
647
- ];
648
-
649
- // add the unregistered user to the local KYC SMT, but not to the onchain SMT
650
- await smtKyc.add(
651
- kycHash(unregistered.babyJubPublicKey),
652
- kycHash(unregistered.babyJubPublicKey),
653
- );
654
- const identitiesRoot = await smtKyc.root();
655
- const proof3 = await smtKyc.generateCircomVerifierProof(
656
- kycHash(unregistered.babyJubPublicKey),
657
- identitiesRoot,
658
- );
659
- const proof4 = await smtKyc.generateCircomVerifierProof(
660
- kycHash(Bob.babyJubPublicKey),
661
- identitiesRoot,
662
- );
663
- const identitiesMerkleProofs = [
664
- proof3.siblings.map((s) => s.bigInt()), // identity proof for the sender (unregistered)
665
- proof4.siblings.map((s) => s.bigInt()), // identity proof for the 1st owner of the output UTXO (Bob)
666
- proof3.siblings.map((s) => s.bigInt()), // identity proof for the 2nd owner of the output UTXO (unregistered)
667
- ];
668
652
  await expect(
669
- doTransfer(
670
- unregistered,
671
- [unregisteredUtxo100, ZERO_UTXO],
672
- [nullifier, ZERO_UTXO],
673
- [output1, output2],
674
- utxosRoot.bigInt(),
675
- merkleProofs,
676
- identitiesRoot.bigInt(),
677
- identitiesMerkleProofs,
678
- [Bob, unregistered],
679
- ),
680
- ).rejectedWith("Invalid proof");
681
- });
682
-
683
- it("the unregistered user can still withdraw their UTXOs to ERC20 tokens", async function () {
684
- const startingBalance = await erc20.balanceOf(unregistered.ethAddress);
685
- // unregistered user generates the nullifiers for the UTXOs to be spent
686
- const nullifier1 = newNullifier(unregisteredUtxo100, unregistered);
687
-
688
- // unregistered user generates inclusion proofs for the UTXOs to be spent
689
- let root = await smtUnregistered.root();
690
- const proof1 = await smtUnregistered.generateCircomVerifierProof(
691
- unregisteredUtxo100.hash,
692
- root,
693
- );
694
- const proof2 = await smtUnregistered.generateCircomVerifierProof(
695
- 0n,
696
- root,
697
- );
698
- const merkleProofs = [
699
- proof1.siblings.map((s) => s.bigInt()),
700
- proof2.siblings.map((s) => s.bigInt()),
701
- ];
702
-
703
- // unregistered user proposes the output ERC20 tokens
704
- const unregisteredWithdrawChangeUTXO = newUTXO(0, unregistered);
705
-
706
- const { nullifiers, outputCommitments, encodedProof } =
707
- await prepareNullifierWithdrawProof(
708
- unregistered,
709
- [unregisteredUtxo100, ZERO_UTXO],
710
- [nullifier1, ZERO_UTXO],
711
- unregisteredWithdrawChangeUTXO,
712
- root.bigInt(),
713
- merkleProofs,
714
- );
715
-
716
- // unregistered user withdraws her UTXOs to ERC20 tokens
717
- const tx = await zeto
718
- .connect(unregistered.signer)
719
- .withdraw(
720
- 100,
721
- nullifiers,
722
- outputCommitments[0],
723
- root.bigInt(),
724
- encodedProof,
725
- "0x",
726
- );
727
- await tx.wait();
728
-
729
- // Alice tracks the UTXO inside the SMT
730
- await smtAlice.add(
731
- unregisteredWithdrawChangeUTXO.hash,
732
- unregisteredWithdrawChangeUTXO.hash,
733
- );
734
- // Bob also locally tracks the UTXOs inside the SMT
735
- await smtBob.add(
736
- unregisteredWithdrawChangeUTXO.hash,
737
- unregisteredWithdrawChangeUTXO.hash,
738
- );
739
-
740
- // unregistered user checks her ERC20 balance
741
- const endingBalance = await erc20.balanceOf(unregistered.ethAddress);
742
- expect(endingBalance - startingBalance).to.be.equal(100);
653
+ zeto
654
+ .connect(unregistered.signer)
655
+ .deposit(
656
+ 100,
657
+ outputCommitments,
658
+ encodeToBytesForDeposit(encodedProof),
659
+ "0x",
660
+ ),
661
+ ).to.be.rejectedWith("VM Exception while processing transaction: reverted with custom error 'InvalidProof()'");
743
662
  });
744
663
  });
745
664
 
@@ -788,8 +707,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
788
707
  10,
789
708
  nullifiers,
790
709
  outputCommitments[0],
791
- root.bigInt(),
792
- encodedProof,
710
+ encodeToBytesForWithdraw(root.bigInt(), encodedProof),
793
711
  "0x",
794
712
  ),
795
713
  ).rejectedWith("UTXOAlreadySpent");
@@ -1181,11 +1099,13 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
1181
1099
  const tx = await zeto.connect(signer.signer).transfer(
1182
1100
  nullifiers.filter((ic) => ic !== 0n), // trim off empty utxo hashes to check padding logic for batching works
1183
1101
  outputCommitments.filter((oc) => oc !== 0n), // trim off empty utxo hashes to check padding logic for batching works
1184
- root,
1185
- encryptionNonce,
1186
- ecdhPublicKey,
1187
- encryptedValues,
1188
- encodedProof,
1102
+ encodeToBytes(
1103
+ root,
1104
+ encryptionNonce,
1105
+ ecdhPublicKey,
1106
+ encryptedValues,
1107
+ encodedProof,
1108
+ ),
1189
1109
  "0x",
1190
1110
  );
1191
1111
  const results: ContractTransactionReceipt | null = await tx.wait();
@@ -1195,3 +1115,22 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
1195
1115
  return results;
1196
1116
  }
1197
1117
  });
1118
+
1119
+ function encodeToBytes(
1120
+ root: any,
1121
+ encryptionNonce: any,
1122
+ ecdhPublicKey: any,
1123
+ encryptedValues: any,
1124
+ proof: any,
1125
+ ) {
1126
+ return new AbiCoder().encode(
1127
+ [
1128
+ "uint256 root",
1129
+ "uint256 encryptionNonce",
1130
+ "uint256[2] ecdhPublicKey",
1131
+ "uint256[] encryptedValues",
1132
+ "tuple(uint256[2] pA, uint256[2][2] pB, uint256[2] pC)",
1133
+ ],
1134
+ [root, encryptionNonce, ecdhPublicKey, encryptedValues, proof],
1135
+ );
1136
+ }
@@ -15,7 +15,12 @@
15
15
  // limitations under the License.
16
16
 
17
17
  import { ethers, network } from "hardhat";
18
- import { ContractTransactionReceipt, Signer, BigNumberish } from "ethers";
18
+ import {
19
+ ContractTransactionReceipt,
20
+ Signer,
21
+ BigNumberish,
22
+ AbiCoder,
23
+ } from "ethers";
19
24
  import { expect } from "chai";
20
25
  import {
21
26
  loadCircuit,
@@ -46,6 +51,8 @@ import {
46
51
  loadProvingKeys,
47
52
  prepareDepositProof,
48
53
  prepareNullifierWithdrawProof,
54
+ encodeToBytesForDeposit,
55
+ encodeToBytesForWithdraw,
49
56
  } from "./utils";
50
57
  import { deployZeto } from "./lib/deploy";
51
58
  const poseidonHash = Poseidon.poseidon4;
@@ -177,12 +184,13 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
177
184
 
178
185
  const signerAddress = await Alice.signer.getAddress();
179
186
  const events = parseUTXOEvents(zeto, result.txResult!);
180
- expect(events[0].submitter).to.equal(signerAddress);
181
- expect(events[0].inputs).to.deep.equal(nullifiers.map((n) => n.hash));
187
+ const event = events[0];
188
+ expect(event.submitter).to.equal(signerAddress);
189
+ expect(event.inputs).to.deep.equal(nullifiers.map((n) => n.hash));
182
190
 
183
- const incomingUTXOs: any = events[0].outputs;
191
+ const incomingUTXOs: any = event.outputs;
184
192
 
185
- const ecdhPublicKey = events[0].ecdhPublicKey;
193
+ const ecdhPublicKey = event.ecdhPublicKey;
186
194
 
187
195
  // check the non-empty output hashes are correct
188
196
  for (let i = 0; i < outputUtxos.length; i++) {
@@ -192,9 +200,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
192
200
  ecdhPublicKey,
193
201
  );
194
202
  const plainText = poseidonDecrypt(
195
- events[0].encryptedValuesForReceiver.slice(4 * i, 4 * i + 4),
203
+ event.encryptedValuesForReceiver.slice(4 * i, 4 * i + 4),
196
204
  sharedKey,
197
- events[0].encryptionNonce,
205
+ event.encryptionNonce,
198
206
  2,
199
207
  );
200
208
  expect(plainText).to.deep.equal(
@@ -210,20 +218,15 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
210
218
  await smtAlice.add(incomingUTXOs[i], incomingUTXOs[i]);
211
219
  await smtBob.add(incomingUTXOs[i], incomingUTXOs[i]);
212
220
  }
213
-
214
- // check empty values, salt and hashes are empty
215
- for (let i = outputUtxos.length; i < 10; i++) {
216
- expect(incomingUTXOs[i]).to.equal(0);
217
- }
218
221
  // The regulator uses the encrypted values in the event to decrypt and recover the UTXO value and salt
219
222
  const auditKey = genEcdhSharedKey(
220
223
  Authority.babyJubPrivateKey,
221
224
  ecdhPublicKey,
222
225
  );
223
226
  const auditPlainText = poseidonDecrypt(
224
- events[0].encryptedValuesForAuthority,
227
+ event.encryptedValuesForAuthority,
225
228
  auditKey,
226
- events[0].encryptionNonce,
229
+ event.encryptionNonce,
227
230
  62,
228
231
  );
229
232
 
@@ -252,12 +255,6 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
252
255
  expect(calHash).to.equal(outputUtxos[i].hash);
253
256
  }
254
257
 
255
- // check empty hashes are empty
256
- for (let i = outputUtxos.length; i < 10; i++) {
257
- expect(auditPlainText[2 * i + 42]).to.equal(0);
258
- expect(auditPlainText[2 * i + 43]).to.equal(0);
259
- }
260
-
261
258
  // mint sufficient balance in Zeto contract address for Alice to withdraw
262
259
  const mintTx = await erc20.connect(deployer).mint(zeto, 3);
263
260
  await mintTx.wait();
@@ -310,8 +307,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
310
307
  3,
311
308
  _withdrawNullifiers,
312
309
  withdrawCommitments[0],
313
- root.bigInt(),
314
- withdrawEncodedProof,
310
+ encodeToBytesForWithdraw(root.bigInt(), withdrawEncodedProof),
315
311
  "0x",
316
312
  );
317
313
  await tx.wait();
@@ -339,7 +335,12 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
339
335
  );
340
336
  const tx2 = await zeto
341
337
  .connect(Alice.signer)
342
- .deposit(100, outputCommitments, encodedProof, "0x");
338
+ .deposit(
339
+ 100,
340
+ outputCommitments,
341
+ encodeToBytesForDeposit(encodedProof),
342
+ "0x",
343
+ );
343
344
  await tx2.wait();
344
345
 
345
346
  await smtAlice.add(utxo100.hash, utxo100.hash);
@@ -414,20 +415,21 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
414
415
  // Bob parses the UTXOs from the onchain event
415
416
  const signerAddress = await Alice.signer.getAddress();
416
417
  const events = parseUTXOEvents(zeto, result2.txResult!);
417
- expect(events[0].submitter).to.equal(signerAddress);
418
- expect(events[0].inputs).to.deep.equal([nullifier1.hash, nullifier2.hash]);
419
- expect(events[0].outputs).to.deep.equal([_utxo3.hash, utxo4.hash]);
420
- await smtBob.add(events[0].outputs[0], events[0].outputs[0]);
421
- await smtBob.add(events[0].outputs[1], events[0].outputs[1]);
422
-
423
- const ecdhPublicKey = events[0].ecdhPublicKey;
418
+ const event = events[0];
419
+ expect(event.submitter).to.equal(signerAddress);
420
+ expect(event.inputs).to.deep.equal([nullifier1.hash, nullifier2.hash]);
421
+ expect(event.outputs).to.deep.equal([_utxo3.hash, utxo4.hash]);
422
+ await smtBob.add(event.outputs[0], event.outputs[0]);
423
+ await smtBob.add(event.outputs[1], event.outputs[1]);
424
+
425
+ const ecdhPublicKey = event.ecdhPublicKey;
424
426
  // Bob reconstructs the shared key using his private key and ephemeral public key
425
427
 
426
428
  const sharedKey = genEcdhSharedKey(Bob.babyJubPrivateKey, ecdhPublicKey);
427
429
  const plainText1 = poseidonDecrypt(
428
- events[0].encryptedValuesForReceiver.slice(0, 4),
430
+ event.encryptedValuesForReceiver.slice(0, 4),
429
431
  sharedKey,
430
- events[0].encryptionNonce,
432
+ event.encryptionNonce,
431
433
  2,
432
434
  );
433
435
  expect(plainText1).to.deep.equal(result2.expectedPlainText.slice(0, 2));
@@ -441,9 +443,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
441
443
  ecdhPublicKey,
442
444
  );
443
445
  const plainText2 = poseidonDecrypt(
444
- events[0].encryptedValuesForAuthority,
446
+ event.encryptedValuesForAuthority,
445
447
  sharedKey2,
446
- events[0].encryptionNonce,
448
+ event.encryptionNonce,
447
449
  14,
448
450
  );
449
451
  expect(plainText2).to.deep.equal([
@@ -527,8 +529,9 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
527
529
 
528
530
  // Alice gets the new UTXOs from the onchain event and keeps the local SMT in sync
529
531
  const events = parseUTXOEvents(zeto, result.txResult!);
530
- await smtAlice.add(events[0].outputs[0], events[0].outputs[0]);
531
- await smtAlice.add(events[0].outputs[1], events[0].outputs[1]);
532
+ const event = events[0];
533
+ await smtAlice.add(event.outputs[0], event.outputs[0]);
534
+ await smtAlice.add(event.outputs[1], event.outputs[1]);
532
535
  }).timeout(600000);
533
536
 
534
537
  it("Alice withdraws her UTXOs to ERC20 tokens should succeed", async function () {
@@ -568,8 +571,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
568
571
  80,
569
572
  nullifiers,
570
573
  outputCommitments[0],
571
- root.bigInt(),
572
- encodedProof,
574
+ encodeToBytesForWithdraw(root.bigInt(), encodedProof),
573
575
  "0x",
574
576
  );
575
577
  await tx.wait();
@@ -629,8 +631,7 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
629
631
  80,
630
632
  nullifiers,
631
633
  outputCommitments[0],
632
- root.bigInt(),
633
- encodedProof,
634
+ encodeToBytesForWithdraw(root.bigInt(), encodedProof),
634
635
  "0x",
635
636
  ),
636
637
  ).rejectedWith("UTXOAlreadySpent");
@@ -944,12 +945,14 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
944
945
  const tx = await zeto.connect(signer.signer).transfer(
945
946
  nullifiers.filter((ic) => ic !== 0n), // trim off empty utxo hashes to check padding logic for batching works
946
947
  outputCommitments.filter((oc) => oc !== 0n), // trim off empty utxo hashes to check padding logic for batching works
947
- root,
948
- encryptionNonce,
949
- ecdhPublicKey,
950
- encryptedValuesForReceiver,
951
- encryptedValuesForRegulator,
952
- encodedProof,
948
+ encodeToBytes(
949
+ root,
950
+ encryptionNonce,
951
+ ecdhPublicKey,
952
+ encryptedValuesForReceiver,
953
+ encryptedValuesForRegulator,
954
+ encodedProof,
955
+ ),
953
956
  "0x",
954
957
  );
955
958
  const results: ContractTransactionReceipt | null = await tx.wait();
@@ -959,3 +962,31 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti
959
962
  return results;
960
963
  }
961
964
  });
965
+
966
+ function encodeToBytes(
967
+ root: any,
968
+ encryptionNonce: any,
969
+ ecdhPublicKey: any,
970
+ encryptedValuesForReceiver: any,
971
+ encryptedValuesForAuthority: any,
972
+ proof: any,
973
+ ) {
974
+ return new AbiCoder().encode(
975
+ [
976
+ "uint256 root",
977
+ "uint256 encryptionNonce",
978
+ "uint256[2] ecdhPublicKey",
979
+ "uint256[] encryptedValuesForReceiver",
980
+ "uint256[] encryptedValuesForAuthority",
981
+ "tuple(uint256[2] pA, uint256[2][2] pB, uint256[2] pC)",
982
+ ],
983
+ [
984
+ root,
985
+ encryptionNonce,
986
+ ecdhPublicKey,
987
+ encryptedValuesForReceiver,
988
+ encryptedValuesForAuthority,
989
+ proof,
990
+ ],
991
+ );
992
+ }