@vocdoni/davinci-sdk 0.0.1 → 0.0.2

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
@@ -402,29 +402,34 @@ function resolveUrls(options = {}) {
402
402
  }
403
403
 
404
404
  var processRegistry = {
405
- sepolia: "0x40939Ec9FD872eb79A1723B559572dfD71a05d11",
405
+ sepolia: "0x50CA6A350f3A9C7B8a82eE7a4D5F0f21C54D68e3",
406
406
  uzh: "0x69B16f67Bd2fB18bD720379E9C1Ef5EaD3872d67",
407
- mainnet: "0x0"
407
+ mainnet: "0x0",
408
+ celo: "0xDda6c75d32c375946C8ae9be41B2F3539dB1118A"
408
409
  };
409
410
  var organizationRegistry = {
410
- sepolia: "0xe7136ED5a7b0e995A8fe35d8B1B815E4160cB491",
411
+ sepolia: "0xF30678f579Fd89b86295503dC179d5d3aed47a98",
411
412
  uzh: "0xf7BCE4546805547bE526Ca864d6722Ed193E51Aa",
412
- mainnet: "0x0"
413
+ mainnet: "0x0",
414
+ celo: "0xE17D701EA8f34022F97fC2Ec68c73D42bF99D0BD"
413
415
  };
414
416
  var stateTransitionVerifierGroth16 = {
415
- sepolia: "0xb7A142D24b9220eCBC4f7fcB89Ee952a6C7E332a",
417
+ sepolia: "0x96EcBbD6aB5fDC063E0fC426F2700290DeeAFE4E",
416
418
  uzh: "0x5e4673CD378F05cc3Ae25804539c91E711548741",
417
- mainnet: "0x0"
419
+ mainnet: "0x0",
420
+ celo: "0x2DaF913D423128258b2F378E320F9D9D3Be5eCf5"
418
421
  };
419
422
  var resultsVerifierGroth16 = {
420
- sepolia: "0x1188cEbB56ecc90e2bAe5c914274C81Fe1a22e67",
423
+ sepolia: "0x3ab37C40f7d0649f7a15BA3230f14AB29B51eDCC",
421
424
  uzh: "0x00c7F87731346F592197E49A90Ad6EC236Ad9985",
422
- mainnet: "0x0"
425
+ mainnet: "0x0",
426
+ celo: "0x808276962217AD1ED3af7D51bFc791903CAd9389"
423
427
  };
424
428
  var sequencerRegistry = {
425
429
  sepolia: "0x0",
426
430
  uzh: "0x0",
427
- mainnet: "0x0"
431
+ mainnet: "0x0",
432
+ celo: "0x0"
428
433
  };
429
434
  var addressesJson = {
430
435
  processRegistry: processRegistry,
@@ -443,6 +448,12 @@ var TxStatus = /* @__PURE__ */ ((TxStatus2) => {
443
448
  return TxStatus2;
444
449
  })(TxStatus || {});
445
450
  class SmartContractService {
451
+ constructor() {
452
+ /** Active polling intervals for event listeners using fallback mode */
453
+ this.pollingIntervals = [];
454
+ /** Default polling interval in milliseconds for event listener fallback */
455
+ this.eventPollingInterval = 5e3;
456
+ }
446
457
  /**
447
458
  * Sends a transaction and yields status events during its lifecycle.
448
459
  * This method handles the complete transaction flow from submission to completion,
@@ -554,6 +565,141 @@ class SmartContractService {
554
565
  callback(...args);
555
566
  };
556
567
  }
568
+ /**
569
+ * Sets up an event listener with automatic fallback for RPCs that don't support eth_newFilter.
570
+ * First attempts to use contract.on() which relies on eth_newFilter. If the RPC doesn't support
571
+ * this method (error code -32601), automatically falls back to polling with queryFilter.
572
+ *
573
+ * @template Args - Tuple type representing the event arguments
574
+ * @param contract - The contract instance to listen to
575
+ * @param eventFilter - The event filter to listen for
576
+ * @param callback - The callback function to invoke when the event occurs
577
+ *
578
+ * @example
579
+ * ```typescript
580
+ * this.setupEventListener(
581
+ * this.contract,
582
+ * this.contract.filters.Transfer(),
583
+ * (from: string, to: string, amount: bigint) => {
584
+ * console.log(`Transfer: ${from} -> ${to}: ${amount}`);
585
+ * }
586
+ * );
587
+ * ```
588
+ */
589
+ async setupEventListener(contract, eventFilter, callback) {
590
+ const normalizedCallback = this.normalizeListener(callback);
591
+ const provider = contract.runner?.provider;
592
+ if (!provider) {
593
+ console.warn("No provider available for event listeners");
594
+ return;
595
+ }
596
+ try {
597
+ const testFilter = {
598
+ address: await contract.getAddress(),
599
+ topics: []
600
+ };
601
+ if ("send" in provider && typeof provider.send === "function") {
602
+ try {
603
+ await provider.send("eth_newFilter", [testFilter]);
604
+ contract.on(eventFilter, normalizedCallback);
605
+ return;
606
+ } catch (error) {
607
+ if (this.isUnsupportedMethodError(error)) {
608
+ console.warn(
609
+ "RPC does not support eth_newFilter, falling back to polling for events. This may result in delayed event notifications."
610
+ );
611
+ this.setupPollingListener(contract, eventFilter, callback);
612
+ return;
613
+ }
614
+ }
615
+ }
616
+ const errorHandler = (error) => {
617
+ if (this.isUnsupportedMethodError(error)) {
618
+ contract.off(eventFilter, normalizedCallback);
619
+ contract.off("error", errorHandler);
620
+ console.warn(
621
+ "RPC does not support eth_newFilter, falling back to polling for events. This may result in delayed event notifications."
622
+ );
623
+ this.setupPollingListener(contract, eventFilter, callback);
624
+ }
625
+ };
626
+ contract.once("error", errorHandler);
627
+ contract.on(eventFilter, normalizedCallback);
628
+ } catch (error) {
629
+ console.warn("Error setting up event listener, falling back to polling:", error.message);
630
+ this.setupPollingListener(contract, eventFilter, callback);
631
+ }
632
+ }
633
+ /**
634
+ * Checks if an error indicates that the RPC method is unsupported (eth_newFilter).
635
+ *
636
+ * @param error - The error to check
637
+ * @returns true if the error indicates unsupported method
638
+ */
639
+ isUnsupportedMethodError(error) {
640
+ return error?.code === -32601 || error?.error?.code === -32601 || error?.data?.code === -32601 || typeof error?.message === "string" && error.message.includes("unsupported method");
641
+ }
642
+ /**
643
+ * Sets up a polling-based event listener as fallback when eth_newFilter is not supported.
644
+ * Periodically queries for new events and invokes the callback for each new event found.
645
+ *
646
+ * @template Args - Tuple type representing the event arguments
647
+ * @param contract - The contract instance to poll
648
+ * @param eventFilter - The event filter to poll for
649
+ * @param callback - The callback function to invoke for each event
650
+ */
651
+ setupPollingListener(contract, eventFilter, callback) {
652
+ let lastProcessedBlock = 0;
653
+ const poll = async () => {
654
+ try {
655
+ const provider = contract.runner?.provider;
656
+ if (!provider) {
657
+ console.warn("No provider available for polling events");
658
+ return;
659
+ }
660
+ const currentBlock = await provider.getBlockNumber();
661
+ if (lastProcessedBlock === 0) {
662
+ lastProcessedBlock = currentBlock - 1;
663
+ }
664
+ if (currentBlock > lastProcessedBlock) {
665
+ const events = await contract.queryFilter(
666
+ eventFilter,
667
+ lastProcessedBlock + 1,
668
+ currentBlock
669
+ );
670
+ for (const event of events) {
671
+ if ("args" in event && event.args) {
672
+ callback(...event.args);
673
+ }
674
+ }
675
+ lastProcessedBlock = currentBlock;
676
+ }
677
+ } catch (error) {
678
+ console.error("Error polling for events:", error);
679
+ }
680
+ };
681
+ const intervalId = setInterval(poll, this.eventPollingInterval);
682
+ this.pollingIntervals.push(intervalId);
683
+ poll();
684
+ }
685
+ /**
686
+ * Clears all active polling intervals.
687
+ * Should be called when removing all listeners or cleaning up the service.
688
+ */
689
+ clearPollingIntervals() {
690
+ for (const intervalId of this.pollingIntervals) {
691
+ clearInterval(intervalId);
692
+ }
693
+ this.pollingIntervals = [];
694
+ }
695
+ /**
696
+ * Sets the polling interval for event listeners using the fallback mechanism.
697
+ *
698
+ * @param intervalMs - Polling interval in milliseconds
699
+ */
700
+ setEventPollingInterval(intervalMs) {
701
+ this.eventPollingInterval = intervalMs;
702
+ }
557
703
  }
558
704
 
559
705
  class ContractServiceError extends Error {
@@ -732,43 +878,50 @@ class ProcessRegistryService extends SmartContractService {
732
878
  }
733
879
  // ─── EVENT LISTENERS ───────────────────────────────────────────────────────
734
880
  onProcessCreated(cb) {
735
- this.contract.on(
881
+ this.setupEventListener(
882
+ this.contract,
736
883
  this.contract.filters.ProcessCreated(),
737
- this.normalizeListener(cb)
738
- );
884
+ cb
885
+ ).catch((err) => console.error("Error setting up ProcessCreated listener:", err));
739
886
  }
740
887
  onProcessStatusChanged(cb) {
741
- this.contract.on(
888
+ this.setupEventListener(
889
+ this.contract,
742
890
  this.contract.filters.ProcessStatusChanged(),
743
- this.normalizeListener(cb)
744
- );
891
+ cb
892
+ ).catch((err) => console.error("Error setting up ProcessStatusChanged listener:", err));
745
893
  }
746
894
  onCensusUpdated(cb) {
747
- this.contract.on(
895
+ this.setupEventListener(
896
+ this.contract,
748
897
  this.contract.filters.CensusUpdated(),
749
- this.normalizeListener(cb)
750
- );
898
+ cb
899
+ ).catch((err) => console.error("Error setting up CensusUpdated listener:", err));
751
900
  }
752
901
  onProcessDurationChanged(cb) {
753
- this.contract.on(
902
+ this.setupEventListener(
903
+ this.contract,
754
904
  this.contract.filters.ProcessDurationChanged(),
755
- this.normalizeListener(cb)
756
- );
905
+ cb
906
+ ).catch((err) => console.error("Error setting up ProcessDurationChanged listener:", err));
757
907
  }
758
908
  onStateRootUpdated(cb) {
759
- this.contract.on(
909
+ this.setupEventListener(
910
+ this.contract,
760
911
  this.contract.filters.ProcessStateRootUpdated(),
761
- this.normalizeListener(cb)
762
- );
912
+ cb
913
+ ).catch((err) => console.error("Error setting up StateRootUpdated listener:", err));
763
914
  }
764
915
  onProcessResultsSet(cb) {
765
- this.contract.on(
916
+ this.setupEventListener(
917
+ this.contract,
766
918
  this.contract.filters.ProcessResultsSet(),
767
- this.normalizeListener(cb)
768
- );
919
+ cb
920
+ ).catch((err) => console.error("Error setting up ProcessResultsSet listener:", err));
769
921
  }
770
922
  removeAllListeners() {
771
923
  this.contract.removeAllListeners();
924
+ this.clearPollingIntervals();
772
925
  }
773
926
  }
774
927
 
@@ -852,10 +1005,114 @@ class ProcessOrchestrationService {
852
1005
  };
853
1006
  }
854
1007
  /**
855
- * Creates a complete voting process with minimal configuration
856
- * This method handles all the complex orchestration internally
1008
+ * Creates a complete voting process and returns an async generator that yields transaction status events.
1009
+ * This method allows you to monitor the transaction progress in real-time.
1010
+ *
1011
+ * @param config - Process configuration
1012
+ * @returns AsyncGenerator yielding transaction status events with ProcessCreationResult
1013
+ *
1014
+ * @example
1015
+ * ```typescript
1016
+ * const stream = sdk.createProcessStream({
1017
+ * title: "My Election",
1018
+ * description: "A simple election",
1019
+ * census: { ... },
1020
+ * ballot: { ... },
1021
+ * timing: { ... },
1022
+ * questions: [ ... ]
1023
+ * });
1024
+ *
1025
+ * for await (const event of stream) {
1026
+ * switch (event.status) {
1027
+ * case "pending":
1028
+ * console.log("Transaction pending:", event.hash);
1029
+ * break;
1030
+ * case "completed":
1031
+ * console.log("Process created:", event.response.processId);
1032
+ * console.log("Transaction hash:", event.response.transactionHash);
1033
+ * break;
1034
+ * case "failed":
1035
+ * console.error("Transaction failed:", event.error);
1036
+ * break;
1037
+ * case "reverted":
1038
+ * console.error("Transaction reverted:", event.reason);
1039
+ * break;
1040
+ * }
1041
+ * }
1042
+ * ```
1043
+ */
1044
+ async *createProcessStream(config) {
1045
+ const data = await this.prepareProcessCreation(config);
1046
+ const encryptionKey = {
1047
+ x: data.sequencerResult.encryptionPubKey[0],
1048
+ y: data.sequencerResult.encryptionPubKey[1]
1049
+ };
1050
+ const txStream = this.processRegistry.newProcess(
1051
+ ProcessStatus.READY,
1052
+ data.startTime,
1053
+ data.duration,
1054
+ data.ballotMode,
1055
+ data.census,
1056
+ data.metadataUri,
1057
+ encryptionKey,
1058
+ BigInt(data.sequencerResult.stateRoot)
1059
+ );
1060
+ let transactionHash = "unknown";
1061
+ for await (const event of txStream) {
1062
+ if (event.status === TxStatus.Pending) {
1063
+ transactionHash = event.hash;
1064
+ yield { status: TxStatus.Pending, hash: event.hash };
1065
+ } else if (event.status === TxStatus.Completed) {
1066
+ yield {
1067
+ status: TxStatus.Completed,
1068
+ response: {
1069
+ processId: data.processId,
1070
+ transactionHash
1071
+ }
1072
+ };
1073
+ break;
1074
+ } else if (event.status === TxStatus.Failed) {
1075
+ yield { status: TxStatus.Failed, error: event.error };
1076
+ break;
1077
+ } else if (event.status === TxStatus.Reverted) {
1078
+ yield { status: TxStatus.Reverted, reason: event.reason };
1079
+ break;
1080
+ }
1081
+ }
1082
+ }
1083
+ /**
1084
+ * Creates a complete voting process with minimal configuration.
1085
+ * This is the ultra-easy method for end users that handles all the complex orchestration internally.
1086
+ *
1087
+ * For real-time transaction status updates, use createProcessStream() instead.
1088
+ *
1089
+ * The method automatically:
1090
+ * - Gets encryption keys and initial state root from the sequencer
1091
+ * - Handles process creation signatures
1092
+ * - Coordinates between sequencer API and on-chain contract calls
1093
+ * - Creates and pushes metadata
1094
+ * - Submits the on-chain transaction
1095
+ *
1096
+ * @param config - Simplified process configuration
1097
+ * @returns Promise resolving to the process creation result
857
1098
  */
858
1099
  async createProcess(config) {
1100
+ for await (const event of this.createProcessStream(config)) {
1101
+ if (event.status === "completed") {
1102
+ return event.response;
1103
+ } else if (event.status === "failed") {
1104
+ throw event.error;
1105
+ } else if (event.status === "reverted") {
1106
+ throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
1107
+ }
1108
+ }
1109
+ throw new Error("Process creation stream ended unexpectedly");
1110
+ }
1111
+ /**
1112
+ * Prepares all data needed for process creation
1113
+ * @private
1114
+ */
1115
+ async prepareProcessCreation(config) {
859
1116
  const { startTime, duration } = this.calculateTiming(config.timing);
860
1117
  const signerAddress = await this.signer.getAddress();
861
1118
  const processId = await this.processRegistry.getNextProcessId(signerAddress);
@@ -878,35 +1135,15 @@ class ProcessOrchestrationService {
878
1135
  censusRoot,
879
1136
  censusURI: config.census.uri
880
1137
  };
881
- const encryptionKey = {
882
- x: sequencerResult.encryptionPubKey[0],
883
- y: sequencerResult.encryptionPubKey[1]
884
- };
885
- const txStream = this.processRegistry.newProcess(
886
- ProcessStatus.READY,
1138
+ return {
1139
+ processId,
887
1140
  startTime,
888
1141
  duration,
1142
+ censusRoot,
889
1143
  ballotMode,
890
- census,
891
1144
  metadataUri,
892
- encryptionKey,
893
- BigInt(sequencerResult.stateRoot)
894
- );
895
- let transactionHash = "unknown";
896
- for await (const event of txStream) {
897
- if (event.status === "pending") {
898
- transactionHash = event.hash;
899
- } else if (event.status === "completed") {
900
- break;
901
- } else if (event.status === "failed") {
902
- throw event.error;
903
- } else if (event.status === "reverted") {
904
- throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
905
- }
906
- }
907
- return {
908
- processId,
909
- transactionHash
1145
+ sequencerResult,
1146
+ census
910
1147
  };
911
1148
  }
912
1149
  /**
@@ -984,6 +1221,312 @@ class ProcessOrchestrationService {
984
1221
  }));
985
1222
  return metadata;
986
1223
  }
1224
+ /**
1225
+ * Ends a voting process by setting its status to ENDED.
1226
+ * Returns an async generator that yields transaction status events.
1227
+ *
1228
+ * @param processId - The process ID to end
1229
+ * @returns AsyncGenerator yielding transaction status events
1230
+ *
1231
+ * @example
1232
+ * ```typescript
1233
+ * const stream = sdk.endProcessStream("0x1234567890abcdef...");
1234
+ *
1235
+ * for await (const event of stream) {
1236
+ * switch (event.status) {
1237
+ * case "pending":
1238
+ * console.log("Transaction pending:", event.hash);
1239
+ * break;
1240
+ * case "completed":
1241
+ * console.log("Process ended successfully");
1242
+ * break;
1243
+ * case "failed":
1244
+ * console.error("Transaction failed:", event.error);
1245
+ * break;
1246
+ * case "reverted":
1247
+ * console.error("Transaction reverted:", event.reason);
1248
+ * break;
1249
+ * }
1250
+ * }
1251
+ * ```
1252
+ */
1253
+ async *endProcessStream(processId) {
1254
+ const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.ENDED);
1255
+ for await (const event of txStream) {
1256
+ if (event.status === TxStatus.Pending) {
1257
+ yield { status: TxStatus.Pending, hash: event.hash };
1258
+ } else if (event.status === TxStatus.Completed) {
1259
+ yield {
1260
+ status: TxStatus.Completed,
1261
+ response: { success: true }
1262
+ };
1263
+ break;
1264
+ } else if (event.status === TxStatus.Failed) {
1265
+ yield { status: TxStatus.Failed, error: event.error };
1266
+ break;
1267
+ } else if (event.status === TxStatus.Reverted) {
1268
+ yield { status: TxStatus.Reverted, reason: event.reason };
1269
+ break;
1270
+ }
1271
+ }
1272
+ }
1273
+ /**
1274
+ * Ends a voting process by setting its status to ENDED.
1275
+ * This is a simplified method that waits for transaction completion.
1276
+ *
1277
+ * For real-time transaction status updates, use endProcessStream() instead.
1278
+ *
1279
+ * @param processId - The process ID to end
1280
+ * @returns Promise resolving when the process is ended
1281
+ *
1282
+ * @example
1283
+ * ```typescript
1284
+ * await sdk.endProcess("0x1234567890abcdef...");
1285
+ * console.log("Process ended successfully");
1286
+ * ```
1287
+ */
1288
+ async endProcess(processId) {
1289
+ for await (const event of this.endProcessStream(processId)) {
1290
+ if (event.status === "completed") {
1291
+ return;
1292
+ } else if (event.status === "failed") {
1293
+ throw event.error;
1294
+ } else if (event.status === "reverted") {
1295
+ throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
1296
+ }
1297
+ }
1298
+ throw new Error("End process stream ended unexpectedly");
1299
+ }
1300
+ /**
1301
+ * Pauses a voting process by setting its status to PAUSED.
1302
+ * Returns an async generator that yields transaction status events.
1303
+ *
1304
+ * @param processId - The process ID to pause
1305
+ * @returns AsyncGenerator yielding transaction status events
1306
+ *
1307
+ * @example
1308
+ * ```typescript
1309
+ * const stream = sdk.pauseProcessStream("0x1234567890abcdef...");
1310
+ *
1311
+ * for await (const event of stream) {
1312
+ * switch (event.status) {
1313
+ * case "pending":
1314
+ * console.log("Transaction pending:", event.hash);
1315
+ * break;
1316
+ * case "completed":
1317
+ * console.log("Process paused successfully");
1318
+ * break;
1319
+ * case "failed":
1320
+ * console.error("Transaction failed:", event.error);
1321
+ * break;
1322
+ * case "reverted":
1323
+ * console.error("Transaction reverted:", event.reason);
1324
+ * break;
1325
+ * }
1326
+ * }
1327
+ * ```
1328
+ */
1329
+ async *pauseProcessStream(processId) {
1330
+ const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.PAUSED);
1331
+ for await (const event of txStream) {
1332
+ if (event.status === TxStatus.Pending) {
1333
+ yield { status: TxStatus.Pending, hash: event.hash };
1334
+ } else if (event.status === TxStatus.Completed) {
1335
+ yield {
1336
+ status: TxStatus.Completed,
1337
+ response: { success: true }
1338
+ };
1339
+ break;
1340
+ } else if (event.status === TxStatus.Failed) {
1341
+ yield { status: TxStatus.Failed, error: event.error };
1342
+ break;
1343
+ } else if (event.status === TxStatus.Reverted) {
1344
+ yield { status: TxStatus.Reverted, reason: event.reason };
1345
+ break;
1346
+ }
1347
+ }
1348
+ }
1349
+ /**
1350
+ * Pauses a voting process by setting its status to PAUSED.
1351
+ * This is a simplified method that waits for transaction completion.
1352
+ *
1353
+ * For real-time transaction status updates, use pauseProcessStream() instead.
1354
+ *
1355
+ * @param processId - The process ID to pause
1356
+ * @returns Promise resolving when the process is paused
1357
+ *
1358
+ * @example
1359
+ * ```typescript
1360
+ * await sdk.pauseProcess("0x1234567890abcdef...");
1361
+ * console.log("Process paused successfully");
1362
+ * ```
1363
+ */
1364
+ async pauseProcess(processId) {
1365
+ for await (const event of this.pauseProcessStream(processId)) {
1366
+ if (event.status === "completed") {
1367
+ return;
1368
+ } else if (event.status === "failed") {
1369
+ throw event.error;
1370
+ } else if (event.status === "reverted") {
1371
+ throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
1372
+ }
1373
+ }
1374
+ throw new Error("Pause process stream ended unexpectedly");
1375
+ }
1376
+ /**
1377
+ * Cancels a voting process by setting its status to CANCELED.
1378
+ * Returns an async generator that yields transaction status events.
1379
+ *
1380
+ * @param processId - The process ID to cancel
1381
+ * @returns AsyncGenerator yielding transaction status events
1382
+ *
1383
+ * @example
1384
+ * ```typescript
1385
+ * const stream = sdk.cancelProcessStream("0x1234567890abcdef...");
1386
+ *
1387
+ * for await (const event of stream) {
1388
+ * switch (event.status) {
1389
+ * case "pending":
1390
+ * console.log("Transaction pending:", event.hash);
1391
+ * break;
1392
+ * case "completed":
1393
+ * console.log("Process canceled successfully");
1394
+ * break;
1395
+ * case "failed":
1396
+ * console.error("Transaction failed:", event.error);
1397
+ * break;
1398
+ * case "reverted":
1399
+ * console.error("Transaction reverted:", event.reason);
1400
+ * break;
1401
+ * }
1402
+ * }
1403
+ * ```
1404
+ */
1405
+ async *cancelProcessStream(processId) {
1406
+ const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.CANCELED);
1407
+ for await (const event of txStream) {
1408
+ if (event.status === TxStatus.Pending) {
1409
+ yield { status: TxStatus.Pending, hash: event.hash };
1410
+ } else if (event.status === TxStatus.Completed) {
1411
+ yield {
1412
+ status: TxStatus.Completed,
1413
+ response: { success: true }
1414
+ };
1415
+ break;
1416
+ } else if (event.status === TxStatus.Failed) {
1417
+ yield { status: TxStatus.Failed, error: event.error };
1418
+ break;
1419
+ } else if (event.status === TxStatus.Reverted) {
1420
+ yield { status: TxStatus.Reverted, reason: event.reason };
1421
+ break;
1422
+ }
1423
+ }
1424
+ }
1425
+ /**
1426
+ * Cancels a voting process by setting its status to CANCELED.
1427
+ * This is a simplified method that waits for transaction completion.
1428
+ *
1429
+ * For real-time transaction status updates, use cancelProcessStream() instead.
1430
+ *
1431
+ * @param processId - The process ID to cancel
1432
+ * @returns Promise resolving when the process is canceled
1433
+ *
1434
+ * @example
1435
+ * ```typescript
1436
+ * await sdk.cancelProcess("0x1234567890abcdef...");
1437
+ * console.log("Process canceled successfully");
1438
+ * ```
1439
+ */
1440
+ async cancelProcess(processId) {
1441
+ for await (const event of this.cancelProcessStream(processId)) {
1442
+ if (event.status === "completed") {
1443
+ return;
1444
+ } else if (event.status === "failed") {
1445
+ throw event.error;
1446
+ } else if (event.status === "reverted") {
1447
+ throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
1448
+ }
1449
+ }
1450
+ throw new Error("Cancel process stream ended unexpectedly");
1451
+ }
1452
+ /**
1453
+ * Resumes a voting process by setting its status to READY.
1454
+ * This is typically used to resume a paused process.
1455
+ * Returns an async generator that yields transaction status events.
1456
+ *
1457
+ * @param processId - The process ID to resume
1458
+ * @returns AsyncGenerator yielding transaction status events
1459
+ *
1460
+ * @example
1461
+ * ```typescript
1462
+ * const stream = sdk.resumeProcessStream("0x1234567890abcdef...");
1463
+ *
1464
+ * for await (const event of stream) {
1465
+ * switch (event.status) {
1466
+ * case "pending":
1467
+ * console.log("Transaction pending:", event.hash);
1468
+ * break;
1469
+ * case "completed":
1470
+ * console.log("Process resumed successfully");
1471
+ * break;
1472
+ * case "failed":
1473
+ * console.error("Transaction failed:", event.error);
1474
+ * break;
1475
+ * case "reverted":
1476
+ * console.error("Transaction reverted:", event.reason);
1477
+ * break;
1478
+ * }
1479
+ * }
1480
+ * ```
1481
+ */
1482
+ async *resumeProcessStream(processId) {
1483
+ const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.READY);
1484
+ for await (const event of txStream) {
1485
+ if (event.status === TxStatus.Pending) {
1486
+ yield { status: TxStatus.Pending, hash: event.hash };
1487
+ } else if (event.status === TxStatus.Completed) {
1488
+ yield {
1489
+ status: TxStatus.Completed,
1490
+ response: { success: true }
1491
+ };
1492
+ break;
1493
+ } else if (event.status === TxStatus.Failed) {
1494
+ yield { status: TxStatus.Failed, error: event.error };
1495
+ break;
1496
+ } else if (event.status === TxStatus.Reverted) {
1497
+ yield { status: TxStatus.Reverted, reason: event.reason };
1498
+ break;
1499
+ }
1500
+ }
1501
+ }
1502
+ /**
1503
+ * Resumes a voting process by setting its status to READY.
1504
+ * This is typically used to resume a paused process.
1505
+ * This is a simplified method that waits for transaction completion.
1506
+ *
1507
+ * For real-time transaction status updates, use resumeProcessStream() instead.
1508
+ *
1509
+ * @param processId - The process ID to resume
1510
+ * @returns Promise resolving when the process is resumed
1511
+ *
1512
+ * @example
1513
+ * ```typescript
1514
+ * await sdk.resumeProcess("0x1234567890abcdef...");
1515
+ * console.log("Process resumed successfully");
1516
+ * ```
1517
+ */
1518
+ async resumeProcess(processId) {
1519
+ for await (const event of this.resumeProcessStream(processId)) {
1520
+ if (event.status === "completed") {
1521
+ return;
1522
+ } else if (event.status === "failed") {
1523
+ throw event.error;
1524
+ } else if (event.status === "reverted") {
1525
+ throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
1526
+ }
1527
+ }
1528
+ throw new Error("Resume process stream ended unexpectedly");
1529
+ }
987
1530
  }
988
1531
 
989
1532
  class CircomProof {
@@ -1173,11 +1716,61 @@ class VoteOrchestrationService {
1173
1716
  * @param address - The voter's address
1174
1717
  * @returns Promise resolving to boolean indicating if the address has voted
1175
1718
  */
1176
- async hasAddressVoted(processId, address) {
1177
- return this.apiService.sequencer.hasAddressVoted(processId, address);
1719
+ async hasAddressVoted(processId, address) {
1720
+ return this.apiService.sequencer.hasAddressVoted(processId, address);
1721
+ }
1722
+ /**
1723
+ * Watch vote status changes in real-time using an async generator.
1724
+ * Yields each status change as it happens, allowing for reactive UI updates.
1725
+ *
1726
+ * @param processId - The process ID
1727
+ * @param voteId - The vote ID
1728
+ * @param options - Optional configuration
1729
+ * @returns AsyncGenerator yielding vote status updates
1730
+ *
1731
+ * @example
1732
+ * ```typescript
1733
+ * const vote = await sdk.submitVote({ processId, choices: [1] });
1734
+ *
1735
+ * for await (const statusInfo of sdk.watchVoteStatus(vote.processId, vote.voteId)) {
1736
+ * console.log(`Vote status: ${statusInfo.status}`);
1737
+ *
1738
+ * switch (statusInfo.status) {
1739
+ * case VoteStatus.Pending:
1740
+ * console.log("⏳ Processing...");
1741
+ * break;
1742
+ * case VoteStatus.Verified:
1743
+ * console.log("✓ Verified");
1744
+ * break;
1745
+ * case VoteStatus.Settled:
1746
+ * console.log("✅ Settled");
1747
+ * break;
1748
+ * }
1749
+ * }
1750
+ * ```
1751
+ */
1752
+ async *watchVoteStatus(processId, voteId, options) {
1753
+ const targetStatus = options?.targetStatus ?? VoteStatus.Settled;
1754
+ const timeoutMs = options?.timeoutMs ?? 3e5;
1755
+ const pollIntervalMs = options?.pollIntervalMs ?? 5e3;
1756
+ const startTime = Date.now();
1757
+ let previousStatus = null;
1758
+ while (Date.now() - startTime < timeoutMs) {
1759
+ const statusInfo = await this.getVoteStatus(processId, voteId);
1760
+ if (statusInfo.status !== previousStatus) {
1761
+ previousStatus = statusInfo.status;
1762
+ yield statusInfo;
1763
+ if (statusInfo.status === targetStatus || statusInfo.status === VoteStatus.Error) {
1764
+ return;
1765
+ }
1766
+ }
1767
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
1768
+ }
1769
+ throw new Error(`Vote did not reach status ${targetStatus} within ${timeoutMs}ms`);
1178
1770
  }
1179
1771
  /**
1180
- * Wait for a vote to reach a specific status
1772
+ * Wait for a vote to reach a specific status.
1773
+ * This is a simpler alternative to watchVoteStatus() that returns only the final status.
1181
1774
  *
1182
1775
  * @param processId - The process ID
1183
1776
  * @param voteId - The vote ID
@@ -1187,15 +1780,18 @@ class VoteOrchestrationService {
1187
1780
  * @returns Promise resolving to final vote status
1188
1781
  */
1189
1782
  async waitForVoteStatus(processId, voteId, targetStatus = VoteStatus.Settled, timeoutMs = 3e5, pollIntervalMs = 5e3) {
1190
- const startTime = Date.now();
1191
- while (Date.now() - startTime < timeoutMs) {
1192
- const statusInfo = await this.getVoteStatus(processId, voteId);
1193
- if (statusInfo.status === targetStatus || statusInfo.status === VoteStatus.Error) {
1194
- return statusInfo;
1195
- }
1196
- await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
1783
+ let finalStatus = null;
1784
+ for await (const statusInfo of this.watchVoteStatus(processId, voteId, {
1785
+ targetStatus,
1786
+ timeoutMs,
1787
+ pollIntervalMs
1788
+ })) {
1789
+ finalStatus = statusInfo;
1197
1790
  }
1198
- throw new Error(`Vote did not reach status ${targetStatus} within ${timeoutMs}ms`);
1791
+ if (!finalStatus) {
1792
+ throw new Error(`Vote did not reach status ${targetStatus} within ${timeoutMs}ms`);
1793
+ }
1794
+ return finalStatus;
1199
1795
  }
1200
1796
  /**
1201
1797
  * Get census proof based on census origin type
@@ -1384,31 +1980,36 @@ class OrganizationRegistryService extends SmartContractService {
1384
1980
  }
1385
1981
  // ─── EVENT LISTENERS ───────────────────────────────────────────────────────
1386
1982
  onOrganizationCreated(cb) {
1387
- this.contract.on(
1983
+ this.setupEventListener(
1984
+ this.contract,
1388
1985
  this.contract.filters.OrganizationCreated(),
1389
- this.normalizeListener(cb)
1390
- );
1986
+ cb
1987
+ ).catch((err) => console.error("Error setting up OrganizationCreated listener:", err));
1391
1988
  }
1392
1989
  onOrganizationUpdated(cb) {
1393
- this.contract.on(
1990
+ this.setupEventListener(
1991
+ this.contract,
1394
1992
  this.contract.filters.OrganizationUpdated(),
1395
- this.normalizeListener(cb)
1396
- );
1993
+ cb
1994
+ ).catch((err) => console.error("Error setting up OrganizationUpdated listener:", err));
1397
1995
  }
1398
1996
  onAdministratorAdded(cb) {
1399
- this.contract.on(
1997
+ this.setupEventListener(
1998
+ this.contract,
1400
1999
  this.contract.filters.AdministratorAdded(),
1401
- this.normalizeListener(cb)
1402
- );
2000
+ cb
2001
+ ).catch((err) => console.error("Error setting up AdministratorAdded listener:", err));
1403
2002
  }
1404
2003
  onAdministratorRemoved(cb) {
1405
- this.contract.on(
2004
+ this.setupEventListener(
2005
+ this.contract,
1406
2006
  this.contract.filters.AdministratorRemoved(),
1407
- this.normalizeListener(cb)
1408
- );
2007
+ cb
2008
+ ).catch((err) => console.error("Error setting up AdministratorRemoved listener:", err));
1409
2009
  }
1410
2010
  removeAllListeners() {
1411
2011
  this.contract.removeAllListeners();
2012
+ this.clearPollingIntervals();
1412
2013
  }
1413
2014
  }
1414
2015
 
@@ -1635,9 +2236,13 @@ class DavinciSDK {
1635
2236
  return this.apiService;
1636
2237
  }
1637
2238
  /**
1638
- * Get the process registry service for process management
2239
+ * Get the process registry service for process management.
2240
+ * Requires a signer with a provider for blockchain interactions.
2241
+ *
2242
+ * @throws Error if signer does not have a provider
1639
2243
  */
1640
2244
  get processes() {
2245
+ this.ensureProvider();
1641
2246
  if (!this._processRegistry) {
1642
2247
  const processRegistryAddress = this.resolveContractAddress("processRegistry");
1643
2248
  this._processRegistry = new ProcessRegistryService(processRegistryAddress, this.config.signer);
@@ -1645,9 +2250,13 @@ class DavinciSDK {
1645
2250
  return this._processRegistry;
1646
2251
  }
1647
2252
  /**
1648
- * Get the organization registry service for organization management
2253
+ * Get the organization registry service for organization management.
2254
+ * Requires a signer with a provider for blockchain interactions.
2255
+ *
2256
+ * @throws Error if signer does not have a provider
1649
2257
  */
1650
2258
  get organizations() {
2259
+ this.ensureProvider();
1651
2260
  if (!this._organizationRegistry) {
1652
2261
  const organizationRegistryAddress = this.resolveContractAddress("organizationRegistry");
1653
2262
  this._organizationRegistry = new OrganizationRegistryService(organizationRegistryAddress, this.config.signer);
@@ -1669,9 +2278,13 @@ class DavinciSDK {
1669
2278
  return this.davinciCrypto;
1670
2279
  }
1671
2280
  /**
1672
- * Get the process orchestration service for simplified process creation
2281
+ * Get the process orchestration service for simplified process creation.
2282
+ * Requires a signer with a provider for blockchain interactions.
2283
+ *
2284
+ * @throws Error if signer does not have a provider
1673
2285
  */
1674
2286
  get processOrchestrator() {
2287
+ this.ensureProvider();
1675
2288
  if (!this._processOrchestrator) {
1676
2289
  this._processOrchestrator = new ProcessOrchestrationService(
1677
2290
  this.processes,
@@ -1702,8 +2315,11 @@ class DavinciSDK {
1702
2315
  * This method fetches raw contract data and transforms it into a user-friendly format
1703
2316
  * that matches the ProcessConfig interface used for creation, plus additional runtime data.
1704
2317
  *
2318
+ * Requires a signer with a provider for blockchain interactions.
2319
+ *
1705
2320
  * @param processId - The process ID to fetch
1706
2321
  * @returns Promise resolving to user-friendly process information
2322
+ * @throws Error if signer does not have a provider
1707
2323
  *
1708
2324
  * @example
1709
2325
  * ```typescript
@@ -1732,12 +2348,95 @@ class DavinciSDK {
1732
2348
  if (!this.initialized) {
1733
2349
  throw new Error("SDK must be initialized before getting processes. Call sdk.init() first.");
1734
2350
  }
2351
+ this.ensureProvider();
1735
2352
  return this.processOrchestrator.getProcess(processId);
1736
2353
  }
2354
+ /**
2355
+ * Creates a complete voting process and returns an async generator that yields transaction status events.
2356
+ * This method allows you to monitor the transaction progress in real-time, including pending, completed,
2357
+ * failed, and reverted states.
2358
+ *
2359
+ * Requires a signer with a provider for blockchain interactions.
2360
+ *
2361
+ * @param config - Simplified process configuration
2362
+ * @returns AsyncGenerator yielding transaction status events
2363
+ * @throws Error if signer does not have a provider
2364
+ *
2365
+ * @example
2366
+ * ```typescript
2367
+ * const stream = sdk.createProcessStream({
2368
+ * title: "My Election",
2369
+ * description: "A simple election",
2370
+ * census: {
2371
+ * type: CensusOrigin.CensusOriginMerkleTree,
2372
+ * root: "0x1234...",
2373
+ * size: 100,
2374
+ * uri: "ipfs://..."
2375
+ * },
2376
+ * ballot: {
2377
+ * numFields: 2,
2378
+ * maxValue: "3",
2379
+ * minValue: "0",
2380
+ * uniqueValues: false,
2381
+ * costFromWeight: false,
2382
+ * costExponent: 10000,
2383
+ * maxValueSum: "6",
2384
+ * minValueSum: "0"
2385
+ * },
2386
+ * timing: {
2387
+ * startDate: new Date("2024-12-01T10:00:00Z"),
2388
+ * duration: 3600 * 24
2389
+ * },
2390
+ * questions: [
2391
+ * {
2392
+ * title: "What is your favorite color?",
2393
+ * choices: [
2394
+ * { title: "Red", value: 0 },
2395
+ * { title: "Blue", value: 1 }
2396
+ * ]
2397
+ * }
2398
+ * ]
2399
+ * });
2400
+ *
2401
+ * // Monitor transaction progress
2402
+ * for await (const event of stream) {
2403
+ * switch (event.status) {
2404
+ * case TxStatus.Pending:
2405
+ * console.log("Transaction pending:", event.hash);
2406
+ * // Update UI to show pending state
2407
+ * break;
2408
+ * case TxStatus.Completed:
2409
+ * console.log("Process created:", event.response.processId);
2410
+ * console.log("Transaction hash:", event.response.transactionHash);
2411
+ * // Update UI to show success
2412
+ * break;
2413
+ * case TxStatus.Failed:
2414
+ * console.error("Transaction failed:", event.error);
2415
+ * // Update UI to show error
2416
+ * break;
2417
+ * case TxStatus.Reverted:
2418
+ * console.error("Transaction reverted:", event.reason);
2419
+ * // Update UI to show revert reason
2420
+ * break;
2421
+ * }
2422
+ * }
2423
+ * ```
2424
+ */
2425
+ createProcessStream(config) {
2426
+ if (!this.initialized) {
2427
+ throw new Error("SDK must be initialized before creating processes. Call sdk.init() first.");
2428
+ }
2429
+ this.ensureProvider();
2430
+ return this.processOrchestrator.createProcessStream(config);
2431
+ }
1737
2432
  /**
1738
2433
  * Creates a complete voting process with minimal configuration.
1739
2434
  * This is the ultra-easy method for end users that handles all the complex orchestration internally.
1740
2435
  *
2436
+ * For real-time transaction status updates, use createProcessStream() instead.
2437
+ *
2438
+ * Requires a signer with a provider for blockchain interactions.
2439
+ *
1741
2440
  * The method automatically:
1742
2441
  * - Gets encryption keys and initial state root from the sequencer
1743
2442
  * - Handles process creation signatures
@@ -1747,6 +2446,7 @@ class DavinciSDK {
1747
2446
  *
1748
2447
  * @param config - Simplified process configuration
1749
2448
  * @returns Promise resolving to the process creation result
2449
+ * @throws Error if signer does not have a provider
1750
2450
  *
1751
2451
  * @example
1752
2452
  * ```typescript
@@ -1799,12 +2499,15 @@ class DavinciSDK {
1799
2499
  if (!this.initialized) {
1800
2500
  throw new Error("SDK must be initialized before creating processes. Call sdk.init() first.");
1801
2501
  }
2502
+ this.ensureProvider();
1802
2503
  return this.processOrchestrator.createProcess(config);
1803
2504
  }
1804
2505
  /**
1805
2506
  * Submit a vote with simplified configuration.
1806
2507
  * This is the ultra-easy method for end users that handles all the complex voting workflow internally.
1807
2508
  *
2509
+ * Does NOT require a provider - can be used with a bare Wallet for signing only.
2510
+ *
1808
2511
  * The method automatically:
1809
2512
  * - Fetches process information and validates voting is allowed
1810
2513
  * - Gets census proof (Merkle tree based)
@@ -1846,6 +2549,8 @@ class DavinciSDK {
1846
2549
  /**
1847
2550
  * Get the status of a submitted vote.
1848
2551
  *
2552
+ * Does NOT require a provider - uses API calls only.
2553
+ *
1849
2554
  * @param processId - The process ID
1850
2555
  * @param voteId - The vote ID returned from submitVote()
1851
2556
  * @returns Promise resolving to vote status information
@@ -1866,6 +2571,8 @@ class DavinciSDK {
1866
2571
  /**
1867
2572
  * Check if an address has voted in a process.
1868
2573
  *
2574
+ * Does NOT require a provider - uses API calls only.
2575
+ *
1869
2576
  * @param processId - The process ID
1870
2577
  * @param address - The voter's address
1871
2578
  * @returns Promise resolving to boolean indicating if the address has voted
@@ -1884,9 +2591,59 @@ class DavinciSDK {
1884
2591
  }
1885
2592
  return this.voteOrchestrator.hasAddressVoted(processId, address);
1886
2593
  }
2594
+ /**
2595
+ * Watch vote status changes in real-time using an async generator.
2596
+ * This method yields each status change as it happens, perfect for showing
2597
+ * progress indicators in UI applications.
2598
+ *
2599
+ * Does NOT require a provider - uses API calls only.
2600
+ *
2601
+ * @param processId - The process ID
2602
+ * @param voteId - The vote ID
2603
+ * @param options - Optional configuration
2604
+ * @returns AsyncGenerator yielding vote status updates
2605
+ *
2606
+ * @example
2607
+ * ```typescript
2608
+ * // Submit vote
2609
+ * const voteResult = await sdk.submitVote({
2610
+ * processId: "0x1234567890abcdef...",
2611
+ * choices: [1]
2612
+ * });
2613
+ *
2614
+ * // Watch status changes in real-time
2615
+ * for await (const statusInfo of sdk.watchVoteStatus(voteResult.processId, voteResult.voteId)) {
2616
+ * console.log(`Vote status: ${statusInfo.status}`);
2617
+ *
2618
+ * switch (statusInfo.status) {
2619
+ * case VoteStatus.Pending:
2620
+ * console.log("⏳ Processing...");
2621
+ * break;
2622
+ * case VoteStatus.Verified:
2623
+ * console.log("✓ Vote verified");
2624
+ * break;
2625
+ * case VoteStatus.Aggregated:
2626
+ * console.log("📊 Vote aggregated");
2627
+ * break;
2628
+ * case VoteStatus.Settled:
2629
+ * console.log("✅ Vote settled");
2630
+ * break;
2631
+ * }
2632
+ * }
2633
+ * ```
2634
+ */
2635
+ watchVoteStatus(processId, voteId, options) {
2636
+ if (!this.initialized) {
2637
+ throw new Error("SDK must be initialized before watching vote status. Call sdk.init() first.");
2638
+ }
2639
+ return this.voteOrchestrator.watchVoteStatus(processId, voteId, options);
2640
+ }
1887
2641
  /**
1888
2642
  * Wait for a vote to reach a specific status.
1889
- * Useful for waiting for vote confirmation and processing.
2643
+ * This is a simpler alternative to watchVoteStatus() that returns only the final status.
2644
+ * Useful for waiting for vote confirmation and processing without needing to handle each intermediate status.
2645
+ *
2646
+ * Does NOT require a provider - uses API calls only.
1890
2647
  *
1891
2648
  * @param processId - The process ID
1892
2649
  * @param voteId - The vote ID
@@ -1900,15 +2657,14 @@ class DavinciSDK {
1900
2657
  * // Submit vote and wait for it to be settled
1901
2658
  * const voteResult = await sdk.submitVote({
1902
2659
  * processId: "0x1234567890abcdef...",
1903
- * choices: [1],
1904
- * voterKey: "0x..."
2660
+ * choices: [1]
1905
2661
  * });
1906
2662
  *
1907
2663
  * // Wait for the vote to be fully processed
1908
2664
  * const finalStatus = await sdk.waitForVoteStatus(
1909
2665
  * voteResult.processId,
1910
2666
  * voteResult.voteId,
1911
- * "settled", // Wait until vote is settled
2667
+ * VoteStatus.Settled, // Wait until vote is settled
1912
2668
  * 300000, // 5 minute timeout
1913
2669
  * 5000 // Check every 5 seconds
1914
2670
  * );
@@ -1922,6 +2678,266 @@ class DavinciSDK {
1922
2678
  }
1923
2679
  return this.voteOrchestrator.waitForVoteStatus(processId, voteId, targetStatus, timeoutMs, pollIntervalMs);
1924
2680
  }
2681
+ /**
2682
+ * Ends a voting process by setting its status to ENDED and returns an async generator
2683
+ * that yields transaction status events. This method allows you to monitor the
2684
+ * transaction progress in real-time.
2685
+ *
2686
+ * Requires a signer with a provider for blockchain interactions.
2687
+ *
2688
+ * @param processId - The process ID to end
2689
+ * @returns AsyncGenerator yielding transaction status events
2690
+ * @throws Error if signer does not have a provider
2691
+ *
2692
+ * @example
2693
+ * ```typescript
2694
+ * const stream = sdk.endProcessStream("0x1234567890abcdef...");
2695
+ *
2696
+ * for await (const event of stream) {
2697
+ * switch (event.status) {
2698
+ * case TxStatus.Pending:
2699
+ * console.log("Transaction pending:", event.hash);
2700
+ * break;
2701
+ * case TxStatus.Completed:
2702
+ * console.log("Process ended successfully");
2703
+ * break;
2704
+ * case TxStatus.Failed:
2705
+ * console.error("Transaction failed:", event.error);
2706
+ * break;
2707
+ * case TxStatus.Reverted:
2708
+ * console.error("Transaction reverted:", event.reason);
2709
+ * break;
2710
+ * }
2711
+ * }
2712
+ * ```
2713
+ */
2714
+ endProcessStream(processId) {
2715
+ if (!this.initialized) {
2716
+ throw new Error("SDK must be initialized before ending processes. Call sdk.init() first.");
2717
+ }
2718
+ this.ensureProvider();
2719
+ return this.processOrchestrator.endProcessStream(processId);
2720
+ }
2721
+ /**
2722
+ * Ends a voting process by setting its status to ENDED.
2723
+ * This is the simplified method that waits for transaction completion.
2724
+ *
2725
+ * For real-time transaction status updates, use endProcessStream() instead.
2726
+ *
2727
+ * Requires a signer with a provider for blockchain interactions.
2728
+ *
2729
+ * @param processId - The process ID to end
2730
+ * @returns Promise resolving when the process is ended
2731
+ * @throws Error if signer does not have a provider
2732
+ *
2733
+ * @example
2734
+ * ```typescript
2735
+ * await sdk.endProcess("0x1234567890abcdef...");
2736
+ * console.log("Process ended successfully");
2737
+ * ```
2738
+ */
2739
+ async endProcess(processId) {
2740
+ if (!this.initialized) {
2741
+ throw new Error("SDK must be initialized before ending processes. Call sdk.init() first.");
2742
+ }
2743
+ this.ensureProvider();
2744
+ return this.processOrchestrator.endProcess(processId);
2745
+ }
2746
+ /**
2747
+ * Pauses a voting process by setting its status to PAUSED and returns an async generator
2748
+ * that yields transaction status events. This method allows you to monitor the
2749
+ * transaction progress in real-time.
2750
+ *
2751
+ * Requires a signer with a provider for blockchain interactions.
2752
+ *
2753
+ * @param processId - The process ID to pause
2754
+ * @returns AsyncGenerator yielding transaction status events
2755
+ * @throws Error if signer does not have a provider
2756
+ *
2757
+ * @example
2758
+ * ```typescript
2759
+ * const stream = sdk.pauseProcessStream("0x1234567890abcdef...");
2760
+ *
2761
+ * for await (const event of stream) {
2762
+ * switch (event.status) {
2763
+ * case TxStatus.Pending:
2764
+ * console.log("Transaction pending:", event.hash);
2765
+ * break;
2766
+ * case TxStatus.Completed:
2767
+ * console.log("Process paused successfully");
2768
+ * break;
2769
+ * case TxStatus.Failed:
2770
+ * console.error("Transaction failed:", event.error);
2771
+ * break;
2772
+ * case TxStatus.Reverted:
2773
+ * console.error("Transaction reverted:", event.reason);
2774
+ * break;
2775
+ * }
2776
+ * }
2777
+ * ```
2778
+ */
2779
+ pauseProcessStream(processId) {
2780
+ if (!this.initialized) {
2781
+ throw new Error("SDK must be initialized before pausing processes. Call sdk.init() first.");
2782
+ }
2783
+ this.ensureProvider();
2784
+ return this.processOrchestrator.pauseProcessStream(processId);
2785
+ }
2786
+ /**
2787
+ * Pauses a voting process by setting its status to PAUSED.
2788
+ * This is the simplified method that waits for transaction completion.
2789
+ *
2790
+ * For real-time transaction status updates, use pauseProcessStream() instead.
2791
+ *
2792
+ * Requires a signer with a provider for blockchain interactions.
2793
+ *
2794
+ * @param processId - The process ID to pause
2795
+ * @returns Promise resolving when the process is paused
2796
+ * @throws Error if signer does not have a provider
2797
+ *
2798
+ * @example
2799
+ * ```typescript
2800
+ * await sdk.pauseProcess("0x1234567890abcdef...");
2801
+ * console.log("Process paused successfully");
2802
+ * ```
2803
+ */
2804
+ async pauseProcess(processId) {
2805
+ if (!this.initialized) {
2806
+ throw new Error("SDK must be initialized before pausing processes. Call sdk.init() first.");
2807
+ }
2808
+ this.ensureProvider();
2809
+ return this.processOrchestrator.pauseProcess(processId);
2810
+ }
2811
+ /**
2812
+ * Cancels a voting process by setting its status to CANCELED and returns an async generator
2813
+ * that yields transaction status events. This method allows you to monitor the
2814
+ * transaction progress in real-time.
2815
+ *
2816
+ * Requires a signer with a provider for blockchain interactions.
2817
+ *
2818
+ * @param processId - The process ID to cancel
2819
+ * @returns AsyncGenerator yielding transaction status events
2820
+ * @throws Error if signer does not have a provider
2821
+ *
2822
+ * @example
2823
+ * ```typescript
2824
+ * const stream = sdk.cancelProcessStream("0x1234567890abcdef...");
2825
+ *
2826
+ * for await (const event of stream) {
2827
+ * switch (event.status) {
2828
+ * case TxStatus.Pending:
2829
+ * console.log("Transaction pending:", event.hash);
2830
+ * break;
2831
+ * case TxStatus.Completed:
2832
+ * console.log("Process canceled successfully");
2833
+ * break;
2834
+ * case TxStatus.Failed:
2835
+ * console.error("Transaction failed:", event.error);
2836
+ * break;
2837
+ * case TxStatus.Reverted:
2838
+ * console.error("Transaction reverted:", event.reason);
2839
+ * break;
2840
+ * }
2841
+ * }
2842
+ * ```
2843
+ */
2844
+ cancelProcessStream(processId) {
2845
+ if (!this.initialized) {
2846
+ throw new Error("SDK must be initialized before canceling processes. Call sdk.init() first.");
2847
+ }
2848
+ this.ensureProvider();
2849
+ return this.processOrchestrator.cancelProcessStream(processId);
2850
+ }
2851
+ /**
2852
+ * Cancels a voting process by setting its status to CANCELED.
2853
+ * This is the simplified method that waits for transaction completion.
2854
+ *
2855
+ * For real-time transaction status updates, use cancelProcessStream() instead.
2856
+ *
2857
+ * Requires a signer with a provider for blockchain interactions.
2858
+ *
2859
+ * @param processId - The process ID to cancel
2860
+ * @returns Promise resolving when the process is canceled
2861
+ * @throws Error if signer does not have a provider
2862
+ *
2863
+ * @example
2864
+ * ```typescript
2865
+ * await sdk.cancelProcess("0x1234567890abcdef...");
2866
+ * console.log("Process canceled successfully");
2867
+ * ```
2868
+ */
2869
+ async cancelProcess(processId) {
2870
+ if (!this.initialized) {
2871
+ throw new Error("SDK must be initialized before canceling processes. Call sdk.init() first.");
2872
+ }
2873
+ this.ensureProvider();
2874
+ return this.processOrchestrator.cancelProcess(processId);
2875
+ }
2876
+ /**
2877
+ * Resumes a voting process by setting its status to READY and returns an async generator
2878
+ * that yields transaction status events. This is typically used to resume a paused process.
2879
+ *
2880
+ * Requires a signer with a provider for blockchain interactions.
2881
+ *
2882
+ * @param processId - The process ID to resume
2883
+ * @returns AsyncGenerator yielding transaction status events
2884
+ * @throws Error if signer does not have a provider
2885
+ *
2886
+ * @example
2887
+ * ```typescript
2888
+ * const stream = sdk.resumeProcessStream("0x1234567890abcdef...");
2889
+ *
2890
+ * for await (const event of stream) {
2891
+ * switch (event.status) {
2892
+ * case TxStatus.Pending:
2893
+ * console.log("Transaction pending:", event.hash);
2894
+ * break;
2895
+ * case TxStatus.Completed:
2896
+ * console.log("Process resumed successfully");
2897
+ * break;
2898
+ * case TxStatus.Failed:
2899
+ * console.error("Transaction failed:", event.error);
2900
+ * break;
2901
+ * case TxStatus.Reverted:
2902
+ * console.error("Transaction reverted:", event.reason);
2903
+ * break;
2904
+ * }
2905
+ * }
2906
+ * ```
2907
+ */
2908
+ resumeProcessStream(processId) {
2909
+ if (!this.initialized) {
2910
+ throw new Error("SDK must be initialized before resuming processes. Call sdk.init() first.");
2911
+ }
2912
+ this.ensureProvider();
2913
+ return this.processOrchestrator.resumeProcessStream(processId);
2914
+ }
2915
+ /**
2916
+ * Resumes a voting process by setting its status to READY.
2917
+ * This is typically used to resume a paused process.
2918
+ * This is the simplified method that waits for transaction completion.
2919
+ *
2920
+ * For real-time transaction status updates, use resumeProcessStream() instead.
2921
+ *
2922
+ * Requires a signer with a provider for blockchain interactions.
2923
+ *
2924
+ * @param processId - The process ID to resume
2925
+ * @returns Promise resolving when the process is resumed
2926
+ * @throws Error if signer does not have a provider
2927
+ *
2928
+ * @example
2929
+ * ```typescript
2930
+ * await sdk.resumeProcess("0x1234567890abcdef...");
2931
+ * console.log("Process resumed successfully");
2932
+ * ```
2933
+ */
2934
+ async resumeProcess(processId) {
2935
+ if (!this.initialized) {
2936
+ throw new Error("SDK must be initialized before resuming processes. Call sdk.init() first.");
2937
+ }
2938
+ this.ensureProvider();
2939
+ return this.processOrchestrator.resumeProcess(processId);
2940
+ }
1925
2941
  /**
1926
2942
  * Resolve contract address based on configuration priority:
1927
2943
  * 1. If useSequencerAddresses is true: addresses from sequencer (highest priority)
@@ -1996,6 +3012,18 @@ class DavinciSDK {
1996
3012
  isInitialized() {
1997
3013
  return this.initialized;
1998
3014
  }
3015
+ /**
3016
+ * Ensures that the signer has a provider for blockchain operations.
3017
+ * @throws Error if the signer does not have a provider
3018
+ * @private
3019
+ */
3020
+ ensureProvider() {
3021
+ if (!this.config.signer.provider) {
3022
+ throw new Error(
3023
+ "Provider required for blockchain operations (process/organization management). The signer must be connected to a provider. Use wallet.connect(provider) or a browser signer like MetaMask. Note: Voting operations do not require a provider."
3024
+ );
3025
+ }
3026
+ }
1999
3027
  }
2000
3028
 
2001
3029
  exports.BaseService = BaseService;