@dorafactory/maci-sdk 0.1.3-pre.46.beta.1 → 0.1.3-pre.46.beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1247,7 +1247,9 @@ var rerandomize = (pubKey, ciphertext, randomVal = genRandomSalt()) => {
1247
1247
  var genAddKeyInput = (depth, {
1248
1248
  coordPubKey,
1249
1249
  oldKey,
1250
- deactivates
1250
+ deactivates,
1251
+ newPubKey,
1252
+ pollId
1251
1253
  }) => {
1252
1254
  const sharedKeyHash = poseidon(genEcdhSharedKey(oldKey.privKey, coordPubKey));
1253
1255
  const randomVal = genRandomSalt();
@@ -1259,7 +1261,7 @@ var genAddKeyInput = (depth, {
1259
1261
  const c1 = [deactivateLeaf[0], deactivateLeaf[1]];
1260
1262
  const c2 = [deactivateLeaf[2], deactivateLeaf[3]];
1261
1263
  const { d1, d2 } = rerandomize(coordPubKey, { c1, c2 }, randomVal);
1262
- const nullifier = poseidon([BigInt(oldKey.formatedPrivKey), 1444992409218394441042n]);
1264
+ const nullifier = poseidon([BigInt(oldKey.formatedPrivKey), pollId]);
1263
1265
  const tree = new Tree(5, depth, 0n);
1264
1266
  const leaves = deactivates.map((d) => poseidon(d));
1265
1267
  tree.initLeaves(leaves);
@@ -1272,7 +1274,9 @@ var genAddKeyInput = (depth, {
1272
1274
  d1[0],
1273
1275
  d1[1],
1274
1276
  d2[0],
1275
- d2[1]
1277
+ d2[1],
1278
+ poseidon(newPubKey),
1279
+ pollId
1276
1280
  ]);
1277
1281
  const input = {
1278
1282
  inputHash,
@@ -1287,7 +1291,9 @@ var genAddKeyInput = (depth, {
1287
1291
  d2,
1288
1292
  deactivateLeafPathElements,
1289
1293
  nullifier,
1290
- oldPrivateKey: oldKey.formatedPrivKey
1294
+ oldPrivateKey: oldKey.formatedPrivKey,
1295
+ newPubKey,
1296
+ pollId
1291
1297
  };
1292
1298
  return input;
1293
1299
  };
@@ -4342,6 +4348,46 @@ var ApiSaasClient = class extends ApiSaasQueryClient {
4342
4348
  _funds
4343
4349
  );
4344
4350
  };
4351
+ this.publishMessage = async ({
4352
+ contractAddr,
4353
+ encPubKeys,
4354
+ messages
4355
+ }, fee = "auto", memo, _funds) => {
4356
+ return await this.client.execute(
4357
+ this.sender,
4358
+ this.contractAddress,
4359
+ {
4360
+ publish_message: {
4361
+ contract_addr: contractAddr,
4362
+ enc_pub_keys: encPubKeys,
4363
+ messages
4364
+ }
4365
+ },
4366
+ fee,
4367
+ memo,
4368
+ _funds
4369
+ );
4370
+ };
4371
+ this.publishDeactivateMessage = async ({
4372
+ contractAddr,
4373
+ encPubKey,
4374
+ message
4375
+ }, fee = "auto", memo, _funds) => {
4376
+ return await this.client.execute(
4377
+ this.sender,
4378
+ this.contractAddress,
4379
+ {
4380
+ publish_deactivate_message: {
4381
+ contract_addr: contractAddr,
4382
+ enc_pub_key: encPubKey,
4383
+ message
4384
+ }
4385
+ },
4386
+ fee,
4387
+ memo,
4388
+ _funds
4389
+ );
4390
+ };
4345
4391
  this.client = client;
4346
4392
  this.sender = sender;
4347
4393
  this.contractAddress = contractAddress;
@@ -4354,6 +4400,8 @@ var ApiSaasClient = class extends ApiSaasQueryClient {
4354
4400
  this.createAmaciRound = this.createAmaciRound.bind(this);
4355
4401
  this.setRoundInfo = this.setRoundInfo.bind(this);
4356
4402
  this.setVoteOptionsMap = this.setVoteOptionsMap.bind(this);
4403
+ this.publishMessage = this.publishMessage.bind(this);
4404
+ this.publishDeactivateMessage = this.publishDeactivateMessage.bind(this);
4357
4405
  }
4358
4406
  };
4359
4407
 
@@ -4399,7 +4447,6 @@ async function createApiSaasClientBy({
4399
4447
  return new ApiSaasClient(signingCosmWasmClient, address, contractAddress);
4400
4448
  }
4401
4449
  async function createContractClientByWallet(rpcEndpoint, wallet) {
4402
- console.log("rpcEndpoint", rpcEndpoint);
4403
4450
  const client = await import_cosmwasm_stargate.SigningCosmWasmClient.connectWithSigner(rpcEndpoint, wallet, {
4404
4451
  ...defaultSigningClientOptions
4405
4452
  });
@@ -4431,11 +4478,11 @@ function getAMaciRoundCircuitFee(network, maxVoter, maxOption) {
4431
4478
  amount: "0"
4432
4479
  };
4433
4480
  if (maxVoter <= 25 && maxOption <= 5) {
4434
- requiredFee.amount = "20000000000000000000";
4481
+ requiredFee.amount = "5000000000000000000";
4435
4482
  } else if (maxVoter <= 625 && maxOption <= 25) {
4436
- requiredFee.amount = "540000000000000000000";
4483
+ requiredFee.amount = "27000000000000000000";
4437
4484
  } else if (maxVoter <= 15625 && maxOption <= 125) {
4438
- requiredFee.amount = "1080000000000000000000";
4485
+ requiredFee.amount = "208000000000000000000";
4439
4486
  } else {
4440
4487
  throw new Error("Number of voters or options is too large.");
4441
4488
  }
@@ -4515,6 +4562,7 @@ var Contract = class {
4515
4562
  [requiredFee]
4516
4563
  );
4517
4564
  let contractAddress = "";
4565
+ let pollId = "";
4518
4566
  for (const event of res.events) {
4519
4567
  if (event.type === "wasm") {
4520
4568
  const actionEvent = event.attributes.find(
@@ -4524,8 +4572,16 @@ var Contract = class {
4524
4572
  const roundAddrEvent = event.attributes.find(
4525
4573
  (attr) => attr.key === "round_addr"
4526
4574
  );
4575
+ const pollIdEvent = event.attributes.find(
4576
+ (attr) => attr.key === "poll_id"
4577
+ );
4527
4578
  if (roundAddrEvent) {
4528
4579
  contractAddress = roundAddrEvent.value.toString();
4580
+ }
4581
+ if (pollIdEvent) {
4582
+ pollId = pollIdEvent.value.toString();
4583
+ }
4584
+ if (contractAddress) {
4529
4585
  break;
4530
4586
  }
4531
4587
  }
@@ -4533,7 +4589,8 @@ var Contract = class {
4533
4589
  }
4534
4590
  return {
4535
4591
  ...res,
4536
- contractAddress
4592
+ contractAddress,
4593
+ pollId
4537
4594
  };
4538
4595
  }
4539
4596
  async queryRoundInfo({ signer, roundAddress }) {
@@ -4944,6 +5001,7 @@ var Contract = class {
4944
5001
  createResponse = await client.createAmaciRound(roundParams, fee);
4945
5002
  }
4946
5003
  let contractAddress = "";
5004
+ let pollId = "";
4947
5005
  for (const event of createResponse.events) {
4948
5006
  if (event.type === "wasm") {
4949
5007
  const actionEvent = event.attributes.find(
@@ -4953,8 +5011,16 @@ var Contract = class {
4953
5011
  const roundAddrEvent = event.attributes.find(
4954
5012
  (attr) => attr.key === "round_addr"
4955
5013
  );
5014
+ const pollIdEvent = event.attributes.find(
5015
+ (attr) => attr.key === "poll_id"
5016
+ );
4956
5017
  if (roundAddrEvent) {
4957
5018
  contractAddress = roundAddrEvent.value.toString();
5019
+ }
5020
+ if (pollIdEvent) {
5021
+ pollId = pollIdEvent.value.toString();
5022
+ }
5023
+ if (contractAddress) {
4958
5024
  break;
4959
5025
  }
4960
5026
  }
@@ -4962,9 +5028,126 @@ var Contract = class {
4962
5028
  }
4963
5029
  return {
4964
5030
  ...createResponse,
4965
- contractAddress
5031
+ contractAddress,
5032
+ pollId
4966
5033
  };
4967
5034
  }
5035
+ async publishMessageViaSaas({
5036
+ signer,
5037
+ contractAddress,
5038
+ encPubKeys,
5039
+ messages,
5040
+ granter,
5041
+ fee = 1.8
5042
+ }) {
5043
+ const client = await createApiSaasClientBy({
5044
+ rpcEndpoint: this.rpcEndpoint,
5045
+ wallet: signer,
5046
+ contractAddress: this.apiSaasAddress
5047
+ });
5048
+ const saasGranter = granter || this.apiSaasAddress;
5049
+ if (typeof fee !== "object") {
5050
+ const [{ address }] = await signer.getAccounts();
5051
+ const contractClient = await this.contractClient({ signer });
5052
+ const msg = {
5053
+ publish_message: {
5054
+ contract_addr: contractAddress,
5055
+ enc_pub_keys: encPubKeys,
5056
+ messages
5057
+ }
5058
+ };
5059
+ const gasEstimation = await contractClient.simulate(
5060
+ address,
5061
+ [
5062
+ {
5063
+ typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
5064
+ value: {
5065
+ sender: address,
5066
+ contract: this.apiSaasAddress,
5067
+ msg: new TextEncoder().encode(JSON.stringify(msg))
5068
+ }
5069
+ }
5070
+ ],
5071
+ ""
5072
+ );
5073
+ const multiplier = typeof fee === "number" ? fee : 1.8;
5074
+ const gasPrice = import_stargate2.GasPrice.fromString("10000000000peaka");
5075
+ const calculatedFee = (0, import_stargate2.calculateFee)(Math.round(gasEstimation * multiplier), gasPrice);
5076
+ const grantFee = {
5077
+ amount: calculatedFee.amount,
5078
+ gas: calculatedFee.gas,
5079
+ granter: saasGranter
5080
+ };
5081
+ return client.publishMessage({ contractAddr: contractAddress, encPubKeys, messages }, grantFee);
5082
+ } else {
5083
+ const grantFee = {
5084
+ ...fee,
5085
+ granter: saasGranter
5086
+ };
5087
+ return client.publishMessage({ contractAddr: contractAddress, encPubKeys, messages }, grantFee);
5088
+ }
5089
+ }
5090
+ async publishDeactivateMessageViaSaas({
5091
+ signer,
5092
+ contractAddress,
5093
+ encPubKey,
5094
+ message,
5095
+ granter,
5096
+ fee = 1.8
5097
+ }) {
5098
+ const client = await createApiSaasClientBy({
5099
+ rpcEndpoint: this.rpcEndpoint,
5100
+ wallet: signer,
5101
+ contractAddress: this.apiSaasAddress
5102
+ });
5103
+ const saasGranter = granter || this.apiSaasAddress;
5104
+ if (typeof fee !== "object") {
5105
+ const [{ address }] = await signer.getAccounts();
5106
+ const contractClient = await this.contractClient({ signer });
5107
+ const msg = {
5108
+ publish_deactivate_message: {
5109
+ contract_addr: contractAddress,
5110
+ enc_pub_key: encPubKey,
5111
+ message
5112
+ }
5113
+ };
5114
+ const gasEstimation = await contractClient.simulate(
5115
+ address,
5116
+ [
5117
+ {
5118
+ typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
5119
+ value: {
5120
+ sender: address,
5121
+ contract: this.apiSaasAddress,
5122
+ msg: new TextEncoder().encode(JSON.stringify(msg))
5123
+ }
5124
+ }
5125
+ ],
5126
+ ""
5127
+ );
5128
+ const multiplier = typeof fee === "number" ? fee : 1.8;
5129
+ const gasPrice = import_stargate2.GasPrice.fromString("10000000000peaka");
5130
+ const calculatedFee = (0, import_stargate2.calculateFee)(Math.round(gasEstimation * multiplier), gasPrice);
5131
+ const grantFee = {
5132
+ amount: calculatedFee.amount,
5133
+ gas: calculatedFee.gas,
5134
+ granter: saasGranter
5135
+ };
5136
+ return client.publishDeactivateMessage(
5137
+ { contractAddr: contractAddress, encPubKey, message },
5138
+ grantFee
5139
+ );
5140
+ } else {
5141
+ const grantFee = {
5142
+ ...fee,
5143
+ granter: saasGranter
5144
+ };
5145
+ return client.publishDeactivateMessage(
5146
+ { contractAddr: contractAddress, encPubKey, message },
5147
+ grantFee
5148
+ );
5149
+ }
5150
+ }
4968
5151
  };
4969
5152
 
4970
5153
  // src/libs/oracle-certificate/oracle-certificate.ts
@@ -5023,7 +5206,7 @@ var import_tx = require("cosmjs-types/cosmwasm/wasm/v1/tx.js");
5023
5206
  // src/libs/maci/config.ts
5024
5207
  var FEE_DENOM = "peaka";
5025
5208
  var DEACTIVATE_FEE = "10000000000000000000";
5026
- var MESSAGE_FEE = "10000000000000000000";
5209
+ var MESSAGE_FEE = "60000000000000000";
5027
5210
 
5028
5211
  // src/libs/maci/maci.ts
5029
5212
  function isErrorResponse(response) {
@@ -5486,6 +5669,18 @@ var MACI = class {
5486
5669
  if (!address) {
5487
5670
  address = (await signer.getAccounts())[0].address;
5488
5671
  }
5672
+ const msgLength = payload[0]?.msg.length ?? 0;
5673
+ if (msgLength === 7) {
5674
+ return await this.publishMessageBatchLegacy({
5675
+ signer,
5676
+ address,
5677
+ payload,
5678
+ contractAddress,
5679
+ gasStation,
5680
+ granter,
5681
+ fee
5682
+ });
5683
+ }
5489
5684
  return await this.publishMessageBatch({
5490
5685
  signer,
5491
5686
  address,
@@ -5571,34 +5766,29 @@ var MACI = class {
5571
5766
  }));
5572
5767
  const totalFee = (BigInt(MESSAGE_FEE) * BigInt(payload.length)).toString();
5573
5768
  const batchFunds = [{ denom: FEE_DENOM, amount: totalFee }];
5574
- if (gasStation && typeof fee !== "object") {
5769
+ if (gasStation && granter === this.contract.apiSaasAddress) {
5770
+ return this.contract.publishMessageViaSaas({
5771
+ signer,
5772
+ contractAddress,
5773
+ encPubKeys,
5774
+ messages,
5775
+ granter,
5776
+ fee
5777
+ });
5778
+ } else if (gasStation && typeof fee !== "object") {
5575
5779
  const client = await this.contract.contractClient({ signer });
5576
- const encPubKeysBigInt = payload.map((p) => ({
5577
- x: p.encPubkeys[0],
5578
- y: p.encPubkeys[1]
5579
- }));
5580
- const messagesBigInt = payload.map((p) => ({
5581
- data: p.msg
5582
- }));
5583
- const msg = {
5780
+ const msgForSimulate = {
5584
5781
  typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
5585
5782
  value: import_tx.MsgExecuteContract.fromPartial({
5586
5783
  sender: address,
5587
5784
  contract: contractAddress,
5588
5785
  msg: new TextEncoder().encode(
5589
- JSON.stringify(
5590
- stringizing({
5591
- publish_message: {
5592
- enc_pub_keys: encPubKeysBigInt,
5593
- messages: messagesBigInt
5594
- }
5595
- })
5596
- )
5786
+ JSON.stringify({ publish_message: { enc_pub_keys: encPubKeys, messages } })
5597
5787
  ),
5598
5788
  funds: batchFunds
5599
5789
  })
5600
5790
  };
5601
- const gasEstimation = await client.simulate(address, [msg], "");
5791
+ const gasEstimation = await client.simulate(address, [msgForSimulate], "");
5602
5792
  const multiplier = typeof fee === "number" ? fee : 1.8;
5603
5793
  const gasPrice = import_stargate3.GasPrice.fromString("10000000000peaka");
5604
5794
  const calculatedFee = (0, import_stargate3.calculateFee)(Math.round(gasEstimation * multiplier), gasPrice);
@@ -5617,6 +5807,63 @@ var MACI = class {
5617
5807
  }
5618
5808
  return amaciClient.publishMessage({ encPubKeys, messages }, fee, void 0, batchFunds);
5619
5809
  }
5810
+ async publishMessageBatchLegacy({
5811
+ signer,
5812
+ address,
5813
+ payload,
5814
+ contractAddress,
5815
+ gasStation,
5816
+ granter,
5817
+ fee = 1.8
5818
+ }) {
5819
+ if (!address) {
5820
+ address = (await signer.getAccounts())[0].address;
5821
+ }
5822
+ const client = await this.contract.contractClient({ signer });
5823
+ const messages = payload.map((p) => ({
5824
+ data: p.msg
5825
+ }));
5826
+ const encPubKeys = payload.map((p) => ({
5827
+ x: p.encPubkeys[0],
5828
+ y: p.encPubkeys[1]
5829
+ }));
5830
+ const msg = {
5831
+ typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
5832
+ value: import_tx.MsgExecuteContract.fromPartial({
5833
+ sender: address,
5834
+ contract: contractAddress,
5835
+ msg: new TextEncoder().encode(
5836
+ JSON.stringify(
5837
+ stringizing({
5838
+ publish_message_batch: {
5839
+ enc_pub_keys: encPubKeys,
5840
+ messages
5841
+ }
5842
+ })
5843
+ )
5844
+ )
5845
+ })
5846
+ };
5847
+ if (gasStation && typeof fee !== "object") {
5848
+ const gasEstimation = await client.simulate(address, [msg], "");
5849
+ const multiplier = typeof fee === "number" ? fee : 1.8;
5850
+ const gasPrice = import_stargate3.GasPrice.fromString("10000000000peaka");
5851
+ const calculatedFee = (0, import_stargate3.calculateFee)(Math.round(gasEstimation * multiplier), gasPrice);
5852
+ const grantFee = {
5853
+ amount: calculatedFee.amount,
5854
+ gas: calculatedFee.gas,
5855
+ granter: granter || contractAddress
5856
+ };
5857
+ return client.signAndBroadcast(address, [msg], grantFee);
5858
+ } else if (gasStation && typeof fee === "object") {
5859
+ const grantFee = {
5860
+ ...fee,
5861
+ granter: granter || contractAddress
5862
+ };
5863
+ return client.signAndBroadcast(address, [msg], grantFee);
5864
+ }
5865
+ return client.signAndBroadcast(address, [msg], fee);
5866
+ }
5620
5867
  async deactivate({
5621
5868
  signer,
5622
5869
  address,
@@ -5712,10 +5959,23 @@ var MACI = class {
5712
5959
  }) {
5713
5960
  try {
5714
5961
  address = address || (await signer.getAccounts())[0].address;
5715
- const client = await this.contract.contractClient({
5716
- signer
5717
- });
5718
5962
  const { msg, encPubkeys } = payload;
5963
+ if (gasStation === true && granter === this.contract.apiSaasAddress) {
5964
+ return this.contract.publishDeactivateMessageViaSaas({
5965
+ signer,
5966
+ contractAddress,
5967
+ encPubKey: {
5968
+ x: encPubkeys[0].toString(),
5969
+ y: encPubkeys[1].toString()
5970
+ },
5971
+ message: {
5972
+ data: msg.map((m) => m.toString())
5973
+ },
5974
+ granter,
5975
+ fee
5976
+ });
5977
+ }
5978
+ const client = await this.contract.contractClient({ signer });
5719
5979
  const deactivateMsg = stringizing({
5720
5980
  publish_deactivate_message: {
5721
5981
  enc_pub_key: {
@@ -5771,6 +6031,7 @@ var MACI = class {
5771
6031
  }
5772
6032
  async genAddKeyInput({
5773
6033
  maciKeypair,
6034
+ newMaciKeypair,
5774
6035
  contractAddress
5775
6036
  }) {
5776
6037
  const deactivates = await this.fetchAllDeactivateLogs({
@@ -5779,12 +6040,15 @@ var MACI = class {
5779
6040
  const roundInfo = await this.getRoundInfo({
5780
6041
  contractAddress
5781
6042
  });
6043
+ const pollId = await this.getPollId({ contractAddress });
5782
6044
  const circuitPower = roundInfo.circuitPower;
5783
6045
  const stateTreeDepth = Number(circuitPower.split("-")[0]);
5784
6046
  const inputObj = genAddKeyInput(stateTreeDepth + 2, {
5785
6047
  coordPubKey: [BigInt(roundInfo.coordinatorPubkeyX), BigInt(roundInfo.coordinatorPubkeyY)],
5786
6048
  oldKey: maciKeypair,
5787
- deactivates: deactivates.map((d) => d.map(BigInt))
6049
+ deactivates: deactivates.map((d) => d.map(BigInt)),
6050
+ newPubKey: newMaciKeypair.pubKey,
6051
+ pollId: BigInt(pollId)
5788
6052
  });
5789
6053
  return inputObj;
5790
6054
  }
@@ -6429,6 +6693,47 @@ var MaciApiClient = class {
6429
6693
  method: "GET"
6430
6694
  });
6431
6695
  }
6696
+ /**
6697
+ * Get coordinator public key, deactivate root, and voter scale for a round.
6698
+ * Lighter alternative to the full data endpoint when only circuit inputs are needed.
6699
+ */
6700
+ async getPreDeactivateMeta(params) {
6701
+ return this.fetch(`/v1/pre-deactivate/${params.contractAddress}/meta`, {
6702
+ method: "GET"
6703
+ });
6704
+ }
6705
+ /**
6706
+ * Get K-anonymous Merkle proof packages for the specified leaf indices.
6707
+ * The caller should mix the real leaf index with decoy indices to preserve privacy.
6708
+ */
6709
+ async getPreDeactivateProof(contractAddress, indices) {
6710
+ return this.fetch(
6711
+ `/v1/pre-deactivate/${contractAddress}/proof?indices=${indices}`,
6712
+ { method: "GET" }
6713
+ );
6714
+ }
6715
+ // ==================== Claim Key APIs ====================
6716
+ /**
6717
+ * Claim MACI Key
6718
+ * Assign the next available pre-generated MACI key pair for an AMACI round (first-come-first-served).
6719
+ * Returns pubkey, secretKey, and the full deactivate Merkle proof.
6720
+ * WARNING: secretKey is returned only once and cannot be retrieved again.
6721
+ */
6722
+ async claimMaciKey(params) {
6723
+ return this.fetch(`/v1/rounds/${params.contractAddress}/claim-key`, {
6724
+ method: "POST"
6725
+ });
6726
+ }
6727
+ /**
6728
+ * Get Claim Statistics
6729
+ * Returns total key slots (scale), claimed count, and available count for the round.
6730
+ * Public endpoint — no authentication required.
6731
+ */
6732
+ async getClaimStats(params) {
6733
+ return this.fetch(`/v1/rounds/${params.contractAddress}/claim-key-stats`, {
6734
+ method: "GET"
6735
+ });
6736
+ }
6432
6737
  };
6433
6738
 
6434
6739
  // src/maci.ts
@@ -6789,10 +7094,12 @@ var MaciClient = class {
6789
7094
  }
6790
7095
  async genAddKeyInput({
6791
7096
  contractAddress,
6792
- maciKeypair
7097
+ maciKeypair,
7098
+ newMaciKeypair
6793
7099
  }) {
6794
7100
  return await this.maci.genAddKeyInput({
6795
7101
  maciKeypair: maciKeypair || this.maciKeypair,
7102
+ newMaciKeypair,
6796
7103
  contractAddress
6797
7104
  });
6798
7105
  }
@@ -7005,6 +7312,19 @@ var MaciClient = class {
7005
7312
  }
7006
7313
  return await this.saasApiClient.setVoteOptions(params);
7007
7314
  }
7315
+ /**
7316
+ * Claim the next available pre-generated MACI key pair for an AMACI round via SaaS API.
7317
+ * The key is assigned on a first-come-first-served basis.
7318
+ * WARNING: secretKey is returned only once — save it immediately, it cannot be retrieved again.
7319
+ * @param contractAddress - Round contract address
7320
+ * @returns Claimed key pair with full deactivate Merkle proof
7321
+ */
7322
+ async saasClaimKey(contractAddress) {
7323
+ if (!this.saasApiClient) {
7324
+ throw new Error("SaaS API client not initialized");
7325
+ }
7326
+ return await this.saasApiClient.claimMaciKey({ contractAddress });
7327
+ }
7008
7328
  };
7009
7329
 
7010
7330
  // src/voter.ts
@@ -7497,6 +7817,15 @@ var MaciAccount = class {
7497
7817
 
7498
7818
  // src/voter.ts
7499
7819
  var import_poseidon_cipher3 = require("@zk-kit/poseidon-cipher");
7820
+ function buildKAnonymousIndices(deactivateIdx, voterScale) {
7821
+ const kMax = Math.min(200, Math.floor(voterScale * 0.1) || 1);
7822
+ const pool = Array.from({ length: voterScale }, (_, i) => i).filter((i) => i !== deactivateIdx);
7823
+ for (let i = pool.length - 1; i > 0; i--) {
7824
+ const j = Math.floor(Math.random() * (i + 1));
7825
+ [pool[i], pool[j]] = [pool[j], pool[i]];
7826
+ }
7827
+ return [deactivateIdx, ...pool.slice(0, kMax - 1)].sort((a, b) => a - b).join(",");
7828
+ }
7500
7829
  var VoterClient = class _VoterClient {
7501
7830
  /**
7502
7831
  * @constructor
@@ -7627,7 +7956,7 @@ var VoterClient = class _VoterClient {
7627
7956
  derivePathParams
7628
7957
  }) {
7629
7958
  const plan = this.normalizeVoteOptions(selectedOptions);
7630
- const payload = this.batchGenMessage(stateIdx, operatorPubkey, pollId, plan, derivePathParams);
7959
+ const payload = pollId !== void 0 ? this.batchGenMessage(stateIdx, operatorPubkey, pollId, plan, derivePathParams) : this.legacyBatchGenMessage(stateIdx, operatorPubkey, plan, derivePathParams);
7631
7960
  return stringizing(payload);
7632
7961
  }
7633
7962
  /**
@@ -7728,16 +8057,32 @@ var VoterClient = class _VoterClient {
7728
8057
  stateTreeDepth,
7729
8058
  operatorPubkey,
7730
8059
  deactivates,
8060
+ newPubkey,
8061
+ pollId,
7731
8062
  wasmFile,
7732
8063
  zkeyFile,
7733
8064
  derivePathParams
7734
8065
  }) {
7735
8066
  const [coordPubkeyX, coordPubkeyY] = this.unpackMaciPubkey(operatorPubkey);
7736
- const addKeyInput = await this.genAddKeyInput(stateTreeDepth + 2, {
7737
- coordPubKey: [coordPubkeyX, coordPubkeyY],
7738
- deactivates: deactivates.map((d) => d.map(BigInt)),
7739
- derivePathParams
7740
- });
8067
+ let addKeyInput;
8068
+ if (pollId !== void 0) {
8069
+ if (!newPubkey) {
8070
+ throw new Error("buildAddNewKeyPayload: `newPubkey` is required when `pollId` is provided");
8071
+ }
8072
+ addKeyInput = await this.genAddKeyInput(stateTreeDepth + 2, {
8073
+ coordPubKey: [coordPubkeyX, coordPubkeyY],
8074
+ deactivates: deactivates.map((d) => d.map(BigInt)),
8075
+ newPubKey: newPubkey,
8076
+ pollId,
8077
+ derivePathParams
8078
+ });
8079
+ } else {
8080
+ addKeyInput = await this.legacyGenAddKeyInput(stateTreeDepth + 2, {
8081
+ coordPubKey: [coordPubkeyX, coordPubkeyY],
8082
+ deactivates: deactivates.map((d) => d.map(BigInt)),
8083
+ derivePathParams
8084
+ });
8085
+ }
7741
8086
  if (addKeyInput === null) {
7742
8087
  throw Error("genAddKeyInput failed");
7743
8088
  }
@@ -7758,20 +8103,117 @@ var VoterClient = class _VoterClient {
7758
8103
  stateTreeDepth,
7759
8104
  coordinatorPubkey,
7760
8105
  deactivates,
8106
+ contractAddress,
8107
+ deactivateIdx,
8108
+ voterScale,
8109
+ preComputedProof,
8110
+ newPubkey,
8111
+ pollId,
7761
8112
  wasmFile,
7762
8113
  zkeyFile,
7763
8114
  derivePathParams
7764
8115
  }) {
7765
8116
  const [coordPubkeyX, coordPubkeyY] = this.unpackMaciPubkey(coordinatorPubkey);
8117
+ if (pollId === void 0) {
8118
+ if (!deactivates || deactivates.length === 0) {
8119
+ throw new Error(
8120
+ "buildPreAddNewKeyPayload: `deactivates` is required in legacy mode (pollId omitted)"
8121
+ );
8122
+ }
8123
+ const addKeyInput2 = await this.legacyGenPreAddKeyInput(stateTreeDepth + 2, {
8124
+ coordPubKey: [coordPubkeyX, coordPubkeyY],
8125
+ deactivates: deactivates.map((d) => d.map(BigInt)),
8126
+ derivePathParams
8127
+ });
8128
+ if (addKeyInput2 === null) {
8129
+ throw Error("legacyGenPreAddKeyInput failed, cannot find deactivate idx");
8130
+ }
8131
+ const { proof: proof2 } = await import_snarkjs.groth16.fullProve(addKeyInput2, wasmFile, zkeyFile);
8132
+ const proofHex2 = await adaptToUncompressed(proof2);
8133
+ return {
8134
+ proof: proofHex2,
8135
+ d: [
8136
+ addKeyInput2.d1[0].toString(),
8137
+ addKeyInput2.d1[1].toString(),
8138
+ addKeyInput2.d2[0].toString(),
8139
+ addKeyInput2.d2[1].toString()
8140
+ ],
8141
+ nullifier: addKeyInput2.nullifier.toString()
8142
+ };
8143
+ }
8144
+ if (!newPubkey) {
8145
+ throw new Error("buildPreAddNewKeyPayload: `newPubkey` is required when `pollId` is provided");
8146
+ }
8147
+ const coordPubKey = [coordPubkeyX, coordPubkeyY];
8148
+ let resolvedDeactivates;
8149
+ let preComputedTreeProof;
8150
+ let preComputedLeaf;
8151
+ if (deactivates && deactivates.length > 0) {
8152
+ resolvedDeactivates = deactivates.map((d) => d.map(BigInt));
8153
+ } else if (preComputedProof) {
8154
+ if (deactivateIdx === void 0) {
8155
+ throw new Error(
8156
+ "buildPreAddNewKeyPayload: `deactivateIdx` is required when `preComputedProof` is provided"
8157
+ );
8158
+ }
8159
+ preComputedLeaf = preComputedProof.deactivateLeaf.map(BigInt);
8160
+ preComputedTreeProof = {
8161
+ root: preComputedProof.root,
8162
+ pathElements: preComputedProof.pathElements
8163
+ };
8164
+ resolvedDeactivates = [];
8165
+ } else {
8166
+ if (!contractAddress) {
8167
+ throw new Error(
8168
+ "buildPreAddNewKeyPayload: `contractAddress` is required when `deactivates` is not provided"
8169
+ );
8170
+ }
8171
+ if (deactivateIdx === void 0) {
8172
+ throw new Error(
8173
+ "buildPreAddNewKeyPayload: `deactivateIdx` is required when `deactivates` is not provided"
8174
+ );
8175
+ }
8176
+ if (voterScale === void 0) {
8177
+ throw new Error(
8178
+ "buildPreAddNewKeyPayload: `voterScale` is required for the K-anonymous API path"
8179
+ );
8180
+ }
8181
+ const indicesParam = buildKAnonymousIndices(deactivateIdx, voterScale);
8182
+ const proofResp = await this.saasApiClient.getPreDeactivateProof(
8183
+ contractAddress,
8184
+ indicesParam
8185
+ );
8186
+ const pkg = proofResp.proofs.find((p) => p.leafIndex === deactivateIdx);
8187
+ if (!pkg) {
8188
+ throw new Error(
8189
+ `buildPreAddNewKeyPayload: proof package for leafIndex ${deactivateIdx} not found in API response`
8190
+ );
8191
+ }
8192
+ preComputedLeaf = pkg.deactivateLeaf.map(BigInt);
8193
+ preComputedTreeProof = {
8194
+ root: proofResp.root,
8195
+ pathElements: pkg.pathElements
8196
+ };
8197
+ resolvedDeactivates = [];
8198
+ }
8199
+ const genPreAddKeyInputStart = Date.now();
7766
8200
  const addKeyInput = await this.genPreAddKeyInput(stateTreeDepth + 2, {
7767
- coordPubKey: [coordPubkeyX, coordPubkeyY],
7768
- deactivates: deactivates.map((d) => d.map(BigInt)),
7769
- derivePathParams
8201
+ coordPubKey,
8202
+ deactivates: resolvedDeactivates,
8203
+ newPubKey: newPubkey,
8204
+ pollId,
8205
+ derivePathParams,
8206
+ preComputedTreeProof,
8207
+ preComputedLeaf,
8208
+ deactivateIdx
7770
8209
  });
8210
+ console.log(`[genPreAddKeyInput] elapsed: ${Date.now() - genPreAddKeyInputStart}ms`);
7771
8211
  if (addKeyInput === null) {
7772
8212
  throw Error("genPreAddKeyInput failed, cannot find deactivate idx");
7773
8213
  }
8214
+ const fullProveStart = Date.now();
7774
8215
  const { proof } = await import_snarkjs.groth16.fullProve(addKeyInput, wasmFile, zkeyFile);
8216
+ console.log(`[fullProve] elapsed: ${Date.now() - fullProveStart}ms`);
7775
8217
  const proofHex = await adaptToUncompressed(proof);
7776
8218
  return {
7777
8219
  proof: proofHex,
@@ -7787,6 +8229,8 @@ var VoterClient = class _VoterClient {
7787
8229
  async genAddKeyInput(depth, {
7788
8230
  coordPubKey,
7789
8231
  deactivates,
8232
+ newPubKey,
8233
+ pollId,
7790
8234
  derivePathParams
7791
8235
  }) {
7792
8236
  const signer = this.getSigner(derivePathParams);
@@ -7800,7 +8244,7 @@ var VoterClient = class _VoterClient {
7800
8244
  const c1 = [deactivateLeaf[0], deactivateLeaf[1]];
7801
8245
  const c2 = [deactivateLeaf[2], deactivateLeaf[3]];
7802
8246
  const { d1, d2 } = rerandomize(coordPubKey, { c1, c2 }, randomVal);
7803
- const nullifier = poseidon([signer.getFormatedPrivKey(), 1444992409218394441042n]);
8247
+ const nullifier = poseidon([signer.getFormatedPrivKey(), pollId]);
7804
8248
  const tree = new Tree(5, depth, 0n);
7805
8249
  const leaves = deactivates.map((d) => poseidon(d));
7806
8250
  tree.initLeaves(leaves);
@@ -7813,7 +8257,9 @@ var VoterClient = class _VoterClient {
7813
8257
  d1[0],
7814
8258
  d1[1],
7815
8259
  d2[0],
7816
- d2[1]
8260
+ d2[1],
8261
+ poseidon(newPubKey),
8262
+ pollId
7817
8263
  ]);
7818
8264
  const input = {
7819
8265
  inputHash,
@@ -7828,32 +8274,75 @@ var VoterClient = class _VoterClient {
7828
8274
  d2,
7829
8275
  deactivateLeafPathElements,
7830
8276
  nullifier,
7831
- oldPrivateKey: signer.getFormatedPrivKey()
8277
+ oldPrivateKey: signer.getFormatedPrivKey(),
8278
+ newPubKey,
8279
+ pollId
7832
8280
  };
7833
8281
  return input;
7834
8282
  }
7835
8283
  async genPreAddKeyInput(depth, {
7836
8284
  coordPubKey,
7837
8285
  deactivates,
7838
- derivePathParams
8286
+ newPubKey,
8287
+ pollId,
8288
+ derivePathParams,
8289
+ preComputedTreeProof,
8290
+ preComputedLeaf,
8291
+ deactivateIdx: providedDeactivateIdx
7839
8292
  }) {
8293
+ let t0 = Date.now();
7840
8294
  const signer = this.getSigner(derivePathParams);
7841
- const sharedKeyHash = poseidon(signer.genEcdhSharedKey(coordPubKey));
8295
+ console.log(`[genPreAddKeyInput] getSigner: ${Date.now() - t0}ms`);
8296
+ t0 = Date.now();
7842
8297
  const randomVal = genRandomSalt();
7843
- const deactivateIdx = deactivates.findIndex((d) => d[4] === sharedKeyHash);
7844
- if (deactivateIdx < 0) {
8298
+ let deactivateIdx;
8299
+ if (providedDeactivateIdx !== void 0) {
8300
+ deactivateIdx = providedDeactivateIdx;
8301
+ console.log(
8302
+ `[genPreAddKeyInput] using provided deactivateIdx=${deactivateIdx} (skip search)`
8303
+ );
8304
+ } else {
8305
+ const sharedKeyHash = poseidon(signer.genEcdhSharedKey(coordPubKey));
8306
+ console.log(`[genPreAddKeyInput] genEcdhSharedKey + poseidon: ${Date.now() - t0}ms`);
8307
+ t0 = Date.now();
8308
+ deactivateIdx = deactivates.findIndex((d) => d[4] === sharedKeyHash);
8309
+ if (deactivateIdx < 0) {
8310
+ return null;
8311
+ }
8312
+ console.log(`[genPreAddKeyInput] genRandomSalt + findDeactivateIdx: ${Date.now() - t0}ms`);
8313
+ t0 = Date.now();
8314
+ }
8315
+ const deactivateLeaf = preComputedLeaf ?? deactivates[deactivateIdx];
8316
+ if (!deactivateLeaf) {
7845
8317
  return null;
7846
8318
  }
7847
- const deactivateLeaf = deactivates[deactivateIdx];
7848
8319
  const c1 = [deactivateLeaf[0], deactivateLeaf[1]];
7849
8320
  const c2 = [deactivateLeaf[2], deactivateLeaf[3]];
7850
8321
  const { d1, d2 } = rerandomize(coordPubKey, { c1, c2 }, randomVal);
7851
- const nullifier = poseidon([signer.getFormatedPrivKey(), 1444992409218394441042n]);
7852
- const tree = new Tree(5, depth, 0n);
7853
- const leaves = deactivates.map((d) => poseidon(d));
7854
- tree.initLeaves(leaves);
7855
- const deactivateRoot = tree.root;
7856
- const deactivateLeafPathElements = tree.pathElementOf(deactivateIdx);
8322
+ console.log(`[genPreAddKeyInput] rerandomize: ${Date.now() - t0}ms`);
8323
+ t0 = Date.now();
8324
+ const nullifier = poseidon([signer.getFormatedPrivKey(), pollId]);
8325
+ console.log(`[genPreAddKeyInput] nullifier (poseidon): ${Date.now() - t0}ms`);
8326
+ t0 = Date.now();
8327
+ let deactivateRoot;
8328
+ let deactivateLeafPathElements;
8329
+ if (preComputedTreeProof) {
8330
+ deactivateRoot = BigInt(preComputedTreeProof.root);
8331
+ deactivateLeafPathElements = preComputedTreeProof.pathElements.map(
8332
+ (level) => level.map(BigInt)
8333
+ );
8334
+ console.log(`[genPreAddKeyInput] using preComputedTreeProof (API path)`);
8335
+ } else {
8336
+ const tree = new Tree(5, depth, 0n);
8337
+ const leaves = deactivates.map((d) => poseidon(d));
8338
+ tree.initLeaves(leaves);
8339
+ console.log(`[genPreAddKeyInput] build tree + initLeaves: ${Date.now() - t0}ms`);
8340
+ t0 = Date.now();
8341
+ deactivateRoot = tree.root;
8342
+ deactivateLeafPathElements = tree.pathElementOf(deactivateIdx);
8343
+ console.log(`[genPreAddKeyInput] tree.root + pathElementOf: ${Date.now() - t0}ms`);
8344
+ t0 = Date.now();
8345
+ }
7857
8346
  const inputHash = computeInputHash([
7858
8347
  deactivateRoot,
7859
8348
  poseidon(coordPubKey),
@@ -7861,8 +8350,12 @@ var VoterClient = class _VoterClient {
7861
8350
  d1[0],
7862
8351
  d1[1],
7863
8352
  d2[0],
7864
- d2[1]
8353
+ d2[1],
8354
+ poseidon(newPubKey),
8355
+ pollId
7865
8356
  ]);
8357
+ console.log(`[genPreAddKeyInput] computeInputHash: ${Date.now() - t0}ms`);
8358
+ t0 = Date.now();
7866
8359
  const input = {
7867
8360
  inputHash,
7868
8361
  coordPubKey,
@@ -7876,7 +8369,9 @@ var VoterClient = class _VoterClient {
7876
8369
  d2,
7877
8370
  deactivateLeafPathElements,
7878
8371
  nullifier,
7879
- oldPrivateKey: signer.getFormatedPrivKey()
8372
+ oldPrivateKey: signer.getFormatedPrivKey(),
8373
+ newPubKey,
8374
+ pollId
7880
8375
  };
7881
8376
  return input;
7882
8377
  }
@@ -7897,7 +8392,68 @@ var VoterClient = class _VoterClient {
7897
8392
  nonce = 0,
7898
8393
  derivePathParams
7899
8394
  }) {
7900
- const genMessage = this.genMessageFactory(stateIdx, operatorPubkey, pollId, derivePathParams);
8395
+ const genMessage = pollId !== void 0 ? this.genMessageFactory(stateIdx, operatorPubkey, pollId, derivePathParams) : this.legacyGenMessageFactory(stateIdx, operatorPubkey, derivePathParams);
8396
+ const encAccount = genKeypair();
8397
+ const msg = genMessage(BigInt(encAccount.privKey), nonce, 0, 0, true);
8398
+ return stringizing({
8399
+ msg,
8400
+ encPubkeys: encAccount.pubKey
8401
+ });
8402
+ }
8403
+ // ==================== Legacy Methods (backward-compat, no pollId) ====================
8404
+ legacyGenMessageFactory(stateIdx, operatorPubkey, derivePathParams) {
8405
+ return (encPriKey, nonce, voIdx, newVotes, isLastCmd, salt) => {
8406
+ if (salt === void 0) {
8407
+ salt = BigInt(`0x${import_crypto_js3.default.lib.WordArray.random(7).toString(import_crypto_js3.default.enc.Hex)}`);
8408
+ }
8409
+ const packaged = BigInt(nonce) + (BigInt(stateIdx) << 32n) + (BigInt(voIdx) << 64n) + (BigInt(newVotes) << 96n) + (BigInt(salt) << 192n);
8410
+ const signer = this.getSigner(derivePathParams);
8411
+ let newPubKey;
8412
+ if (isLastCmd) {
8413
+ newPubKey = [0n, 0n];
8414
+ } else {
8415
+ newPubKey = [...signer.getPublicKey().toPoints()];
8416
+ }
8417
+ const hash = poseidon([packaged, ...newPubKey]);
8418
+ const signature = signer.sign(hash);
8419
+ const command = [packaged, ...newPubKey, ...signature.R8, signature.S];
8420
+ const coordPubkey = this.unpackMaciPubkey(operatorPubkey);
8421
+ const message = (0, import_poseidon_cipher3.poseidonEncrypt)(command, genEcdhSharedKey(encPriKey, coordPubkey), 0n);
8422
+ return message;
8423
+ };
8424
+ }
8425
+ legacyBatchGenMessage(stateIdx, operatorPubkey, plan, derivePathParams) {
8426
+ const genMessage = this.legacyGenMessageFactory(stateIdx, operatorPubkey, derivePathParams);
8427
+ const payload = [];
8428
+ for (let i = plan.length - 1; i >= 0; i--) {
8429
+ const p = plan[i];
8430
+ const encAccount = genKeypair();
8431
+ const isLastCmd = i === plan.length - 1;
8432
+ const msg = genMessage(BigInt(encAccount.privKey), i + 1, p[0], p[1], isLastCmd);
8433
+ payload.push({
8434
+ msg,
8435
+ encPubkeys: encAccount.pubKey
8436
+ });
8437
+ }
8438
+ return payload;
8439
+ }
8440
+ legacyBuildVotePayload({
8441
+ stateIdx,
8442
+ operatorPubkey,
8443
+ selectedOptions,
8444
+ derivePathParams
8445
+ }) {
8446
+ const plan = this.normalizeVoteOptions(selectedOptions);
8447
+ const payload = this.legacyBatchGenMessage(stateIdx, operatorPubkey, plan, derivePathParams);
8448
+ return stringizing(payload);
8449
+ }
8450
+ legacyBuildDeactivatePayload({
8451
+ stateIdx,
8452
+ operatorPubkey,
8453
+ nonce = 0,
8454
+ derivePathParams
8455
+ }) {
8456
+ const genMessage = this.legacyGenMessageFactory(stateIdx, operatorPubkey, derivePathParams);
7901
8457
  const encAccount = genKeypair();
7902
8458
  const msg = genMessage(BigInt(encAccount.privKey), nonce, 0, 0, true);
7903
8459
  return stringizing({
@@ -7905,6 +8461,164 @@ var VoterClient = class _VoterClient {
7905
8461
  encPubkeys: encAccount.pubKey
7906
8462
  });
7907
8463
  }
8464
+ async legacyGenAddKeyInput(depth, {
8465
+ coordPubKey,
8466
+ deactivates,
8467
+ derivePathParams
8468
+ }) {
8469
+ const signer = this.getSigner(derivePathParams);
8470
+ const sharedKeyHash = poseidon(signer.genEcdhSharedKey(coordPubKey));
8471
+ const randomVal = genRandomSalt();
8472
+ const deactivateIdx = deactivates.findIndex((d) => d[4] === sharedKeyHash);
8473
+ if (deactivateIdx < 0) {
8474
+ return null;
8475
+ }
8476
+ const deactivateLeaf = deactivates[deactivateIdx];
8477
+ const c1 = [deactivateLeaf[0], deactivateLeaf[1]];
8478
+ const c2 = [deactivateLeaf[2], deactivateLeaf[3]];
8479
+ const { d1, d2 } = rerandomize(coordPubKey, { c1, c2 }, randomVal);
8480
+ const nullifier = poseidon([signer.getFormatedPrivKey(), 1444992409218394441042n]);
8481
+ const tree = new Tree(5, depth, 0n);
8482
+ const leaves = deactivates.map((d) => poseidon(d));
8483
+ tree.initLeaves(leaves);
8484
+ const deactivateRoot = tree.root;
8485
+ const deactivateLeafPathElements = tree.pathElementOf(deactivateIdx);
8486
+ const inputHash = computeInputHash([
8487
+ deactivateRoot,
8488
+ poseidon(coordPubKey),
8489
+ nullifier,
8490
+ d1[0],
8491
+ d1[1],
8492
+ d2[0],
8493
+ d2[1]
8494
+ ]);
8495
+ return {
8496
+ inputHash,
8497
+ coordPubKey,
8498
+ deactivateRoot,
8499
+ deactivateIndex: deactivateIdx,
8500
+ deactivateLeaf: poseidon(deactivateLeaf),
8501
+ c1,
8502
+ c2,
8503
+ randomVal,
8504
+ d1,
8505
+ d2,
8506
+ deactivateLeafPathElements,
8507
+ nullifier,
8508
+ oldPrivateKey: signer.getFormatedPrivKey()
8509
+ };
8510
+ }
8511
+ async legacyGenPreAddKeyInput(depth, {
8512
+ coordPubKey,
8513
+ deactivates,
8514
+ derivePathParams
8515
+ }) {
8516
+ const signer = this.getSigner(derivePathParams);
8517
+ const sharedKeyHash = poseidon(signer.genEcdhSharedKey(coordPubKey));
8518
+ const randomVal = genRandomSalt();
8519
+ const deactivateIdx = deactivates.findIndex((d) => d[4] === sharedKeyHash);
8520
+ if (deactivateIdx < 0) {
8521
+ return null;
8522
+ }
8523
+ const deactivateLeaf = deactivates[deactivateIdx];
8524
+ const c1 = [deactivateLeaf[0], deactivateLeaf[1]];
8525
+ const c2 = [deactivateLeaf[2], deactivateLeaf[3]];
8526
+ const { d1, d2 } = rerandomize(coordPubKey, { c1, c2 }, randomVal);
8527
+ const nullifier = poseidon([signer.getFormatedPrivKey(), 1444992409218394441042n]);
8528
+ const tree = new Tree(5, depth, 0n);
8529
+ const leaves = deactivates.map((d) => poseidon(d));
8530
+ tree.initLeaves(leaves);
8531
+ const deactivateRoot = tree.root;
8532
+ const deactivateLeafPathElements = tree.pathElementOf(deactivateIdx);
8533
+ const inputHash = computeInputHash([
8534
+ deactivateRoot,
8535
+ poseidon(coordPubKey),
8536
+ nullifier,
8537
+ d1[0],
8538
+ d1[1],
8539
+ d2[0],
8540
+ d2[1]
8541
+ ]);
8542
+ return {
8543
+ inputHash,
8544
+ coordPubKey,
8545
+ deactivateRoot,
8546
+ deactivateIndex: deactivateIdx,
8547
+ deactivateLeaf: poseidon(deactivateLeaf),
8548
+ c1,
8549
+ c2,
8550
+ randomVal,
8551
+ d1,
8552
+ d2,
8553
+ deactivateLeafPathElements,
8554
+ nullifier,
8555
+ oldPrivateKey: signer.getFormatedPrivKey()
8556
+ };
8557
+ }
8558
+ /**
8559
+ * Legacy `buildAddNewKeyPayload` — old deactivate+addNewKey flow without `pollId` / `newPubKey`
8560
+ * in the ZK circuit. Use when interacting with contracts that predate the poll-ID upgrade.
8561
+ */
8562
+ async legacyBuildAddNewKeyPayload({
8563
+ stateTreeDepth,
8564
+ operatorPubkey,
8565
+ deactivates,
8566
+ wasmFile,
8567
+ zkeyFile,
8568
+ derivePathParams
8569
+ }) {
8570
+ const [coordPubkeyX, coordPubkeyY] = this.unpackMaciPubkey(operatorPubkey);
8571
+ const addKeyInput = await this.legacyGenAddKeyInput(stateTreeDepth + 2, {
8572
+ coordPubKey: [coordPubkeyX, coordPubkeyY],
8573
+ deactivates: deactivates.map((d) => d.map(BigInt)),
8574
+ derivePathParams
8575
+ });
8576
+ if (addKeyInput === null) {
8577
+ throw Error("legacyGenAddKeyInput failed, cannot find deactivate idx");
8578
+ }
8579
+ const { proof } = await import_snarkjs.groth16.fullProve(addKeyInput, wasmFile, zkeyFile);
8580
+ const proofHex = await adaptToUncompressed(proof);
8581
+ return {
8582
+ proof: proofHex,
8583
+ d: [
8584
+ addKeyInput.d1[0].toString(),
8585
+ addKeyInput.d1[1].toString(),
8586
+ addKeyInput.d2[0].toString(),
8587
+ addKeyInput.d2[1].toString()
8588
+ ],
8589
+ nullifier: addKeyInput.nullifier.toString()
8590
+ };
8591
+ }
8592
+ async legacyBuildPreAddNewKeyPayload({
8593
+ stateTreeDepth,
8594
+ coordinatorPubkey,
8595
+ deactivates,
8596
+ wasmFile,
8597
+ zkeyFile,
8598
+ derivePathParams
8599
+ }) {
8600
+ const [coordPubkeyX, coordPubkeyY] = this.unpackMaciPubkey(coordinatorPubkey);
8601
+ const addKeyInput = await this.legacyGenPreAddKeyInput(stateTreeDepth + 2, {
8602
+ coordPubKey: [coordPubkeyX, coordPubkeyY],
8603
+ deactivates: deactivates.map((d) => d.map(BigInt)),
8604
+ derivePathParams
8605
+ });
8606
+ if (addKeyInput === null) {
8607
+ throw Error("legacyGenPreAddKeyInput failed, cannot find deactivate idx");
8608
+ }
8609
+ const { proof } = await import_snarkjs.groth16.fullProve(addKeyInput, wasmFile, zkeyFile);
8610
+ const proofHex = await adaptToUncompressed(proof);
8611
+ return {
8612
+ proof: proofHex,
8613
+ d: [
8614
+ addKeyInput.d1[0].toString(),
8615
+ addKeyInput.d1[1].toString(),
8616
+ addKeyInput.d2[0].toString(),
8617
+ addKeyInput.d2[1].toString()
8618
+ ],
8619
+ nullifier: addKeyInput.nullifier.toString()
8620
+ };
8621
+ }
7908
8622
  // ==================== SaaS API Client Methods ====================
7909
8623
  /**
7910
8624
  * Create a MACI round via SaaS API
@@ -7995,8 +8709,14 @@ var VoterClient = class _VoterClient {
7995
8709
  }
7996
8710
  // ==================== Maci Voter Methods ====================
7997
8711
  /**
7998
- * Pre-create a new account for AMACI voting (pre-deactivate mode)
7999
- * @param params - Parameters including contract address, deactivates, circuit files, and ticket
8712
+ * Pre-create a new account for AMACI voting (pre-deactivate mode).
8713
+ *
8714
+ * Two modes are supported:
8715
+ * - **Local mode**: pass `deactivates` to build the Merkle tree locally (original behaviour).
8716
+ * - **API mode**: omit `deactivates` and the proof will be fetched from the SaaS API using
8717
+ * `contractAddress` (K-anonymous request).
8718
+ *
8719
+ * @param params - Parameters including contract address, optional deactivates, circuit files, and ticket
8000
8720
  * @returns Result with transaction details and new voter account
8001
8721
  */
8002
8722
  async saasPreCreateNewAccount({
@@ -8004,32 +8724,43 @@ var VoterClient = class _VoterClient {
8004
8724
  stateTreeDepth,
8005
8725
  coordinatorPubkey,
8006
8726
  deactivates,
8727
+ deactivateIdx,
8728
+ voterScale,
8729
+ preComputedProof,
8730
+ pollId,
8007
8731
  wasmFile,
8008
8732
  zkeyFile,
8009
8733
  ticket,
8010
8734
  derivePathParams
8011
8735
  }) {
8736
+ const newVoterClient = new _VoterClient({
8737
+ network: this.network,
8738
+ restEndpoint: this.restEndpoint,
8739
+ apiEndpoint: this.apiEndpoint,
8740
+ saasApiEndpoint: this.saasApiEndpoint,
8741
+ registryAddress: this.registryAddress
8742
+ });
8743
+ const newPubkey = newVoterClient.getPubkey().toPoints();
8012
8744
  const addNewKeyPayload = await this.buildPreAddNewKeyPayload({
8013
8745
  stateTreeDepth,
8014
8746
  coordinatorPubkey,
8015
8747
  deactivates,
8748
+ contractAddress,
8749
+ deactivateIdx,
8750
+ voterScale,
8751
+ preComputedProof,
8752
+ newPubkey,
8753
+ pollId: pollId !== void 0 ? BigInt(pollId) : void 0,
8016
8754
  wasmFile,
8017
8755
  zkeyFile,
8018
8756
  derivePathParams
8019
8757
  });
8020
- const newVoterClient = new _VoterClient({
8021
- network: this.network,
8022
- restEndpoint: this.restEndpoint,
8023
- apiEndpoint: this.apiEndpoint,
8024
- saasApiEndpoint: this.saasApiEndpoint,
8025
- registryAddress: this.registryAddress
8026
- });
8027
8758
  const addNewKeyResult = await newVoterClient.saasSubmitPreAddNewKey({
8028
8759
  contractAddress,
8029
8760
  proof: addNewKeyPayload.proof,
8030
8761
  d: addNewKeyPayload.d,
8031
8762
  nullifier: addNewKeyPayload.nullifier,
8032
- newPubkey: newVoterClient.getPubkey().toPoints().map((p) => p.toString()),
8763
+ newPubkey: newPubkey.map((p) => p.toString()),
8033
8764
  ticket
8034
8765
  });
8035
8766
  return {
@@ -8047,22 +8778,19 @@ var VoterClient = class _VoterClient {
8047
8778
  operatorPubkey,
8048
8779
  selectedOptions,
8049
8780
  ticket,
8781
+ pollId,
8782
+ stateIdx,
8050
8783
  derivePathParams
8051
8784
  }) {
8052
- const stateIdx = await this.getStateIdx({
8053
- contractAddress,
8054
- derivePathParams
8055
- });
8056
- if (stateIdx === -1) {
8785
+ const resolvedStateIdx = stateIdx !== void 0 ? stateIdx : await this.getStateIdx({ contractAddress, derivePathParams });
8786
+ if (resolvedStateIdx === -1) {
8057
8787
  throw new Error("State index is not set, Please signup or addNewKey first");
8058
8788
  }
8059
- const pollId = await this.getPollId(contractAddress);
8060
8789
  const payload = this.buildVotePayload({
8061
- stateIdx,
8790
+ stateIdx: resolvedStateIdx,
8062
8791
  operatorPubkey,
8063
8792
  selectedOptions,
8064
8793
  pollId,
8065
- // Pass pollId instead of contractAddress
8066
8794
  derivePathParams
8067
8795
  });
8068
8796
  const voteResult = await this.saasSubmitVote({
@@ -8278,6 +9006,8 @@ var OperatorClient = class {
8278
9006
  stateTreeDepth,
8279
9007
  operatorPubkey,
8280
9008
  deactivates,
9009
+ newPubkey,
9010
+ pollId,
8281
9011
  wasmFile,
8282
9012
  zkeyFile,
8283
9013
  derivePathParams
@@ -8286,6 +9016,8 @@ var OperatorClient = class {
8286
9016
  const addKeyInput = await this.genAddKeyInput(stateTreeDepth + 2, {
8287
9017
  coordPubKey: [coordPubkeyX, coordPubkeyY],
8288
9018
  deactivates: deactivates.map((d) => d.map(BigInt)),
9019
+ newPubKey: newPubkey,
9020
+ pollId,
8289
9021
  derivePathParams
8290
9022
  });
8291
9023
  if (addKeyInput === null) {
@@ -8308,6 +9040,8 @@ var OperatorClient = class {
8308
9040
  stateTreeDepth,
8309
9041
  coordinatorPubkey,
8310
9042
  deactivates,
9043
+ newPubkey,
9044
+ pollId,
8311
9045
  wasmFile,
8312
9046
  zkeyFile,
8313
9047
  derivePathParams
@@ -8316,6 +9050,8 @@ var OperatorClient = class {
8316
9050
  const addKeyInput = await this.genPreAddKeyInput(stateTreeDepth + 2, {
8317
9051
  coordPubKey: [coordPubkeyX, coordPubkeyY],
8318
9052
  deactivates: deactivates.map((d) => d.map(BigInt)),
9053
+ newPubKey: newPubkey,
9054
+ pollId,
8319
9055
  derivePathParams
8320
9056
  });
8321
9057
  if (addKeyInput === null) {
@@ -8337,6 +9073,8 @@ var OperatorClient = class {
8337
9073
  async genAddKeyInput(depth, {
8338
9074
  coordPubKey,
8339
9075
  deactivates,
9076
+ newPubKey,
9077
+ pollId,
8340
9078
  derivePathParams
8341
9079
  }) {
8342
9080
  const signer = this.getSigner(derivePathParams);
@@ -8350,7 +9088,7 @@ var OperatorClient = class {
8350
9088
  const c1 = [deactivateLeaf[0], deactivateLeaf[1]];
8351
9089
  const c2 = [deactivateLeaf[2], deactivateLeaf[3]];
8352
9090
  const { d1, d2 } = rerandomize(coordPubKey, { c1, c2 }, randomVal);
8353
- const nullifier = poseidon([signer.getFormatedPrivKey(), 1444992409218394441042n]);
9091
+ const nullifier = poseidon([signer.getFormatedPrivKey(), pollId]);
8354
9092
  const tree = new Tree(5, depth, 0n);
8355
9093
  const leaves = deactivates.map((d) => poseidon(d));
8356
9094
  tree.initLeaves(leaves);
@@ -8363,7 +9101,9 @@ var OperatorClient = class {
8363
9101
  d1[0],
8364
9102
  d1[1],
8365
9103
  d2[0],
8366
- d2[1]
9104
+ d2[1],
9105
+ poseidon(newPubKey),
9106
+ pollId
8367
9107
  ]);
8368
9108
  const input = {
8369
9109
  inputHash,
@@ -8378,13 +9118,17 @@ var OperatorClient = class {
8378
9118
  d2,
8379
9119
  deactivateLeafPathElements,
8380
9120
  nullifier,
8381
- oldPrivateKey: signer.getFormatedPrivKey()
9121
+ oldPrivateKey: signer.getFormatedPrivKey(),
9122
+ newPubKey,
9123
+ pollId
8382
9124
  };
8383
9125
  return input;
8384
9126
  }
8385
9127
  async genPreAddKeyInput(depth, {
8386
9128
  coordPubKey,
8387
9129
  deactivates,
9130
+ newPubKey,
9131
+ pollId,
8388
9132
  derivePathParams
8389
9133
  }) {
8390
9134
  const signer = this.getSigner(derivePathParams);
@@ -8398,7 +9142,7 @@ var OperatorClient = class {
8398
9142
  const c1 = [deactivateLeaf[0], deactivateLeaf[1]];
8399
9143
  const c2 = [deactivateLeaf[2], deactivateLeaf[3]];
8400
9144
  const { d1, d2 } = rerandomize(coordPubKey, { c1, c2 }, randomVal);
8401
- const nullifier = poseidon([signer.getFormatedPrivKey(), 1444992409218394441042n]);
9145
+ const nullifier = poseidon([signer.getFormatedPrivKey(), pollId]);
8402
9146
  const tree = new Tree(5, depth, 0n);
8403
9147
  const leaves = deactivates.map((d) => poseidon(d));
8404
9148
  tree.initLeaves(leaves);
@@ -8411,7 +9155,9 @@ var OperatorClient = class {
8411
9155
  d1[0],
8412
9156
  d1[1],
8413
9157
  d2[0],
8414
- d2[1]
9158
+ d2[1],
9159
+ poseidon(newPubKey),
9160
+ pollId
8415
9161
  ]);
8416
9162
  const input = {
8417
9163
  inputHash,
@@ -8426,7 +9172,9 @@ var OperatorClient = class {
8426
9172
  d2,
8427
9173
  deactivateLeafPathElements,
8428
9174
  nullifier,
8429
- oldPrivateKey: signer.getFormatedPrivKey()
9175
+ oldPrivateKey: signer.getFormatedPrivKey(),
9176
+ newPubKey,
9177
+ pollId
8430
9178
  };
8431
9179
  return input;
8432
9180
  }
@@ -8445,7 +9193,13 @@ var OperatorClient = class {
8445
9193
  pollId,
8446
9194
  derivePathParams
8447
9195
  }) {
8448
- const payload = this.batchGenMessage(stateIdx, operatorPubkey, [[0, 0]], pollId, derivePathParams);
9196
+ const payload = this.batchGenMessage(
9197
+ stateIdx,
9198
+ operatorPubkey,
9199
+ [[0, 0]],
9200
+ pollId,
9201
+ derivePathParams
9202
+ );
8449
9203
  return stringizing(payload[0]);
8450
9204
  }
8451
9205
  /**
@@ -8549,10 +9303,10 @@ var OperatorClient = class {
8549
9303
  }
8550
9304
  /**
8551
9305
  * Decrypt message to command
8552
- *
9306
+ *
8553
9307
  * Message structure after decryption (7 elements):
8554
9308
  * [packed_data, newPubKey_x, newPubKey_y, salt, sig_R8_x, sig_R8_y, sig_S]
8555
- *
9309
+ *
8556
9310
  * Packed data contains (from low to high bits):
8557
9311
  * - nonce (bits 0-31)
8558
9312
  * - stateIdx (bits 32-63)
@@ -8949,6 +9703,19 @@ var OperatorClient = class {
8949
9703
  throw new Error("Poll ID not set. Ensure initRound was called with pollId parameter.");
8950
9704
  }
8951
9705
  const batchSize = this.batchSize;
9706
+ if (this.msgEndIdx === 0) {
9707
+ this.endProcessingPeriod();
9708
+ const newStateRoot2 = this.stateTree.root;
9709
+ const newStateCommitment2 = poseidon([newStateRoot2, newStateSalt]);
9710
+ this.stateCommitment = newStateCommitment2;
9711
+ return {
9712
+ input: {
9713
+ newStateCommitment: newStateCommitment2,
9714
+ packedVals: BigInt(this.maxVoteOptions) + (BigInt(this.numSignUps) << 32n) + (this.isQuadraticCost ? 1n << 64n : 0n)
9715
+ },
9716
+ proof: null
9717
+ };
9718
+ }
8952
9719
  const batchStartIdx = Math.floor((this.msgEndIdx - 1) / batchSize) * batchSize;
8953
9720
  const batchEndIdx = Math.min(batchStartIdx + batchSize, this.msgEndIdx);
8954
9721
  console.log(`Process messages [${batchStartIdx}, ${batchEndIdx})`);