@vocdoni/davinci-sdk 0.0.2 → 0.0.3

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.mjs CHANGED
@@ -213,6 +213,281 @@ function assertCSPCensusProof(proof) {
213
213
  }
214
214
  }
215
215
 
216
+ var CensusType = /* @__PURE__ */ ((CensusType2) => {
217
+ CensusType2["PLAIN"] = "plain";
218
+ CensusType2["WEIGHTED"] = "weighted";
219
+ CensusType2["CSP"] = "csp";
220
+ return CensusType2;
221
+ })(CensusType || {});
222
+ class Census {
223
+ constructor(type) {
224
+ this._censusId = null;
225
+ this._censusRoot = null;
226
+ this._censusURI = null;
227
+ this._size = null;
228
+ this._type = type;
229
+ }
230
+ get censusId() {
231
+ return this._censusId;
232
+ }
233
+ get censusRoot() {
234
+ return this._censusRoot;
235
+ }
236
+ get censusURI() {
237
+ return this._censusURI;
238
+ }
239
+ get type() {
240
+ return this._type;
241
+ }
242
+ get size() {
243
+ return this._size;
244
+ }
245
+ get isPublished() {
246
+ return this._censusRoot !== null && this._censusURI !== null;
247
+ }
248
+ /**
249
+ * Convert CensusType to CensusOrigin enum for API compatibility
250
+ */
251
+ get censusOrigin() {
252
+ switch (this._type) {
253
+ case "plain" /* PLAIN */:
254
+ case "weighted" /* WEIGHTED */:
255
+ return CensusOrigin.CensusOriginMerkleTree;
256
+ case "csp" /* CSP */:
257
+ return CensusOrigin.CensusOriginCSP;
258
+ default:
259
+ throw new Error(`Unknown census type: ${this._type}`);
260
+ }
261
+ }
262
+ }
263
+
264
+ class PlainCensus extends Census {
265
+ constructor() {
266
+ super(CensusType.PLAIN);
267
+ this._participants = /* @__PURE__ */ new Set();
268
+ }
269
+ /**
270
+ * Add participant(s) with automatic weight=1
271
+ * @param addresses - Single address or array of addresses
272
+ */
273
+ add(addresses) {
274
+ const toAdd = Array.isArray(addresses) ? addresses : [addresses];
275
+ for (const address of toAdd) {
276
+ this.validateAddress(address);
277
+ this._participants.add(address.toLowerCase());
278
+ }
279
+ }
280
+ /**
281
+ * Remove participant by address
282
+ */
283
+ remove(address) {
284
+ this._participants.delete(address.toLowerCase());
285
+ }
286
+ /**
287
+ * Get all participants as CensusParticipant array (for API)
288
+ * All participants have weight="1"
289
+ */
290
+ get participants() {
291
+ return Array.from(this._participants).map((key) => ({
292
+ key,
293
+ weight: "1"
294
+ // Everyone has weight=1 in plain census
295
+ }));
296
+ }
297
+ /**
298
+ * Get addresses only
299
+ */
300
+ get addresses() {
301
+ return Array.from(this._participants);
302
+ }
303
+ validateAddress(address) {
304
+ if (!address || typeof address !== "string") {
305
+ throw new Error("Address is required and must be a string");
306
+ }
307
+ if (!/^(0x)?[0-9a-fA-F]{40}$/i.test(address)) {
308
+ throw new Error(`Invalid Ethereum address format: ${address}`);
309
+ }
310
+ }
311
+ /**
312
+ * Internal method called after publishing
313
+ * @internal
314
+ */
315
+ _setPublishedData(root, uri, size, censusId) {
316
+ this._censusRoot = root;
317
+ this._censusURI = uri;
318
+ this._size = size;
319
+ if (censusId) this._censusId = censusId;
320
+ }
321
+ }
322
+
323
+ class WeightedCensus extends Census {
324
+ constructor() {
325
+ super(CensusType.WEIGHTED);
326
+ this._participants = /* @__PURE__ */ new Map();
327
+ }
328
+ /**
329
+ * Add participant(s) with custom weights
330
+ * Weight can be provided as string, number, or bigint - will be converted to string internally
331
+ * @param participant - Single participant or array of participants with custom weights
332
+ */
333
+ add(participant) {
334
+ const toAdd = Array.isArray(participant) ? participant : [participant];
335
+ for (const p of toAdd) {
336
+ this.validateParticipant(p);
337
+ const weightString = this.normalizeWeight(p.weight);
338
+ this._participants.set(p.key.toLowerCase(), weightString);
339
+ }
340
+ }
341
+ /**
342
+ * Remove participant by address
343
+ */
344
+ remove(address) {
345
+ this._participants.delete(address.toLowerCase());
346
+ }
347
+ /**
348
+ * Get all participants as CensusParticipant array
349
+ */
350
+ get participants() {
351
+ return Array.from(this._participants.entries()).map(([key, weight]) => ({
352
+ key,
353
+ weight
354
+ }));
355
+ }
356
+ /**
357
+ * Get participant addresses
358
+ */
359
+ get addresses() {
360
+ return Array.from(this._participants.keys());
361
+ }
362
+ /**
363
+ * Get weight for specific address
364
+ */
365
+ getWeight(address) {
366
+ return this._participants.get(address.toLowerCase());
367
+ }
368
+ /**
369
+ * Normalizes weight from string, number, or bigint to string
370
+ */
371
+ normalizeWeight(weight) {
372
+ if (typeof weight === "string") {
373
+ if (!/^\d+$/.test(weight)) {
374
+ throw new Error(`Invalid weight format: ${weight}. Must be a positive integer.`);
375
+ }
376
+ return weight;
377
+ }
378
+ if (typeof weight === "number") {
379
+ if (!Number.isInteger(weight) || weight < 0) {
380
+ throw new Error(`Invalid weight: ${weight}. Must be a positive integer.`);
381
+ }
382
+ return weight.toString();
383
+ }
384
+ if (typeof weight === "bigint") {
385
+ if (weight < 0n) {
386
+ throw new Error(`Invalid weight: ${weight}. Must be a positive integer.`);
387
+ }
388
+ return weight.toString();
389
+ }
390
+ throw new Error(`Invalid weight type. Must be string, number, or bigint.`);
391
+ }
392
+ validateParticipant(participant) {
393
+ if (!participant.key || typeof participant.key !== "string") {
394
+ throw new Error("Participant key (address) is required");
395
+ }
396
+ if (!/^(0x)?[0-9a-fA-F]{40}$/i.test(participant.key)) {
397
+ throw new Error(`Invalid Ethereum address format: ${participant.key}`);
398
+ }
399
+ if (participant.weight === void 0 || participant.weight === null) {
400
+ throw new Error("Participant weight is required");
401
+ }
402
+ }
403
+ /**
404
+ * Internal method called after publishing
405
+ * @internal
406
+ */
407
+ _setPublishedData(root, uri, size, censusId) {
408
+ this._censusRoot = root;
409
+ this._censusURI = uri;
410
+ this._size = size;
411
+ if (censusId) this._censusId = censusId;
412
+ }
413
+ }
414
+
415
+ class CspCensus extends Census {
416
+ constructor(publicKey, cspURI, size) {
417
+ super(CensusType.CSP);
418
+ if (!/^(0x)?[0-9a-fA-F]+$/.test(publicKey)) {
419
+ throw new Error("Public key is missing or invalid");
420
+ }
421
+ try {
422
+ new URL(cspURI);
423
+ } catch {
424
+ throw new Error("CSP URI is missing or invalid");
425
+ }
426
+ this._publicKey = publicKey;
427
+ this._cspURI = cspURI;
428
+ this._censusRoot = publicKey;
429
+ this._censusURI = cspURI;
430
+ this._size = size;
431
+ }
432
+ get publicKey() {
433
+ return this._publicKey;
434
+ }
435
+ get cspURI() {
436
+ return this._cspURI;
437
+ }
438
+ }
439
+
440
+ class PublishedCensus extends Census {
441
+ constructor(type, root, uri, size) {
442
+ super(type);
443
+ this._censusRoot = root;
444
+ this._censusURI = uri;
445
+ this._size = size;
446
+ }
447
+ }
448
+
449
+ class CensusOrchestrator {
450
+ constructor(censusService) {
451
+ this.censusService = censusService;
452
+ }
453
+ /**
454
+ * Publishes a PlainCensus or WeightedCensus
455
+ * Creates a working census, adds participants, and publishes it
456
+ */
457
+ async publish(census) {
458
+ if (census.isPublished) {
459
+ throw new Error("Census is already published");
460
+ }
461
+ if (census.participants.length === 0) {
462
+ throw new Error("Cannot publish empty census");
463
+ }
464
+ const censusId = await this.censusService.createCensus();
465
+ await this.censusService.addParticipants(censusId, census.participants);
466
+ const publishResponse = await this.censusService.publishCensus(censusId);
467
+ census._setPublishedData(
468
+ publishResponse.root,
469
+ publishResponse.uri,
470
+ publishResponse.participantCount,
471
+ censusId
472
+ );
473
+ }
474
+ /**
475
+ * Gets census data for process creation
476
+ * Throws if census is not published
477
+ */
478
+ getCensusData(census) {
479
+ if (!census.isPublished) {
480
+ throw new Error("Census must be published before creating a process");
481
+ }
482
+ return {
483
+ type: census.censusOrigin,
484
+ root: census.censusRoot,
485
+ uri: census.censusURI,
486
+ size: census.size
487
+ };
488
+ }
489
+ }
490
+
216
491
  function createProcessSignatureMessage(processId) {
217
492
  const cleanProcessId = processId.replace(/^0x/, "").toLowerCase();
218
493
  return `I am creating a new voting process for the davinci.vote protocol identified with id ${cleanProcessId}`;
@@ -303,11 +578,15 @@ class VocdoniSequencerService extends BaseService {
303
578
  try {
304
579
  const response = await fetch(hashOrUrl);
305
580
  if (!response.ok) {
306
- throw new Error(`Failed to fetch metadata from URL: ${response.status} ${response.statusText}`);
581
+ throw new Error(
582
+ `Failed to fetch metadata from URL: ${response.status} ${response.statusText}`
583
+ );
307
584
  }
308
585
  return await response.json();
309
586
  } catch (error) {
310
- throw new Error(`Failed to fetch metadata from URL: ${error instanceof Error ? error.message : "Unknown error"}`);
587
+ throw new Error(
588
+ `Failed to fetch metadata from URL: ${error instanceof Error ? error.message : "Unknown error"}`
589
+ );
311
590
  }
312
591
  }
313
592
  if (!isHexString(hashOrUrl)) {
@@ -343,101 +622,6 @@ class VocdoniApiService {
343
622
  }
344
623
  }
345
624
 
346
- const DEFAULT_ENVIRONMENT_URLS = {
347
- dev: {
348
- sequencer: "https://sequencer-dev.davinci.vote",
349
- census: "https://c3-dev.davinci.vote",
350
- chain: "sepolia"
351
- },
352
- stg: {
353
- sequencer: "https://sequencer1.davinci.vote",
354
- census: "https://c3.davinci.vote",
355
- chain: "sepolia"
356
- },
357
- prod: {
358
- // TODO: Add production URLs when available
359
- sequencer: "",
360
- census: "",
361
- chain: "mainnet"
362
- }
363
- };
364
- function getEnvironmentConfig(environment) {
365
- return DEFAULT_ENVIRONMENT_URLS[environment];
366
- }
367
- function getEnvironmentUrls(environment) {
368
- const config = DEFAULT_ENVIRONMENT_URLS[environment];
369
- return {
370
- sequencer: config.sequencer,
371
- census: config.census
372
- };
373
- }
374
- function getEnvironmentChain(environment) {
375
- return DEFAULT_ENVIRONMENT_URLS[environment].chain;
376
- }
377
- function resolveConfiguration(options = {}) {
378
- const environment = options.environment || "prod";
379
- const defaultConfig = getEnvironmentConfig(environment);
380
- const resolvedConfig = { ...defaultConfig };
381
- if (options.customUrls) {
382
- if (options.customUrls.sequencer !== void 0) {
383
- resolvedConfig.sequencer = options.customUrls.sequencer;
384
- }
385
- if (options.customUrls.census !== void 0) {
386
- resolvedConfig.census = options.customUrls.census;
387
- }
388
- }
389
- if (options.customChain) {
390
- resolvedConfig.chain = options.customChain;
391
- }
392
- return resolvedConfig;
393
- }
394
- function resolveUrls(options = {}) {
395
- const config = resolveConfiguration(options);
396
- return {
397
- sequencer: config.sequencer,
398
- census: config.census
399
- };
400
- }
401
-
402
- var processRegistry = {
403
- sepolia: "0x50CA6A350f3A9C7B8a82eE7a4D5F0f21C54D68e3",
404
- uzh: "0x69B16f67Bd2fB18bD720379E9C1Ef5EaD3872d67",
405
- mainnet: "0x0",
406
- celo: "0xDda6c75d32c375946C8ae9be41B2F3539dB1118A"
407
- };
408
- var organizationRegistry = {
409
- sepolia: "0xF30678f579Fd89b86295503dC179d5d3aed47a98",
410
- uzh: "0xf7BCE4546805547bE526Ca864d6722Ed193E51Aa",
411
- mainnet: "0x0",
412
- celo: "0xE17D701EA8f34022F97fC2Ec68c73D42bF99D0BD"
413
- };
414
- var stateTransitionVerifierGroth16 = {
415
- sepolia: "0x96EcBbD6aB5fDC063E0fC426F2700290DeeAFE4E",
416
- uzh: "0x5e4673CD378F05cc3Ae25804539c91E711548741",
417
- mainnet: "0x0",
418
- celo: "0x2DaF913D423128258b2F378E320F9D9D3Be5eCf5"
419
- };
420
- var resultsVerifierGroth16 = {
421
- sepolia: "0x3ab37C40f7d0649f7a15BA3230f14AB29B51eDCC",
422
- uzh: "0x00c7F87731346F592197E49A90Ad6EC236Ad9985",
423
- mainnet: "0x0",
424
- celo: "0x808276962217AD1ED3af7D51bFc791903CAd9389"
425
- };
426
- var sequencerRegistry = {
427
- sepolia: "0x0",
428
- uzh: "0x0",
429
- mainnet: "0x0",
430
- celo: "0x0"
431
- };
432
- var addressesJson = {
433
- processRegistry: processRegistry,
434
- organizationRegistry: organizationRegistry,
435
- stateTransitionVerifierGroth16: stateTransitionVerifierGroth16,
436
- resultsVerifierGroth16: resultsVerifierGroth16,
437
- sequencerRegistry: sequencerRegistry
438
- };
439
-
440
- const deployedAddresses = addressesJson;
441
625
  var TxStatus = /* @__PURE__ */ ((TxStatus2) => {
442
626
  TxStatus2["Pending"] = "pending";
443
627
  TxStatus2["Completed"] = "completed";
@@ -456,19 +640,19 @@ class SmartContractService {
456
640
  * Sends a transaction and yields status events during its lifecycle.
457
641
  * This method handles the complete transaction flow from submission to completion,
458
642
  * including error handling and status updates.
459
- *
643
+ *
460
644
  * @template T - The type of the successful response data
461
645
  * @param txPromise - Promise resolving to the transaction response
462
646
  * @param responseHandler - Function to process the successful transaction result
463
647
  * @returns AsyncGenerator yielding transaction status events
464
- *
648
+ *
465
649
  * @example
466
650
  * ```typescript
467
651
  * const txStream = await this.sendTx(
468
652
  * contract.someMethod(),
469
653
  * async () => await contract.getUpdatedValue()
470
654
  * );
471
- *
655
+ *
472
656
  * for await (const event of txStream) {
473
657
  * switch (event.status) {
474
658
  * case TxStatus.Pending:
@@ -505,12 +689,12 @@ class SmartContractService {
505
689
  * Executes a transaction stream and returns the result or throws an error.
506
690
  * This is a convenience method that processes a transaction stream and either
507
691
  * returns the successful result or throws an appropriate error.
508
- *
692
+ *
509
693
  * @template T - The type of the successful response data
510
694
  * @param stream - AsyncGenerator of transaction status events
511
695
  * @returns Promise resolving to the successful response data
512
696
  * @throws Error if the transaction fails or reverts
513
- *
697
+ *
514
698
  * @example
515
699
  * ```typescript
516
700
  * try {
@@ -540,11 +724,11 @@ class SmartContractService {
540
724
  * Normalizes event listener arguments between different ethers.js versions.
541
725
  * This helper method ensures consistent event argument handling regardless of
542
726
  * whether the event payload follows ethers v5 or v6 format.
543
- *
727
+ *
544
728
  * @template Args - Tuple type representing the expected event arguments
545
729
  * @param callback - The event callback function to normalize
546
730
  * @returns Normalized event listener function
547
- *
731
+ *
548
732
  * @example
549
733
  * ```typescript
550
734
  * contract.on('Transfer', this.normalizeListener((from: string, to: string, amount: BigInt) => {
@@ -567,12 +751,12 @@ class SmartContractService {
567
751
  * Sets up an event listener with automatic fallback for RPCs that don't support eth_newFilter.
568
752
  * First attempts to use contract.on() which relies on eth_newFilter. If the RPC doesn't support
569
753
  * this method (error code -32601), automatically falls back to polling with queryFilter.
570
- *
754
+ *
571
755
  * @template Args - Tuple type representing the event arguments
572
756
  * @param contract - The contract instance to listen to
573
757
  * @param eventFilter - The event filter to listen for
574
758
  * @param callback - The callback function to invoke when the event occurs
575
- *
759
+ *
576
760
  * @example
577
761
  * ```typescript
578
762
  * this.setupEventListener(
@@ -598,13 +782,14 @@ class SmartContractService {
598
782
  };
599
783
  if ("send" in provider && typeof provider.send === "function") {
600
784
  try {
601
- await provider.send("eth_newFilter", [testFilter]);
785
+ const filterId = await provider.send("eth_newFilter", [testFilter]);
786
+ await provider.send("eth_getFilterChanges", [filterId]);
602
787
  contract.on(eventFilter, normalizedCallback);
603
788
  return;
604
789
  } catch (error) {
605
790
  if (this.isUnsupportedMethodError(error)) {
606
791
  console.warn(
607
- "RPC does not support eth_newFilter, falling back to polling for events. This may result in delayed event notifications."
792
+ "RPC does not fully support eth_newFilter/eth_getFilterChanges, falling back to polling for events. This may result in delayed event notifications."
608
793
  );
609
794
  this.setupPollingListener(contract, eventFilter, callback);
610
795
  return;
@@ -629,18 +814,23 @@ class SmartContractService {
629
814
  }
630
815
  }
631
816
  /**
632
- * Checks if an error indicates that the RPC method is unsupported (eth_newFilter).
633
- *
817
+ * Checks if an error indicates that the RPC method is unsupported or filter operations are not working.
818
+ * This includes:
819
+ * - Method not found (-32601): RPC doesn't support eth_newFilter
820
+ * - Filter not found (-32000): RPC doesn't properly maintain filters
821
+ *
634
822
  * @param error - The error to check
635
- * @returns true if the error indicates unsupported method
823
+ * @returns true if the error indicates unsupported or broken filter functionality
636
824
  */
637
825
  isUnsupportedMethodError(error) {
638
- return error?.code === -32601 || error?.error?.code === -32601 || error?.data?.code === -32601 || typeof error?.message === "string" && error.message.includes("unsupported method");
826
+ const isMethodNotFound = error?.code === -32601 || error?.error?.code === -32601 || error?.data?.code === -32601 || typeof error?.message === "string" && error.message.includes("unsupported method");
827
+ const isFilterNotFound = (error?.code === -32e3 || error?.error?.code === -32e3 || error?.code === "UNKNOWN_ERROR" && error?.error?.code === -32e3) && (error?.message?.includes("filter not found") || error?.error?.message?.includes("filter not found"));
828
+ return isMethodNotFound || isFilterNotFound;
639
829
  }
640
830
  /**
641
831
  * Sets up a polling-based event listener as fallback when eth_newFilter is not supported.
642
832
  * Periodically queries for new events and invokes the callback for each new event found.
643
- *
833
+ *
644
834
  * @template Args - Tuple type representing the event arguments
645
835
  * @param contract - The contract instance to poll
646
836
  * @param eventFilter - The event filter to poll for
@@ -692,7 +882,7 @@ class SmartContractService {
692
882
  }
693
883
  /**
694
884
  * Sets the polling interval for event listeners using the fallback mechanism.
695
- *
885
+ *
696
886
  * @param intervalMs - Polling interval in milliseconds
697
887
  */
698
888
  setEventPollingInterval(intervalMs) {
@@ -703,7 +893,7 @@ class SmartContractService {
703
893
  class ContractServiceError extends Error {
704
894
  /**
705
895
  * Creates a new ContractServiceError instance.
706
- *
896
+ *
707
897
  * @param message - The error message describing what went wrong
708
898
  * @param operation - The operation that was being performed when the error occurred
709
899
  */
@@ -856,7 +1046,7 @@ class ProcessRegistryService extends SmartContractService {
856
1046
  }
857
1047
  /**
858
1048
  * Sets the results for a voting process.
859
- *
1049
+ *
860
1050
  * @param processID - The ID of the process to set results for
861
1051
  * @param proof - Zero-knowledge proof validating the results
862
1052
  * @param input - Input data for the proof verification
@@ -864,11 +1054,7 @@ class ProcessRegistryService extends SmartContractService {
864
1054
  */
865
1055
  setProcessResults(processID, proof, input) {
866
1056
  return this.sendTx(
867
- this.contract.setProcessResults(
868
- processID,
869
- proof,
870
- input
871
- ).catch((e) => {
1057
+ this.contract.setProcessResults(processID, proof, input).catch((e) => {
872
1058
  throw new ProcessResultError(e.message, "setResults");
873
1059
  }),
874
1060
  async () => ({ success: true })
@@ -930,6 +1116,34 @@ class ProcessOrchestrationService {
930
1116
  this.organizationRegistry = organizationRegistry;
931
1117
  this.getCrypto = getCrypto;
932
1118
  this.signer = signer;
1119
+ this.censusOrchestrator = new CensusOrchestrator(apiService.census);
1120
+ }
1121
+ /**
1122
+ * Handles census - auto-publishes if needed and returns census config
1123
+ * @private
1124
+ */
1125
+ async handleCensus(census) {
1126
+ if ("isPublished" in census) {
1127
+ if (census instanceof PlainCensus || census instanceof WeightedCensus) {
1128
+ if (!census.isPublished) {
1129
+ const censusBaseURL = this.apiService.census?.["axios"]?.defaults?.baseURL;
1130
+ if (!censusBaseURL || censusBaseURL === "" || censusBaseURL === "undefined") {
1131
+ throw new Error(
1132
+ 'Census API URL is required to publish PlainCensus or WeightedCensus. Please provide "censusUrl" when initializing DavinciSDK, or use a pre-published census.'
1133
+ );
1134
+ }
1135
+ await this.censusOrchestrator.publish(census);
1136
+ }
1137
+ }
1138
+ const censusData = this.censusOrchestrator.getCensusData(census);
1139
+ return {
1140
+ type: censusData.type,
1141
+ root: censusData.root,
1142
+ size: censusData.size,
1143
+ uri: censusData.uri
1144
+ };
1145
+ }
1146
+ return census;
933
1147
  }
934
1148
  /**
935
1149
  * Gets user-friendly process information by transforming raw contract data
@@ -1005,10 +1219,10 @@ class ProcessOrchestrationService {
1005
1219
  /**
1006
1220
  * Creates a complete voting process and returns an async generator that yields transaction status events.
1007
1221
  * This method allows you to monitor the transaction progress in real-time.
1008
- *
1222
+ *
1009
1223
  * @param config - Process configuration
1010
1224
  * @returns AsyncGenerator yielding transaction status events with ProcessCreationResult
1011
- *
1225
+ *
1012
1226
  * @example
1013
1227
  * ```typescript
1014
1228
  * const stream = sdk.createProcessStream({
@@ -1019,7 +1233,7 @@ class ProcessOrchestrationService {
1019
1233
  * timing: { ... },
1020
1234
  * questions: [ ... ]
1021
1235
  * });
1022
- *
1236
+ *
1023
1237
  * for await (const event of stream) {
1024
1238
  * switch (event.status) {
1025
1239
  * case "pending":
@@ -1081,16 +1295,16 @@ class ProcessOrchestrationService {
1081
1295
  /**
1082
1296
  * Creates a complete voting process with minimal configuration.
1083
1297
  * This is the ultra-easy method for end users that handles all the complex orchestration internally.
1084
- *
1298
+ *
1085
1299
  * For real-time transaction status updates, use createProcessStream() instead.
1086
- *
1300
+ *
1087
1301
  * The method automatically:
1088
1302
  * - Gets encryption keys and initial state root from the sequencer
1089
1303
  * - Handles process creation signatures
1090
1304
  * - Coordinates between sequencer API and on-chain contract calls
1091
1305
  * - Creates and pushes metadata
1092
1306
  * - Submits the on-chain transaction
1093
- *
1307
+ *
1094
1308
  * @param config - Simplified process configuration
1095
1309
  * @returns Promise resolving to the process creation result
1096
1310
  */
@@ -1114,7 +1328,8 @@ class ProcessOrchestrationService {
1114
1328
  const { startTime, duration } = this.calculateTiming(config.timing);
1115
1329
  const signerAddress = await this.signer.getAddress();
1116
1330
  const processId = await this.processRegistry.getNextProcessId(signerAddress);
1117
- const censusRoot = config.census.root;
1331
+ const censusConfig = await this.handleCensus(config.census);
1332
+ const censusRoot = censusConfig.root;
1118
1333
  const ballotMode = config.ballot;
1119
1334
  const metadata = this.createMetadata(config);
1120
1335
  const metadataHash = await this.apiService.sequencer.pushMetadata(metadata);
@@ -1125,13 +1340,13 @@ class ProcessOrchestrationService {
1125
1340
  censusRoot,
1126
1341
  ballotMode,
1127
1342
  signature,
1128
- censusOrigin: config.census.type
1343
+ censusOrigin: censusConfig.type
1129
1344
  });
1130
1345
  const census = {
1131
- censusOrigin: config.census.type,
1132
- maxVotes: config.census.size.toString(),
1346
+ censusOrigin: censusConfig.type,
1347
+ maxVotes: censusConfig.size.toString(),
1133
1348
  censusRoot,
1134
- censusURI: config.census.uri
1349
+ censusURI: censusConfig.uri
1135
1350
  };
1136
1351
  return {
1137
1352
  processId,
@@ -1222,14 +1437,14 @@ class ProcessOrchestrationService {
1222
1437
  /**
1223
1438
  * Ends a voting process by setting its status to ENDED.
1224
1439
  * Returns an async generator that yields transaction status events.
1225
- *
1440
+ *
1226
1441
  * @param processId - The process ID to end
1227
1442
  * @returns AsyncGenerator yielding transaction status events
1228
- *
1443
+ *
1229
1444
  * @example
1230
1445
  * ```typescript
1231
1446
  * const stream = sdk.endProcessStream("0x1234567890abcdef...");
1232
- *
1447
+ *
1233
1448
  * for await (const event of stream) {
1234
1449
  * switch (event.status) {
1235
1450
  * case "pending":
@@ -1271,12 +1486,12 @@ class ProcessOrchestrationService {
1271
1486
  /**
1272
1487
  * Ends a voting process by setting its status to ENDED.
1273
1488
  * This is a simplified method that waits for transaction completion.
1274
- *
1489
+ *
1275
1490
  * For real-time transaction status updates, use endProcessStream() instead.
1276
- *
1491
+ *
1277
1492
  * @param processId - The process ID to end
1278
1493
  * @returns Promise resolving when the process is ended
1279
- *
1494
+ *
1280
1495
  * @example
1281
1496
  * ```typescript
1282
1497
  * await sdk.endProcess("0x1234567890abcdef...");
@@ -1298,14 +1513,14 @@ class ProcessOrchestrationService {
1298
1513
  /**
1299
1514
  * Pauses a voting process by setting its status to PAUSED.
1300
1515
  * Returns an async generator that yields transaction status events.
1301
- *
1516
+ *
1302
1517
  * @param processId - The process ID to pause
1303
1518
  * @returns AsyncGenerator yielding transaction status events
1304
- *
1519
+ *
1305
1520
  * @example
1306
1521
  * ```typescript
1307
1522
  * const stream = sdk.pauseProcessStream("0x1234567890abcdef...");
1308
- *
1523
+ *
1309
1524
  * for await (const event of stream) {
1310
1525
  * switch (event.status) {
1311
1526
  * case "pending":
@@ -1347,12 +1562,12 @@ class ProcessOrchestrationService {
1347
1562
  /**
1348
1563
  * Pauses a voting process by setting its status to PAUSED.
1349
1564
  * This is a simplified method that waits for transaction completion.
1350
- *
1565
+ *
1351
1566
  * For real-time transaction status updates, use pauseProcessStream() instead.
1352
- *
1567
+ *
1353
1568
  * @param processId - The process ID to pause
1354
1569
  * @returns Promise resolving when the process is paused
1355
- *
1570
+ *
1356
1571
  * @example
1357
1572
  * ```typescript
1358
1573
  * await sdk.pauseProcess("0x1234567890abcdef...");
@@ -1374,14 +1589,14 @@ class ProcessOrchestrationService {
1374
1589
  /**
1375
1590
  * Cancels a voting process by setting its status to CANCELED.
1376
1591
  * Returns an async generator that yields transaction status events.
1377
- *
1592
+ *
1378
1593
  * @param processId - The process ID to cancel
1379
1594
  * @returns AsyncGenerator yielding transaction status events
1380
- *
1595
+ *
1381
1596
  * @example
1382
1597
  * ```typescript
1383
1598
  * const stream = sdk.cancelProcessStream("0x1234567890abcdef...");
1384
- *
1599
+ *
1385
1600
  * for await (const event of stream) {
1386
1601
  * switch (event.status) {
1387
1602
  * case "pending":
@@ -1423,12 +1638,12 @@ class ProcessOrchestrationService {
1423
1638
  /**
1424
1639
  * Cancels a voting process by setting its status to CANCELED.
1425
1640
  * This is a simplified method that waits for transaction completion.
1426
- *
1641
+ *
1427
1642
  * For real-time transaction status updates, use cancelProcessStream() instead.
1428
- *
1643
+ *
1429
1644
  * @param processId - The process ID to cancel
1430
1645
  * @returns Promise resolving when the process is canceled
1431
- *
1646
+ *
1432
1647
  * @example
1433
1648
  * ```typescript
1434
1649
  * await sdk.cancelProcess("0x1234567890abcdef...");
@@ -1451,14 +1666,14 @@ class ProcessOrchestrationService {
1451
1666
  * Resumes a voting process by setting its status to READY.
1452
1667
  * This is typically used to resume a paused process.
1453
1668
  * Returns an async generator that yields transaction status events.
1454
- *
1669
+ *
1455
1670
  * @param processId - The process ID to resume
1456
1671
  * @returns AsyncGenerator yielding transaction status events
1457
- *
1672
+ *
1458
1673
  * @example
1459
1674
  * ```typescript
1460
1675
  * const stream = sdk.resumeProcessStream("0x1234567890abcdef...");
1461
- *
1676
+ *
1462
1677
  * for await (const event of stream) {
1463
1678
  * switch (event.status) {
1464
1679
  * case "pending":
@@ -1501,12 +1716,12 @@ class ProcessOrchestrationService {
1501
1716
  * Resumes a voting process by setting its status to READY.
1502
1717
  * This is typically used to resume a paused process.
1503
1718
  * This is a simplified method that waits for transaction completion.
1504
- *
1719
+ *
1505
1720
  * For real-time transaction status updates, use resumeProcessStream() instead.
1506
- *
1721
+ *
1507
1722
  * @param processId - The process ID to resume
1508
1723
  * @returns Promise resolving when the process is resumed
1509
- *
1724
+ *
1510
1725
  * @example
1511
1726
  * ```typescript
1512
1727
  * await sdk.resumeProcess("0x1234567890abcdef...");
@@ -1594,11 +1809,7 @@ class CircomProof {
1594
1809
  }
1595
1810
  this.zkeyCache.set(zkeyUrl, zkeyBin);
1596
1811
  }
1597
- const { proof, publicSignals } = await groth16.fullProve(
1598
- inputs,
1599
- wasmBin,
1600
- zkeyBin
1601
- );
1812
+ const { proof, publicSignals } = await groth16.fullProve(inputs, wasmBin, zkeyBin);
1602
1813
  return {
1603
1814
  proof,
1604
1815
  publicSignals
@@ -1633,11 +1844,13 @@ var VoteStatus = /* @__PURE__ */ ((VoteStatus2) => {
1633
1844
  })(VoteStatus || {});
1634
1845
 
1635
1846
  class VoteOrchestrationService {
1636
- constructor(apiService, getCrypto, signer, censusProviders = {}) {
1847
+ constructor(apiService, getCrypto, signer, censusProviders = {}, config = {}) {
1637
1848
  this.apiService = apiService;
1638
1849
  this.getCrypto = getCrypto;
1639
1850
  this.signer = signer;
1640
1851
  this.censusProviders = censusProviders;
1852
+ this.verifyCircuitFiles = config.verifyCircuitFiles ?? true;
1853
+ this.verifyProof = config.verifyProof ?? true;
1641
1854
  }
1642
1855
  /**
1643
1856
  * Submit a vote with simplified configuration
@@ -1646,7 +1859,7 @@ class VoteOrchestrationService {
1646
1859
  * - Gets census proof (Merkle or CSP)
1647
1860
  * - Generates cryptographic proofs
1648
1861
  * - Signs and submits the vote
1649
- *
1862
+ *
1650
1863
  * @param config - Simplified vote configuration
1651
1864
  * @returns Promise resolving to vote submission result
1652
1865
  */
@@ -1694,7 +1907,7 @@ class VoteOrchestrationService {
1694
1907
  }
1695
1908
  /**
1696
1909
  * Get the status of a submitted vote
1697
- *
1910
+ *
1698
1911
  * @param processId - The process ID
1699
1912
  * @param voteId - The vote ID
1700
1913
  * @returns Promise resolving to vote status information
@@ -1709,7 +1922,7 @@ class VoteOrchestrationService {
1709
1922
  }
1710
1923
  /**
1711
1924
  * Check if an address has voted in a process
1712
- *
1925
+ *
1713
1926
  * @param processId - The process ID
1714
1927
  * @param address - The voter's address
1715
1928
  * @returns Promise resolving to boolean indicating if the address has voted
@@ -1720,19 +1933,19 @@ class VoteOrchestrationService {
1720
1933
  /**
1721
1934
  * Watch vote status changes in real-time using an async generator.
1722
1935
  * Yields each status change as it happens, allowing for reactive UI updates.
1723
- *
1936
+ *
1724
1937
  * @param processId - The process ID
1725
1938
  * @param voteId - The vote ID
1726
1939
  * @param options - Optional configuration
1727
1940
  * @returns AsyncGenerator yielding vote status updates
1728
- *
1941
+ *
1729
1942
  * @example
1730
1943
  * ```typescript
1731
1944
  * const vote = await sdk.submitVote({ processId, choices: [1] });
1732
- *
1945
+ *
1733
1946
  * for await (const statusInfo of sdk.watchVoteStatus(vote.processId, vote.voteId)) {
1734
1947
  * console.log(`Vote status: ${statusInfo.status}`);
1735
- *
1948
+ *
1736
1949
  * switch (statusInfo.status) {
1737
1950
  * case VoteStatus.Pending:
1738
1951
  * console.log("⏳ Processing...");
@@ -1769,7 +1982,7 @@ class VoteOrchestrationService {
1769
1982
  /**
1770
1983
  * Wait for a vote to reach a specific status.
1771
1984
  * This is a simpler alternative to watchVoteStatus() that returns only the final status.
1772
- *
1985
+ *
1773
1986
  * @param processId - The process ID
1774
1987
  * @param voteId - The vote ID
1775
1988
  * @param targetStatus - The target status to wait for (default: "settled")
@@ -1872,12 +2085,20 @@ class VoteOrchestrationService {
1872
2085
  const circomProof = new CircomProof({
1873
2086
  wasmUrl: info.circuitUrl,
1874
2087
  zkeyUrl: info.provingKeyUrl,
1875
- vkeyUrl: info.verificationKeyUrl
2088
+ vkeyUrl: info.verificationKeyUrl,
2089
+ // Only pass hashes if verifyCircuitFiles is enabled
2090
+ ...this.verifyCircuitFiles && {
2091
+ wasmHash: info.circuitHash,
2092
+ zkeyHash: info.provingKeyHash,
2093
+ vkeyHash: info.verificationKeyHash
2094
+ }
1876
2095
  });
1877
2096
  const { proof, publicSignals } = await circomProof.generate(circomInputs);
1878
- const isValid = await circomProof.verify(proof, publicSignals);
1879
- if (!isValid) {
1880
- throw new Error("Generated proof is invalid");
2097
+ if (this.verifyProof) {
2098
+ const isValid = await circomProof.verify(proof, publicSignals);
2099
+ if (!isValid) {
2100
+ throw new Error("Generated proof is invalid");
2101
+ }
1881
2102
  }
1882
2103
  return { proof, publicSignals };
1883
2104
  }
@@ -1915,10 +2136,7 @@ class VoteOrchestrationService {
1915
2136
  class OrganizationRegistryService extends SmartContractService {
1916
2137
  constructor(contractAddress, runner) {
1917
2138
  super();
1918
- this.contract = OrganizationRegistry__factory.connect(
1919
- contractAddress,
1920
- runner
1921
- );
2139
+ this.contract = OrganizationRegistry__factory.connect(contractAddress, runner);
1922
2140
  }
1923
2141
  // ─── READ OPERATIONS ───────────────────────────────────────────────────────
1924
2142
  async getOrganization(id) {
@@ -2194,25 +2412,23 @@ class DavinciCrypto {
2194
2412
  class DavinciSDK {
2195
2413
  constructor(config) {
2196
2414
  this.initialized = false;
2197
- const resolvedConfig = resolveConfiguration({
2198
- environment: config.environment,
2199
- customUrls: {
2200
- sequencer: config.sequencerUrl,
2201
- census: config.censusUrl
2202
- },
2203
- customChain: config.chain
2204
- });
2415
+ const hasCustomAddresses = !!config.addresses && Object.keys(config.addresses).length > 0;
2205
2416
  this.config = {
2206
2417
  signer: config.signer,
2207
- sequencerUrl: config.sequencerUrl ?? resolvedConfig.sequencer,
2208
- censusUrl: config.censusUrl ?? resolvedConfig.census,
2209
- chain: config.chain ?? resolvedConfig.chain,
2210
- contractAddresses: config.contractAddresses || {},
2211
- useSequencerAddresses: config.useSequencerAddresses || false
2418
+ sequencerUrl: config.sequencerUrl,
2419
+ censusUrl: config.censusUrl,
2420
+ customAddresses: config.addresses || {},
2421
+ fetchAddressesFromSequencer: !hasCustomAddresses,
2422
+ // Automatic: fetch if no custom addresses
2423
+ verifyCircuitFiles: config.verifyCircuitFiles ?? true,
2424
+ // Default to true for security
2425
+ verifyProof: config.verifyProof ?? true
2426
+ // Default to true for security
2212
2427
  };
2213
2428
  this.apiService = new VocdoniApiService({
2214
2429
  sequencerURL: this.config.sequencerUrl,
2215
- censusURL: this.config.censusUrl
2430
+ censusURL: this.config.censusUrl || ""
2431
+ // Use empty string if not provided
2216
2432
  });
2217
2433
  this.censusProviders = config.censusProviders || {};
2218
2434
  }
@@ -2222,9 +2438,10 @@ class DavinciSDK {
2222
2438
  */
2223
2439
  async init() {
2224
2440
  if (this.initialized) return;
2225
- if (this.config.useSequencerAddresses) {
2226
- await this.updateContractAddressesFromSequencer();
2441
+ if (this.config.fetchAddressesFromSequencer) {
2442
+ await this.fetchContractAddressesFromSequencer();
2227
2443
  }
2444
+ if (!this.config.censusUrl) ;
2228
2445
  this.initialized = true;
2229
2446
  }
2230
2447
  /**
@@ -2236,28 +2453,34 @@ class DavinciSDK {
2236
2453
  /**
2237
2454
  * Get the process registry service for process management.
2238
2455
  * Requires a signer with a provider for blockchain interactions.
2239
- *
2456
+ *
2240
2457
  * @throws Error if signer does not have a provider
2241
2458
  */
2242
2459
  get processes() {
2243
2460
  this.ensureProvider();
2244
2461
  if (!this._processRegistry) {
2245
2462
  const processRegistryAddress = this.resolveContractAddress("processRegistry");
2246
- this._processRegistry = new ProcessRegistryService(processRegistryAddress, this.config.signer);
2463
+ this._processRegistry = new ProcessRegistryService(
2464
+ processRegistryAddress,
2465
+ this.config.signer
2466
+ );
2247
2467
  }
2248
2468
  return this._processRegistry;
2249
2469
  }
2250
2470
  /**
2251
2471
  * Get the organization registry service for organization management.
2252
2472
  * Requires a signer with a provider for blockchain interactions.
2253
- *
2473
+ *
2254
2474
  * @throws Error if signer does not have a provider
2255
2475
  */
2256
2476
  get organizations() {
2257
2477
  this.ensureProvider();
2258
2478
  if (!this._organizationRegistry) {
2259
2479
  const organizationRegistryAddress = this.resolveContractAddress("organizationRegistry");
2260
- this._organizationRegistry = new OrganizationRegistryService(organizationRegistryAddress, this.config.signer);
2480
+ this._organizationRegistry = new OrganizationRegistryService(
2481
+ organizationRegistryAddress,
2482
+ this.config.signer
2483
+ );
2261
2484
  }
2262
2485
  return this._organizationRegistry;
2263
2486
  }
@@ -2278,7 +2501,7 @@ class DavinciSDK {
2278
2501
  /**
2279
2502
  * Get the process orchestration service for simplified process creation.
2280
2503
  * Requires a signer with a provider for blockchain interactions.
2281
- *
2504
+ *
2282
2505
  * @throws Error if signer does not have a provider
2283
2506
  */
2284
2507
  get processOrchestrator() {
@@ -2303,7 +2526,11 @@ class DavinciSDK {
2303
2526
  this.apiService,
2304
2527
  () => this.getCrypto(),
2305
2528
  this.config.signer,
2306
- this.censusProviders
2529
+ this.censusProviders,
2530
+ {
2531
+ verifyCircuitFiles: this.config.verifyCircuitFiles,
2532
+ verifyProof: this.config.verifyProof
2533
+ }
2307
2534
  );
2308
2535
  }
2309
2536
  return this._voteOrchestrator;
@@ -2312,24 +2539,24 @@ class DavinciSDK {
2312
2539
  * Gets user-friendly process information from the blockchain.
2313
2540
  * This method fetches raw contract data and transforms it into a user-friendly format
2314
2541
  * that matches the ProcessConfig interface used for creation, plus additional runtime data.
2315
- *
2542
+ *
2316
2543
  * Requires a signer with a provider for blockchain interactions.
2317
- *
2544
+ *
2318
2545
  * @param processId - The process ID to fetch
2319
2546
  * @returns Promise resolving to user-friendly process information
2320
2547
  * @throws Error if signer does not have a provider
2321
- *
2548
+ *
2322
2549
  * @example
2323
2550
  * ```typescript
2324
2551
  * const processInfo = await sdk.getProcess("0x1234567890abcdef...");
2325
- *
2552
+ *
2326
2553
  * // Access the same fields as ProcessConfig
2327
2554
  * console.log("Title:", processInfo.title);
2328
2555
  * console.log("Description:", processInfo.description);
2329
2556
  * console.log("Questions:", processInfo.questions);
2330
2557
  * console.log("Census size:", processInfo.census.size);
2331
2558
  * console.log("Ballot config:", processInfo.ballot);
2332
- *
2559
+ *
2333
2560
  * // Plus additional runtime information
2334
2561
  * console.log("Status:", processInfo.status);
2335
2562
  * console.log("Creator:", processInfo.creator);
@@ -2337,7 +2564,7 @@ class DavinciSDK {
2337
2564
  * console.log("End date:", processInfo.endDate);
2338
2565
  * console.log("Duration:", processInfo.duration, "seconds");
2339
2566
  * console.log("Time remaining:", processInfo.timeRemaining, "seconds");
2340
- *
2567
+ *
2341
2568
  * // Access raw contract data if needed
2342
2569
  * console.log("Raw data:", processInfo.raw);
2343
2570
  * ```
@@ -2353,13 +2580,13 @@ class DavinciSDK {
2353
2580
  * Creates a complete voting process and returns an async generator that yields transaction status events.
2354
2581
  * This method allows you to monitor the transaction progress in real-time, including pending, completed,
2355
2582
  * failed, and reverted states.
2356
- *
2583
+ *
2357
2584
  * Requires a signer with a provider for blockchain interactions.
2358
- *
2585
+ *
2359
2586
  * @param config - Simplified process configuration
2360
2587
  * @returns AsyncGenerator yielding transaction status events
2361
2588
  * @throws Error if signer does not have a provider
2362
- *
2589
+ *
2363
2590
  * @example
2364
2591
  * ```typescript
2365
2592
  * const stream = sdk.createProcessStream({
@@ -2395,7 +2622,7 @@ class DavinciSDK {
2395
2622
  * }
2396
2623
  * ]
2397
2624
  * });
2398
- *
2625
+ *
2399
2626
  * // Monitor transaction progress
2400
2627
  * for await (const event of stream) {
2401
2628
  * switch (event.status) {
@@ -2430,22 +2657,22 @@ class DavinciSDK {
2430
2657
  /**
2431
2658
  * Creates a complete voting process with minimal configuration.
2432
2659
  * This is the ultra-easy method for end users that handles all the complex orchestration internally.
2433
- *
2660
+ *
2434
2661
  * For real-time transaction status updates, use createProcessStream() instead.
2435
- *
2662
+ *
2436
2663
  * Requires a signer with a provider for blockchain interactions.
2437
- *
2664
+ *
2438
2665
  * The method automatically:
2439
2666
  * - Gets encryption keys and initial state root from the sequencer
2440
2667
  * - Handles process creation signatures
2441
2668
  * - Coordinates between sequencer API and on-chain contract calls
2442
2669
  * - Creates and pushes metadata
2443
2670
  * - Submits the on-chain transaction
2444
- *
2671
+ *
2445
2672
  * @param config - Simplified process configuration
2446
2673
  * @returns Promise resolving to the process creation result
2447
2674
  * @throws Error if signer does not have a provider
2448
- *
2675
+ *
2449
2676
  * @example
2450
2677
  * ```typescript
2451
2678
  * // Option 1: Using duration (traditional approach)
@@ -2482,7 +2709,7 @@ class DavinciSDK {
2482
2709
  * }
2483
2710
  * ]
2484
2711
  * });
2485
- *
2712
+ *
2486
2713
  * // Option 2: Using start and end dates
2487
2714
  * const result2 = await sdk.createProcess({
2488
2715
  * title: "Weekend Vote",
@@ -2503,18 +2730,20 @@ class DavinciSDK {
2503
2730
  /**
2504
2731
  * Submit a vote with simplified configuration.
2505
2732
  * This is the ultra-easy method for end users that handles all the complex voting workflow internally.
2506
- *
2733
+ *
2507
2734
  * Does NOT require a provider - can be used with a bare Wallet for signing only.
2508
- *
2735
+ * IMPORTANT: Requires censusUrl to be configured in the SDK for fetching census proofs (unless using custom census providers).
2736
+ *
2509
2737
  * The method automatically:
2510
2738
  * - Fetches process information and validates voting is allowed
2511
2739
  * - Gets census proof (Merkle tree based)
2512
2740
  * - Generates cryptographic proofs and encrypts the vote
2513
2741
  * - Signs and submits the vote to the sequencer
2514
- *
2742
+ *
2515
2743
  * @param config - Simplified vote configuration
2516
2744
  * @returns Promise resolving to vote submission result
2517
- *
2745
+ * @throws Error if censusUrl is not configured (unless using custom census providers)
2746
+ *
2518
2747
  * @example
2519
2748
  * ```typescript
2520
2749
  * // Submit a vote with voter's private key
@@ -2523,14 +2752,14 @@ class DavinciSDK {
2523
2752
  * choices: [1, 0], // Vote for option 1 in question 1, option 0 in question 2
2524
2753
  * voterKey: "0x1234567890abcdef..." // Voter's private key
2525
2754
  * });
2526
- *
2755
+ *
2527
2756
  * console.log("Vote ID:", voteResult.voteId);
2528
2757
  * console.log("Status:", voteResult.status);
2529
- *
2758
+ *
2530
2759
  * // Submit a vote with a Wallet instance
2531
2760
  * import { Wallet } from "ethers";
2532
2761
  * const voterWallet = new Wallet("0x...");
2533
- *
2762
+ *
2534
2763
  * const voteResult2 = await sdk.submitVote({
2535
2764
  * processId: "0x1234567890abcdef...",
2536
2765
  * choices: [2], // Single question vote
@@ -2542,17 +2771,22 @@ class DavinciSDK {
2542
2771
  if (!this.initialized) {
2543
2772
  throw new Error("SDK must be initialized before submitting votes. Call sdk.init() first.");
2544
2773
  }
2774
+ if (!this.config.censusUrl && !this.censusProviders.merkle && !this.censusProviders.csp) {
2775
+ throw new Error(
2776
+ "Census URL is required for voting. Provide censusUrl in the SDK constructor config, or use custom census providers."
2777
+ );
2778
+ }
2545
2779
  return this.voteOrchestrator.submitVote(config);
2546
2780
  }
2547
2781
  /**
2548
2782
  * Get the status of a submitted vote.
2549
- *
2783
+ *
2550
2784
  * Does NOT require a provider - uses API calls only.
2551
- *
2785
+ *
2552
2786
  * @param processId - The process ID
2553
2787
  * @param voteId - The vote ID returned from submitVote()
2554
2788
  * @returns Promise resolving to vote status information
2555
- *
2789
+ *
2556
2790
  * @example
2557
2791
  * ```typescript
2558
2792
  * const statusInfo = await sdk.getVoteStatus(processId, voteId);
@@ -2568,13 +2802,13 @@ class DavinciSDK {
2568
2802
  }
2569
2803
  /**
2570
2804
  * Check if an address has voted in a process.
2571
- *
2805
+ *
2572
2806
  * Does NOT require a provider - uses API calls only.
2573
- *
2807
+ *
2574
2808
  * @param processId - The process ID
2575
2809
  * @param address - The voter's address
2576
2810
  * @returns Promise resolving to boolean indicating if the address has voted
2577
- *
2811
+ *
2578
2812
  * @example
2579
2813
  * ```typescript
2580
2814
  * const hasVoted = await sdk.hasAddressVoted(processId, "0x1234567890abcdef...");
@@ -2585,22 +2819,24 @@ class DavinciSDK {
2585
2819
  */
2586
2820
  async hasAddressVoted(processId, address) {
2587
2821
  if (!this.initialized) {
2588
- throw new Error("SDK must be initialized before checking vote status. Call sdk.init() first.");
2822
+ throw new Error(
2823
+ "SDK must be initialized before checking vote status. Call sdk.init() first."
2824
+ );
2589
2825
  }
2590
2826
  return this.voteOrchestrator.hasAddressVoted(processId, address);
2591
2827
  }
2592
2828
  /**
2593
2829
  * Watch vote status changes in real-time using an async generator.
2594
- * This method yields each status change as it happens, perfect for showing
2830
+ * This method yields each status change as it happens, perfect for showing
2595
2831
  * progress indicators in UI applications.
2596
- *
2832
+ *
2597
2833
  * Does NOT require a provider - uses API calls only.
2598
- *
2834
+ *
2599
2835
  * @param processId - The process ID
2600
2836
  * @param voteId - The vote ID
2601
2837
  * @param options - Optional configuration
2602
2838
  * @returns AsyncGenerator yielding vote status updates
2603
- *
2839
+ *
2604
2840
  * @example
2605
2841
  * ```typescript
2606
2842
  * // Submit vote
@@ -2608,11 +2844,11 @@ class DavinciSDK {
2608
2844
  * processId: "0x1234567890abcdef...",
2609
2845
  * choices: [1]
2610
2846
  * });
2611
- *
2847
+ *
2612
2848
  * // Watch status changes in real-time
2613
2849
  * for await (const statusInfo of sdk.watchVoteStatus(voteResult.processId, voteResult.voteId)) {
2614
2850
  * console.log(`Vote status: ${statusInfo.status}`);
2615
- *
2851
+ *
2616
2852
  * switch (statusInfo.status) {
2617
2853
  * case VoteStatus.Pending:
2618
2854
  * console.log("⏳ Processing...");
@@ -2632,7 +2868,9 @@ class DavinciSDK {
2632
2868
  */
2633
2869
  watchVoteStatus(processId, voteId, options) {
2634
2870
  if (!this.initialized) {
2635
- throw new Error("SDK must be initialized before watching vote status. Call sdk.init() first.");
2871
+ throw new Error(
2872
+ "SDK must be initialized before watching vote status. Call sdk.init() first."
2873
+ );
2636
2874
  }
2637
2875
  return this.voteOrchestrator.watchVoteStatus(processId, voteId, options);
2638
2876
  }
@@ -2640,16 +2878,16 @@ class DavinciSDK {
2640
2878
  * Wait for a vote to reach a specific status.
2641
2879
  * This is a simpler alternative to watchVoteStatus() that returns only the final status.
2642
2880
  * Useful for waiting for vote confirmation and processing without needing to handle each intermediate status.
2643
- *
2881
+ *
2644
2882
  * Does NOT require a provider - uses API calls only.
2645
- *
2883
+ *
2646
2884
  * @param processId - The process ID
2647
2885
  * @param voteId - The vote ID
2648
2886
  * @param targetStatus - The target status to wait for (default: "settled")
2649
2887
  * @param timeoutMs - Maximum time to wait in milliseconds (default: 300000 = 5 minutes)
2650
2888
  * @param pollIntervalMs - Polling interval in milliseconds (default: 5000 = 5 seconds)
2651
2889
  * @returns Promise resolving to final vote status
2652
- *
2890
+ *
2653
2891
  * @example
2654
2892
  * ```typescript
2655
2893
  * // Submit vote and wait for it to be settled
@@ -2657,7 +2895,7 @@ class DavinciSDK {
2657
2895
  * processId: "0x1234567890abcdef...",
2658
2896
  * choices: [1]
2659
2897
  * });
2660
- *
2898
+ *
2661
2899
  * // Wait for the vote to be fully processed
2662
2900
  * const finalStatus = await sdk.waitForVoteStatus(
2663
2901
  * voteResult.processId,
@@ -2666,31 +2904,39 @@ class DavinciSDK {
2666
2904
  * 300000, // 5 minute timeout
2667
2905
  * 5000 // Check every 5 seconds
2668
2906
  * );
2669
- *
2907
+ *
2670
2908
  * console.log("Vote final status:", finalStatus.status);
2671
2909
  * ```
2672
2910
  */
2673
2911
  async waitForVoteStatus(processId, voteId, targetStatus = VoteStatus.Settled, timeoutMs = 3e5, pollIntervalMs = 5e3) {
2674
2912
  if (!this.initialized) {
2675
- throw new Error("SDK must be initialized before waiting for vote status. Call sdk.init() first.");
2913
+ throw new Error(
2914
+ "SDK must be initialized before waiting for vote status. Call sdk.init() first."
2915
+ );
2676
2916
  }
2677
- return this.voteOrchestrator.waitForVoteStatus(processId, voteId, targetStatus, timeoutMs, pollIntervalMs);
2917
+ return this.voteOrchestrator.waitForVoteStatus(
2918
+ processId,
2919
+ voteId,
2920
+ targetStatus,
2921
+ timeoutMs,
2922
+ pollIntervalMs
2923
+ );
2678
2924
  }
2679
2925
  /**
2680
- * Ends a voting process by setting its status to ENDED and returns an async generator
2681
- * that yields transaction status events. This method allows you to monitor the
2926
+ * Ends a voting process by setting its status to ENDED and returns an async generator
2927
+ * that yields transaction status events. This method allows you to monitor the
2682
2928
  * transaction progress in real-time.
2683
- *
2929
+ *
2684
2930
  * Requires a signer with a provider for blockchain interactions.
2685
- *
2931
+ *
2686
2932
  * @param processId - The process ID to end
2687
2933
  * @returns AsyncGenerator yielding transaction status events
2688
2934
  * @throws Error if signer does not have a provider
2689
- *
2935
+ *
2690
2936
  * @example
2691
2937
  * ```typescript
2692
2938
  * const stream = sdk.endProcessStream("0x1234567890abcdef...");
2693
- *
2939
+ *
2694
2940
  * for await (const event of stream) {
2695
2941
  * switch (event.status) {
2696
2942
  * case TxStatus.Pending:
@@ -2719,15 +2965,15 @@ class DavinciSDK {
2719
2965
  /**
2720
2966
  * Ends a voting process by setting its status to ENDED.
2721
2967
  * This is the simplified method that waits for transaction completion.
2722
- *
2968
+ *
2723
2969
  * For real-time transaction status updates, use endProcessStream() instead.
2724
- *
2970
+ *
2725
2971
  * Requires a signer with a provider for blockchain interactions.
2726
- *
2972
+ *
2727
2973
  * @param processId - The process ID to end
2728
2974
  * @returns Promise resolving when the process is ended
2729
2975
  * @throws Error if signer does not have a provider
2730
- *
2976
+ *
2731
2977
  * @example
2732
2978
  * ```typescript
2733
2979
  * await sdk.endProcess("0x1234567890abcdef...");
@@ -2742,20 +2988,20 @@ class DavinciSDK {
2742
2988
  return this.processOrchestrator.endProcess(processId);
2743
2989
  }
2744
2990
  /**
2745
- * Pauses a voting process by setting its status to PAUSED and returns an async generator
2746
- * that yields transaction status events. This method allows you to monitor the
2991
+ * Pauses a voting process by setting its status to PAUSED and returns an async generator
2992
+ * that yields transaction status events. This method allows you to monitor the
2747
2993
  * transaction progress in real-time.
2748
- *
2994
+ *
2749
2995
  * Requires a signer with a provider for blockchain interactions.
2750
- *
2996
+ *
2751
2997
  * @param processId - The process ID to pause
2752
2998
  * @returns AsyncGenerator yielding transaction status events
2753
2999
  * @throws Error if signer does not have a provider
2754
- *
3000
+ *
2755
3001
  * @example
2756
3002
  * ```typescript
2757
3003
  * const stream = sdk.pauseProcessStream("0x1234567890abcdef...");
2758
- *
3004
+ *
2759
3005
  * for await (const event of stream) {
2760
3006
  * switch (event.status) {
2761
3007
  * case TxStatus.Pending:
@@ -2784,15 +3030,15 @@ class DavinciSDK {
2784
3030
  /**
2785
3031
  * Pauses a voting process by setting its status to PAUSED.
2786
3032
  * This is the simplified method that waits for transaction completion.
2787
- *
3033
+ *
2788
3034
  * For real-time transaction status updates, use pauseProcessStream() instead.
2789
- *
3035
+ *
2790
3036
  * Requires a signer with a provider for blockchain interactions.
2791
- *
3037
+ *
2792
3038
  * @param processId - The process ID to pause
2793
3039
  * @returns Promise resolving when the process is paused
2794
3040
  * @throws Error if signer does not have a provider
2795
- *
3041
+ *
2796
3042
  * @example
2797
3043
  * ```typescript
2798
3044
  * await sdk.pauseProcess("0x1234567890abcdef...");
@@ -2807,20 +3053,20 @@ class DavinciSDK {
2807
3053
  return this.processOrchestrator.pauseProcess(processId);
2808
3054
  }
2809
3055
  /**
2810
- * Cancels a voting process by setting its status to CANCELED and returns an async generator
2811
- * that yields transaction status events. This method allows you to monitor the
3056
+ * Cancels a voting process by setting its status to CANCELED and returns an async generator
3057
+ * that yields transaction status events. This method allows you to monitor the
2812
3058
  * transaction progress in real-time.
2813
- *
3059
+ *
2814
3060
  * Requires a signer with a provider for blockchain interactions.
2815
- *
3061
+ *
2816
3062
  * @param processId - The process ID to cancel
2817
3063
  * @returns AsyncGenerator yielding transaction status events
2818
3064
  * @throws Error if signer does not have a provider
2819
- *
3065
+ *
2820
3066
  * @example
2821
3067
  * ```typescript
2822
3068
  * const stream = sdk.cancelProcessStream("0x1234567890abcdef...");
2823
- *
3069
+ *
2824
3070
  * for await (const event of stream) {
2825
3071
  * switch (event.status) {
2826
3072
  * case TxStatus.Pending:
@@ -2849,15 +3095,15 @@ class DavinciSDK {
2849
3095
  /**
2850
3096
  * Cancels a voting process by setting its status to CANCELED.
2851
3097
  * This is the simplified method that waits for transaction completion.
2852
- *
3098
+ *
2853
3099
  * For real-time transaction status updates, use cancelProcessStream() instead.
2854
- *
3100
+ *
2855
3101
  * Requires a signer with a provider for blockchain interactions.
2856
- *
3102
+ *
2857
3103
  * @param processId - The process ID to cancel
2858
3104
  * @returns Promise resolving when the process is canceled
2859
3105
  * @throws Error if signer does not have a provider
2860
- *
3106
+ *
2861
3107
  * @example
2862
3108
  * ```typescript
2863
3109
  * await sdk.cancelProcess("0x1234567890abcdef...");
@@ -2872,19 +3118,19 @@ class DavinciSDK {
2872
3118
  return this.processOrchestrator.cancelProcess(processId);
2873
3119
  }
2874
3120
  /**
2875
- * Resumes a voting process by setting its status to READY and returns an async generator
3121
+ * Resumes a voting process by setting its status to READY and returns an async generator
2876
3122
  * that yields transaction status events. This is typically used to resume a paused process.
2877
- *
3123
+ *
2878
3124
  * Requires a signer with a provider for blockchain interactions.
2879
- *
3125
+ *
2880
3126
  * @param processId - The process ID to resume
2881
3127
  * @returns AsyncGenerator yielding transaction status events
2882
3128
  * @throws Error if signer does not have a provider
2883
- *
3129
+ *
2884
3130
  * @example
2885
3131
  * ```typescript
2886
3132
  * const stream = sdk.resumeProcessStream("0x1234567890abcdef...");
2887
- *
3133
+ *
2888
3134
  * for await (const event of stream) {
2889
3135
  * switch (event.status) {
2890
3136
  * case TxStatus.Pending:
@@ -2914,15 +3160,15 @@ class DavinciSDK {
2914
3160
  * Resumes a voting process by setting its status to READY.
2915
3161
  * This is typically used to resume a paused process.
2916
3162
  * This is the simplified method that waits for transaction completion.
2917
- *
3163
+ *
2918
3164
  * For real-time transaction status updates, use resumeProcessStream() instead.
2919
- *
3165
+ *
2920
3166
  * Requires a signer with a provider for blockchain interactions.
2921
- *
3167
+ *
2922
3168
  * @param processId - The process ID to resume
2923
3169
  * @returns Promise resolving when the process is resumed
2924
3170
  * @throws Error if signer does not have a provider
2925
- *
3171
+ *
2926
3172
  * @example
2927
3173
  * ```typescript
2928
3174
  * await sdk.resumeProcess("0x1234567890abcdef...");
@@ -2938,64 +3184,50 @@ class DavinciSDK {
2938
3184
  }
2939
3185
  /**
2940
3186
  * Resolve contract address based on configuration priority:
2941
- * 1. If useSequencerAddresses is true: addresses from sequencer (highest priority)
2942
- * 2. Custom addresses from config (if provided by user)
2943
- * 3. Default deployed addresses from npm package
3187
+ * 1. Custom addresses from user config (if provided)
3188
+ * 2. Addresses from sequencer (fetched during init if no custom addresses provided)
2944
3189
  */
2945
3190
  resolveContractAddress(contractName) {
2946
- if (this.config.useSequencerAddresses) {
2947
- return this.getDefaultContractAddress(contractName);
2948
- }
2949
- const customAddress = this.config.contractAddresses[contractName];
3191
+ const customAddress = this.config.customAddresses[contractName];
2950
3192
  if (customAddress) {
2951
3193
  return customAddress;
2952
3194
  }
2953
- return this.getDefaultContractAddress(contractName);
2954
- }
2955
- /**
2956
- * Get default contract address from deployed addresses
2957
- */
2958
- getDefaultContractAddress(contractName) {
2959
- const chain = this.config.chain;
2960
- switch (contractName) {
2961
- case "processRegistry":
2962
- return deployedAddresses.processRegistry[chain];
2963
- case "organizationRegistry":
2964
- return deployedAddresses.organizationRegistry[chain];
2965
- case "stateTransitionVerifier":
2966
- return deployedAddresses.stateTransitionVerifierGroth16[chain];
2967
- case "resultsVerifier":
2968
- return deployedAddresses.resultsVerifierGroth16[chain];
2969
- case "sequencerRegistry":
2970
- return deployedAddresses.sequencerRegistry[chain];
2971
- default:
2972
- throw new Error(`Unknown contract: ${contractName}`);
3195
+ if (!this.config.customAddresses[contractName]) {
3196
+ throw new Error(
3197
+ `Contract address for '${contractName}' not found. Make sure SDK is initialized with sdk.init() or provide custom addresses in config.`
3198
+ );
2973
3199
  }
3200
+ return this.config.customAddresses[contractName];
2974
3201
  }
2975
3202
  /**
2976
- * Update contract addresses from sequencer info if useSequencerAddresses is enabled
2977
- * Sequencer addresses have priority over user-provided addresses
3203
+ * Fetch contract addresses from sequencer info
3204
+ * This is called during init() if custom addresses are not provided
2978
3205
  */
2979
- async updateContractAddressesFromSequencer() {
3206
+ async fetchContractAddressesFromSequencer() {
2980
3207
  try {
2981
3208
  const info = await this.apiService.sequencer.getInfo();
2982
3209
  const contracts = info.contracts;
2983
3210
  if (contracts.process) {
3211
+ this.config.customAddresses.processRegistry = contracts.process;
2984
3212
  this._processRegistry = new ProcessRegistryService(contracts.process, this.config.signer);
2985
- this.config.contractAddresses.processRegistry = contracts.process;
2986
3213
  }
2987
3214
  if (contracts.organization) {
2988
- this._organizationRegistry = new OrganizationRegistryService(contracts.organization, this.config.signer);
2989
- this.config.contractAddresses.organizationRegistry = contracts.organization;
3215
+ this.config.customAddresses.organizationRegistry = contracts.organization;
3216
+ this._organizationRegistry = new OrganizationRegistryService(
3217
+ contracts.organization,
3218
+ this.config.signer
3219
+ );
2990
3220
  }
2991
3221
  if (contracts.stateTransitionVerifier) {
2992
- this.config.contractAddresses.stateTransitionVerifier = contracts.stateTransitionVerifier;
3222
+ this.config.customAddresses.stateTransitionVerifier = contracts.stateTransitionVerifier;
2993
3223
  }
2994
3224
  if (contracts.resultsVerifier) {
2995
- this.config.contractAddresses.resultsVerifier = contracts.resultsVerifier;
3225
+ this.config.customAddresses.resultsVerifier = contracts.resultsVerifier;
2996
3226
  }
2997
3227
  } catch (error) {
2998
- console.warn("Failed to fetch contract addresses from sequencer, using defaults:", error);
3228
+ throw new Error(
3229
+ `Failed to fetch contract addresses from sequencer: ${error instanceof Error ? error.message : String(error)}. You can provide custom addresses in the SDK config to avoid this error.`
3230
+ );
2999
3231
  }
3000
3232
  }
3001
3233
  /**
@@ -3024,5 +3256,5 @@ class DavinciSDK {
3024
3256
  }
3025
3257
  }
3026
3258
 
3027
- export { BaseService, CensusOrigin, CircomProof, ContractServiceError, DEFAULT_ENVIRONMENT_URLS, DavinciCrypto, DavinciSDK, ElectionMetadataTemplate, ElectionResultsTypeNames, OrganizationAdministratorError, OrganizationCreateError, OrganizationDeleteError, OrganizationRegistryService, OrganizationUpdateError, ProcessCensusError, ProcessCreateError, ProcessDurationError, ProcessOrchestrationService, ProcessRegistryService, ProcessResultError, ProcessStateTransitionError, ProcessStatus, ProcessStatusError, SmartContractService, TxStatus, VocdoniApiService, VocdoniCensusService, VocdoniSequencerService, VoteOrchestrationService, VoteStatus, assertCSPCensusProof, assertMerkleCensusProof, createProcessSignatureMessage, deployedAddresses, getElectionMetadataTemplate, getEnvironmentChain, getEnvironmentConfig, getEnvironmentUrls, isCSPCensusProof, isMerkleCensusProof, resolveConfiguration, resolveUrls, signProcessCreation, validateProcessId };
3259
+ export { BaseService, Census, CensusOrchestrator, CensusOrigin, CensusType, CircomProof, ContractServiceError, CspCensus, DavinciCrypto, DavinciSDK, ElectionMetadataTemplate, ElectionResultsTypeNames, OrganizationAdministratorError, OrganizationCreateError, OrganizationDeleteError, OrganizationRegistryService, OrganizationUpdateError, PlainCensus, ProcessCensusError, ProcessCreateError, ProcessDurationError, ProcessOrchestrationService, ProcessRegistryService, ProcessResultError, ProcessStateTransitionError, ProcessStatus, ProcessStatusError, PublishedCensus, SmartContractService, TxStatus, VocdoniApiService, VocdoniCensusService, VocdoniSequencerService, VoteOrchestrationService, VoteStatus, WeightedCensus, assertCSPCensusProof, assertMerkleCensusProof, createProcessSignatureMessage, getElectionMetadataTemplate, isCSPCensusProof, isMerkleCensusProof, signProcessCreation, validateProcessId };
3028
3260
  //# sourceMappingURL=index.mjs.map