@vocdoni/davinci-sdk 0.0.1 → 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/README.md +297 -43
- package/dist/contracts.d.ts +65 -54
- package/dist/index.d.ts +1316 -194
- package/dist/index.js +1560 -300
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1554 -294
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +3148 -1888
- package/dist/sequencer.d.ts +2 -2
- package/package.json +3 -3
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(
|
|
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(
|
|
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,96 +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: "0x40939Ec9FD872eb79A1723B559572dfD71a05d11",
|
|
404
|
-
uzh: "0x69B16f67Bd2fB18bD720379E9C1Ef5EaD3872d67",
|
|
405
|
-
mainnet: "0x0"
|
|
406
|
-
};
|
|
407
|
-
var organizationRegistry = {
|
|
408
|
-
sepolia: "0xe7136ED5a7b0e995A8fe35d8B1B815E4160cB491",
|
|
409
|
-
uzh: "0xf7BCE4546805547bE526Ca864d6722Ed193E51Aa",
|
|
410
|
-
mainnet: "0x0"
|
|
411
|
-
};
|
|
412
|
-
var stateTransitionVerifierGroth16 = {
|
|
413
|
-
sepolia: "0xb7A142D24b9220eCBC4f7fcB89Ee952a6C7E332a",
|
|
414
|
-
uzh: "0x5e4673CD378F05cc3Ae25804539c91E711548741",
|
|
415
|
-
mainnet: "0x0"
|
|
416
|
-
};
|
|
417
|
-
var resultsVerifierGroth16 = {
|
|
418
|
-
sepolia: "0x1188cEbB56ecc90e2bAe5c914274C81Fe1a22e67",
|
|
419
|
-
uzh: "0x00c7F87731346F592197E49A90Ad6EC236Ad9985",
|
|
420
|
-
mainnet: "0x0"
|
|
421
|
-
};
|
|
422
|
-
var sequencerRegistry = {
|
|
423
|
-
sepolia: "0x0",
|
|
424
|
-
uzh: "0x0",
|
|
425
|
-
mainnet: "0x0"
|
|
426
|
-
};
|
|
427
|
-
var addressesJson = {
|
|
428
|
-
processRegistry: processRegistry,
|
|
429
|
-
organizationRegistry: organizationRegistry,
|
|
430
|
-
stateTransitionVerifierGroth16: stateTransitionVerifierGroth16,
|
|
431
|
-
resultsVerifierGroth16: resultsVerifierGroth16,
|
|
432
|
-
sequencerRegistry: sequencerRegistry
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
const deployedAddresses = addressesJson;
|
|
436
625
|
var TxStatus = /* @__PURE__ */ ((TxStatus2) => {
|
|
437
626
|
TxStatus2["Pending"] = "pending";
|
|
438
627
|
TxStatus2["Completed"] = "completed";
|
|
@@ -441,23 +630,29 @@ var TxStatus = /* @__PURE__ */ ((TxStatus2) => {
|
|
|
441
630
|
return TxStatus2;
|
|
442
631
|
})(TxStatus || {});
|
|
443
632
|
class SmartContractService {
|
|
633
|
+
constructor() {
|
|
634
|
+
/** Active polling intervals for event listeners using fallback mode */
|
|
635
|
+
this.pollingIntervals = [];
|
|
636
|
+
/** Default polling interval in milliseconds for event listener fallback */
|
|
637
|
+
this.eventPollingInterval = 5e3;
|
|
638
|
+
}
|
|
444
639
|
/**
|
|
445
640
|
* Sends a transaction and yields status events during its lifecycle.
|
|
446
641
|
* This method handles the complete transaction flow from submission to completion,
|
|
447
642
|
* including error handling and status updates.
|
|
448
|
-
*
|
|
643
|
+
*
|
|
449
644
|
* @template T - The type of the successful response data
|
|
450
645
|
* @param txPromise - Promise resolving to the transaction response
|
|
451
646
|
* @param responseHandler - Function to process the successful transaction result
|
|
452
647
|
* @returns AsyncGenerator yielding transaction status events
|
|
453
|
-
*
|
|
648
|
+
*
|
|
454
649
|
* @example
|
|
455
650
|
* ```typescript
|
|
456
651
|
* const txStream = await this.sendTx(
|
|
457
652
|
* contract.someMethod(),
|
|
458
653
|
* async () => await contract.getUpdatedValue()
|
|
459
654
|
* );
|
|
460
|
-
*
|
|
655
|
+
*
|
|
461
656
|
* for await (const event of txStream) {
|
|
462
657
|
* switch (event.status) {
|
|
463
658
|
* case TxStatus.Pending:
|
|
@@ -494,12 +689,12 @@ class SmartContractService {
|
|
|
494
689
|
* Executes a transaction stream and returns the result or throws an error.
|
|
495
690
|
* This is a convenience method that processes a transaction stream and either
|
|
496
691
|
* returns the successful result or throws an appropriate error.
|
|
497
|
-
*
|
|
692
|
+
*
|
|
498
693
|
* @template T - The type of the successful response data
|
|
499
694
|
* @param stream - AsyncGenerator of transaction status events
|
|
500
695
|
* @returns Promise resolving to the successful response data
|
|
501
696
|
* @throws Error if the transaction fails or reverts
|
|
502
|
-
*
|
|
697
|
+
*
|
|
503
698
|
* @example
|
|
504
699
|
* ```typescript
|
|
505
700
|
* try {
|
|
@@ -529,11 +724,11 @@ class SmartContractService {
|
|
|
529
724
|
* Normalizes event listener arguments between different ethers.js versions.
|
|
530
725
|
* This helper method ensures consistent event argument handling regardless of
|
|
531
726
|
* whether the event payload follows ethers v5 or v6 format.
|
|
532
|
-
*
|
|
727
|
+
*
|
|
533
728
|
* @template Args - Tuple type representing the expected event arguments
|
|
534
729
|
* @param callback - The event callback function to normalize
|
|
535
730
|
* @returns Normalized event listener function
|
|
536
|
-
*
|
|
731
|
+
*
|
|
537
732
|
* @example
|
|
538
733
|
* ```typescript
|
|
539
734
|
* contract.on('Transfer', this.normalizeListener((from: string, to: string, amount: BigInt) => {
|
|
@@ -552,12 +747,153 @@ class SmartContractService {
|
|
|
552
747
|
callback(...args);
|
|
553
748
|
};
|
|
554
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* Sets up an event listener with automatic fallback for RPCs that don't support eth_newFilter.
|
|
752
|
+
* First attempts to use contract.on() which relies on eth_newFilter. If the RPC doesn't support
|
|
753
|
+
* this method (error code -32601), automatically falls back to polling with queryFilter.
|
|
754
|
+
*
|
|
755
|
+
* @template Args - Tuple type representing the event arguments
|
|
756
|
+
* @param contract - The contract instance to listen to
|
|
757
|
+
* @param eventFilter - The event filter to listen for
|
|
758
|
+
* @param callback - The callback function to invoke when the event occurs
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* ```typescript
|
|
762
|
+
* this.setupEventListener(
|
|
763
|
+
* this.contract,
|
|
764
|
+
* this.contract.filters.Transfer(),
|
|
765
|
+
* (from: string, to: string, amount: bigint) => {
|
|
766
|
+
* console.log(`Transfer: ${from} -> ${to}: ${amount}`);
|
|
767
|
+
* }
|
|
768
|
+
* );
|
|
769
|
+
* ```
|
|
770
|
+
*/
|
|
771
|
+
async setupEventListener(contract, eventFilter, callback) {
|
|
772
|
+
const normalizedCallback = this.normalizeListener(callback);
|
|
773
|
+
const provider = contract.runner?.provider;
|
|
774
|
+
if (!provider) {
|
|
775
|
+
console.warn("No provider available for event listeners");
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
try {
|
|
779
|
+
const testFilter = {
|
|
780
|
+
address: await contract.getAddress(),
|
|
781
|
+
topics: []
|
|
782
|
+
};
|
|
783
|
+
if ("send" in provider && typeof provider.send === "function") {
|
|
784
|
+
try {
|
|
785
|
+
const filterId = await provider.send("eth_newFilter", [testFilter]);
|
|
786
|
+
await provider.send("eth_getFilterChanges", [filterId]);
|
|
787
|
+
contract.on(eventFilter, normalizedCallback);
|
|
788
|
+
return;
|
|
789
|
+
} catch (error) {
|
|
790
|
+
if (this.isUnsupportedMethodError(error)) {
|
|
791
|
+
console.warn(
|
|
792
|
+
"RPC does not fully support eth_newFilter/eth_getFilterChanges, falling back to polling for events. This may result in delayed event notifications."
|
|
793
|
+
);
|
|
794
|
+
this.setupPollingListener(contract, eventFilter, callback);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
const errorHandler = (error) => {
|
|
800
|
+
if (this.isUnsupportedMethodError(error)) {
|
|
801
|
+
contract.off(eventFilter, normalizedCallback);
|
|
802
|
+
contract.off("error", errorHandler);
|
|
803
|
+
console.warn(
|
|
804
|
+
"RPC does not support eth_newFilter, falling back to polling for events. This may result in delayed event notifications."
|
|
805
|
+
);
|
|
806
|
+
this.setupPollingListener(contract, eventFilter, callback);
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
contract.once("error", errorHandler);
|
|
810
|
+
contract.on(eventFilter, normalizedCallback);
|
|
811
|
+
} catch (error) {
|
|
812
|
+
console.warn("Error setting up event listener, falling back to polling:", error.message);
|
|
813
|
+
this.setupPollingListener(contract, eventFilter, callback);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
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
|
+
*
|
|
822
|
+
* @param error - The error to check
|
|
823
|
+
* @returns true if the error indicates unsupported or broken filter functionality
|
|
824
|
+
*/
|
|
825
|
+
isUnsupportedMethodError(error) {
|
|
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;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Sets up a polling-based event listener as fallback when eth_newFilter is not supported.
|
|
832
|
+
* Periodically queries for new events and invokes the callback for each new event found.
|
|
833
|
+
*
|
|
834
|
+
* @template Args - Tuple type representing the event arguments
|
|
835
|
+
* @param contract - The contract instance to poll
|
|
836
|
+
* @param eventFilter - The event filter to poll for
|
|
837
|
+
* @param callback - The callback function to invoke for each event
|
|
838
|
+
*/
|
|
839
|
+
setupPollingListener(contract, eventFilter, callback) {
|
|
840
|
+
let lastProcessedBlock = 0;
|
|
841
|
+
const poll = async () => {
|
|
842
|
+
try {
|
|
843
|
+
const provider = contract.runner?.provider;
|
|
844
|
+
if (!provider) {
|
|
845
|
+
console.warn("No provider available for polling events");
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const currentBlock = await provider.getBlockNumber();
|
|
849
|
+
if (lastProcessedBlock === 0) {
|
|
850
|
+
lastProcessedBlock = currentBlock - 1;
|
|
851
|
+
}
|
|
852
|
+
if (currentBlock > lastProcessedBlock) {
|
|
853
|
+
const events = await contract.queryFilter(
|
|
854
|
+
eventFilter,
|
|
855
|
+
lastProcessedBlock + 1,
|
|
856
|
+
currentBlock
|
|
857
|
+
);
|
|
858
|
+
for (const event of events) {
|
|
859
|
+
if ("args" in event && event.args) {
|
|
860
|
+
callback(...event.args);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
lastProcessedBlock = currentBlock;
|
|
864
|
+
}
|
|
865
|
+
} catch (error) {
|
|
866
|
+
console.error("Error polling for events:", error);
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
const intervalId = setInterval(poll, this.eventPollingInterval);
|
|
870
|
+
this.pollingIntervals.push(intervalId);
|
|
871
|
+
poll();
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Clears all active polling intervals.
|
|
875
|
+
* Should be called when removing all listeners or cleaning up the service.
|
|
876
|
+
*/
|
|
877
|
+
clearPollingIntervals() {
|
|
878
|
+
for (const intervalId of this.pollingIntervals) {
|
|
879
|
+
clearInterval(intervalId);
|
|
880
|
+
}
|
|
881
|
+
this.pollingIntervals = [];
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Sets the polling interval for event listeners using the fallback mechanism.
|
|
885
|
+
*
|
|
886
|
+
* @param intervalMs - Polling interval in milliseconds
|
|
887
|
+
*/
|
|
888
|
+
setEventPollingInterval(intervalMs) {
|
|
889
|
+
this.eventPollingInterval = intervalMs;
|
|
890
|
+
}
|
|
555
891
|
}
|
|
556
892
|
|
|
557
893
|
class ContractServiceError extends Error {
|
|
558
894
|
/**
|
|
559
895
|
* Creates a new ContractServiceError instance.
|
|
560
|
-
*
|
|
896
|
+
*
|
|
561
897
|
* @param message - The error message describing what went wrong
|
|
562
898
|
* @param operation - The operation that was being performed when the error occurred
|
|
563
899
|
*/
|
|
@@ -710,7 +1046,7 @@ class ProcessRegistryService extends SmartContractService {
|
|
|
710
1046
|
}
|
|
711
1047
|
/**
|
|
712
1048
|
* Sets the results for a voting process.
|
|
713
|
-
*
|
|
1049
|
+
*
|
|
714
1050
|
* @param processID - The ID of the process to set results for
|
|
715
1051
|
* @param proof - Zero-knowledge proof validating the results
|
|
716
1052
|
* @param input - Input data for the proof verification
|
|
@@ -718,11 +1054,7 @@ class ProcessRegistryService extends SmartContractService {
|
|
|
718
1054
|
*/
|
|
719
1055
|
setProcessResults(processID, proof, input) {
|
|
720
1056
|
return this.sendTx(
|
|
721
|
-
this.contract.setProcessResults(
|
|
722
|
-
processID,
|
|
723
|
-
proof,
|
|
724
|
-
input
|
|
725
|
-
).catch((e) => {
|
|
1057
|
+
this.contract.setProcessResults(processID, proof, input).catch((e) => {
|
|
726
1058
|
throw new ProcessResultError(e.message, "setResults");
|
|
727
1059
|
}),
|
|
728
1060
|
async () => ({ success: true })
|
|
@@ -730,43 +1062,50 @@ class ProcessRegistryService extends SmartContractService {
|
|
|
730
1062
|
}
|
|
731
1063
|
// ─── EVENT LISTENERS ───────────────────────────────────────────────────────
|
|
732
1064
|
onProcessCreated(cb) {
|
|
733
|
-
this.
|
|
1065
|
+
this.setupEventListener(
|
|
1066
|
+
this.contract,
|
|
734
1067
|
this.contract.filters.ProcessCreated(),
|
|
735
|
-
|
|
736
|
-
);
|
|
1068
|
+
cb
|
|
1069
|
+
).catch((err) => console.error("Error setting up ProcessCreated listener:", err));
|
|
737
1070
|
}
|
|
738
1071
|
onProcessStatusChanged(cb) {
|
|
739
|
-
this.
|
|
1072
|
+
this.setupEventListener(
|
|
1073
|
+
this.contract,
|
|
740
1074
|
this.contract.filters.ProcessStatusChanged(),
|
|
741
|
-
|
|
742
|
-
);
|
|
1075
|
+
cb
|
|
1076
|
+
).catch((err) => console.error("Error setting up ProcessStatusChanged listener:", err));
|
|
743
1077
|
}
|
|
744
1078
|
onCensusUpdated(cb) {
|
|
745
|
-
this.
|
|
1079
|
+
this.setupEventListener(
|
|
1080
|
+
this.contract,
|
|
746
1081
|
this.contract.filters.CensusUpdated(),
|
|
747
|
-
|
|
748
|
-
);
|
|
1082
|
+
cb
|
|
1083
|
+
).catch((err) => console.error("Error setting up CensusUpdated listener:", err));
|
|
749
1084
|
}
|
|
750
1085
|
onProcessDurationChanged(cb) {
|
|
751
|
-
this.
|
|
1086
|
+
this.setupEventListener(
|
|
1087
|
+
this.contract,
|
|
752
1088
|
this.contract.filters.ProcessDurationChanged(),
|
|
753
|
-
|
|
754
|
-
);
|
|
1089
|
+
cb
|
|
1090
|
+
).catch((err) => console.error("Error setting up ProcessDurationChanged listener:", err));
|
|
755
1091
|
}
|
|
756
1092
|
onStateRootUpdated(cb) {
|
|
757
|
-
this.
|
|
1093
|
+
this.setupEventListener(
|
|
1094
|
+
this.contract,
|
|
758
1095
|
this.contract.filters.ProcessStateRootUpdated(),
|
|
759
|
-
|
|
760
|
-
);
|
|
1096
|
+
cb
|
|
1097
|
+
).catch((err) => console.error("Error setting up StateRootUpdated listener:", err));
|
|
761
1098
|
}
|
|
762
1099
|
onProcessResultsSet(cb) {
|
|
763
|
-
this.
|
|
1100
|
+
this.setupEventListener(
|
|
1101
|
+
this.contract,
|
|
764
1102
|
this.contract.filters.ProcessResultsSet(),
|
|
765
|
-
|
|
766
|
-
);
|
|
1103
|
+
cb
|
|
1104
|
+
).catch((err) => console.error("Error setting up ProcessResultsSet listener:", err));
|
|
767
1105
|
}
|
|
768
1106
|
removeAllListeners() {
|
|
769
1107
|
this.contract.removeAllListeners();
|
|
1108
|
+
this.clearPollingIntervals();
|
|
770
1109
|
}
|
|
771
1110
|
}
|
|
772
1111
|
|
|
@@ -777,6 +1116,34 @@ class ProcessOrchestrationService {
|
|
|
777
1116
|
this.organizationRegistry = organizationRegistry;
|
|
778
1117
|
this.getCrypto = getCrypto;
|
|
779
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;
|
|
780
1147
|
}
|
|
781
1148
|
/**
|
|
782
1149
|
* Gets user-friendly process information by transforming raw contract data
|
|
@@ -850,14 +1217,119 @@ class ProcessOrchestrationService {
|
|
|
850
1217
|
};
|
|
851
1218
|
}
|
|
852
1219
|
/**
|
|
853
|
-
* Creates a complete voting process
|
|
854
|
-
* This method
|
|
1220
|
+
* Creates a complete voting process and returns an async generator that yields transaction status events.
|
|
1221
|
+
* This method allows you to monitor the transaction progress in real-time.
|
|
1222
|
+
*
|
|
1223
|
+
* @param config - Process configuration
|
|
1224
|
+
* @returns AsyncGenerator yielding transaction status events with ProcessCreationResult
|
|
1225
|
+
*
|
|
1226
|
+
* @example
|
|
1227
|
+
* ```typescript
|
|
1228
|
+
* const stream = sdk.createProcessStream({
|
|
1229
|
+
* title: "My Election",
|
|
1230
|
+
* description: "A simple election",
|
|
1231
|
+
* census: { ... },
|
|
1232
|
+
* ballot: { ... },
|
|
1233
|
+
* timing: { ... },
|
|
1234
|
+
* questions: [ ... ]
|
|
1235
|
+
* });
|
|
1236
|
+
*
|
|
1237
|
+
* for await (const event of stream) {
|
|
1238
|
+
* switch (event.status) {
|
|
1239
|
+
* case "pending":
|
|
1240
|
+
* console.log("Transaction pending:", event.hash);
|
|
1241
|
+
* break;
|
|
1242
|
+
* case "completed":
|
|
1243
|
+
* console.log("Process created:", event.response.processId);
|
|
1244
|
+
* console.log("Transaction hash:", event.response.transactionHash);
|
|
1245
|
+
* break;
|
|
1246
|
+
* case "failed":
|
|
1247
|
+
* console.error("Transaction failed:", event.error);
|
|
1248
|
+
* break;
|
|
1249
|
+
* case "reverted":
|
|
1250
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1251
|
+
* break;
|
|
1252
|
+
* }
|
|
1253
|
+
* }
|
|
1254
|
+
* ```
|
|
1255
|
+
*/
|
|
1256
|
+
async *createProcessStream(config) {
|
|
1257
|
+
const data = await this.prepareProcessCreation(config);
|
|
1258
|
+
const encryptionKey = {
|
|
1259
|
+
x: data.sequencerResult.encryptionPubKey[0],
|
|
1260
|
+
y: data.sequencerResult.encryptionPubKey[1]
|
|
1261
|
+
};
|
|
1262
|
+
const txStream = this.processRegistry.newProcess(
|
|
1263
|
+
ProcessStatus.READY,
|
|
1264
|
+
data.startTime,
|
|
1265
|
+
data.duration,
|
|
1266
|
+
data.ballotMode,
|
|
1267
|
+
data.census,
|
|
1268
|
+
data.metadataUri,
|
|
1269
|
+
encryptionKey,
|
|
1270
|
+
BigInt(data.sequencerResult.stateRoot)
|
|
1271
|
+
);
|
|
1272
|
+
let transactionHash = "unknown";
|
|
1273
|
+
for await (const event of txStream) {
|
|
1274
|
+
if (event.status === TxStatus.Pending) {
|
|
1275
|
+
transactionHash = event.hash;
|
|
1276
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1277
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1278
|
+
yield {
|
|
1279
|
+
status: TxStatus.Completed,
|
|
1280
|
+
response: {
|
|
1281
|
+
processId: data.processId,
|
|
1282
|
+
transactionHash
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
break;
|
|
1286
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1287
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1288
|
+
break;
|
|
1289
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1290
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1291
|
+
break;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Creates a complete voting process with minimal configuration.
|
|
1297
|
+
* This is the ultra-easy method for end users that handles all the complex orchestration internally.
|
|
1298
|
+
*
|
|
1299
|
+
* For real-time transaction status updates, use createProcessStream() instead.
|
|
1300
|
+
*
|
|
1301
|
+
* The method automatically:
|
|
1302
|
+
* - Gets encryption keys and initial state root from the sequencer
|
|
1303
|
+
* - Handles process creation signatures
|
|
1304
|
+
* - Coordinates between sequencer API and on-chain contract calls
|
|
1305
|
+
* - Creates and pushes metadata
|
|
1306
|
+
* - Submits the on-chain transaction
|
|
1307
|
+
*
|
|
1308
|
+
* @param config - Simplified process configuration
|
|
1309
|
+
* @returns Promise resolving to the process creation result
|
|
855
1310
|
*/
|
|
856
1311
|
async createProcess(config) {
|
|
1312
|
+
for await (const event of this.createProcessStream(config)) {
|
|
1313
|
+
if (event.status === "completed") {
|
|
1314
|
+
return event.response;
|
|
1315
|
+
} else if (event.status === "failed") {
|
|
1316
|
+
throw event.error;
|
|
1317
|
+
} else if (event.status === "reverted") {
|
|
1318
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
throw new Error("Process creation stream ended unexpectedly");
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Prepares all data needed for process creation
|
|
1325
|
+
* @private
|
|
1326
|
+
*/
|
|
1327
|
+
async prepareProcessCreation(config) {
|
|
857
1328
|
const { startTime, duration } = this.calculateTiming(config.timing);
|
|
858
1329
|
const signerAddress = await this.signer.getAddress();
|
|
859
1330
|
const processId = await this.processRegistry.getNextProcessId(signerAddress);
|
|
860
|
-
const
|
|
1331
|
+
const censusConfig = await this.handleCensus(config.census);
|
|
1332
|
+
const censusRoot = censusConfig.root;
|
|
861
1333
|
const ballotMode = config.ballot;
|
|
862
1334
|
const metadata = this.createMetadata(config);
|
|
863
1335
|
const metadataHash = await this.apiService.sequencer.pushMetadata(metadata);
|
|
@@ -868,43 +1340,23 @@ class ProcessOrchestrationService {
|
|
|
868
1340
|
censusRoot,
|
|
869
1341
|
ballotMode,
|
|
870
1342
|
signature,
|
|
871
|
-
censusOrigin:
|
|
1343
|
+
censusOrigin: censusConfig.type
|
|
872
1344
|
});
|
|
873
1345
|
const census = {
|
|
874
|
-
censusOrigin:
|
|
875
|
-
maxVotes:
|
|
1346
|
+
censusOrigin: censusConfig.type,
|
|
1347
|
+
maxVotes: censusConfig.size.toString(),
|
|
876
1348
|
censusRoot,
|
|
877
|
-
censusURI:
|
|
1349
|
+
censusURI: censusConfig.uri
|
|
878
1350
|
};
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
y: sequencerResult.encryptionPubKey[1]
|
|
882
|
-
};
|
|
883
|
-
const txStream = this.processRegistry.newProcess(
|
|
884
|
-
ProcessStatus.READY,
|
|
1351
|
+
return {
|
|
1352
|
+
processId,
|
|
885
1353
|
startTime,
|
|
886
1354
|
duration,
|
|
1355
|
+
censusRoot,
|
|
887
1356
|
ballotMode,
|
|
888
|
-
census,
|
|
889
1357
|
metadataUri,
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
);
|
|
893
|
-
let transactionHash = "unknown";
|
|
894
|
-
for await (const event of txStream) {
|
|
895
|
-
if (event.status === "pending") {
|
|
896
|
-
transactionHash = event.hash;
|
|
897
|
-
} else if (event.status === "completed") {
|
|
898
|
-
break;
|
|
899
|
-
} else if (event.status === "failed") {
|
|
900
|
-
throw event.error;
|
|
901
|
-
} else if (event.status === "reverted") {
|
|
902
|
-
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
return {
|
|
906
|
-
processId,
|
|
907
|
-
transactionHash
|
|
1358
|
+
sequencerResult,
|
|
1359
|
+
census
|
|
908
1360
|
};
|
|
909
1361
|
}
|
|
910
1362
|
/**
|
|
@@ -970,17 +1422,323 @@ class ProcessOrchestrationService {
|
|
|
970
1422
|
if (!config.questions || config.questions.length === 0) {
|
|
971
1423
|
throw new Error("Questions are required. Please provide at least one question with choices.");
|
|
972
1424
|
}
|
|
973
|
-
metadata.questions = config.questions.map((q) => ({
|
|
974
|
-
title: { default: q.title },
|
|
975
|
-
description: { default: q.description || "" },
|
|
976
|
-
meta: {},
|
|
977
|
-
choices: q.choices.map((c) => ({
|
|
978
|
-
title: { default: c.title },
|
|
979
|
-
value: c.value,
|
|
980
|
-
meta: {}
|
|
981
|
-
}))
|
|
982
|
-
}));
|
|
983
|
-
return metadata;
|
|
1425
|
+
metadata.questions = config.questions.map((q) => ({
|
|
1426
|
+
title: { default: q.title },
|
|
1427
|
+
description: { default: q.description || "" },
|
|
1428
|
+
meta: {},
|
|
1429
|
+
choices: q.choices.map((c) => ({
|
|
1430
|
+
title: { default: c.title },
|
|
1431
|
+
value: c.value,
|
|
1432
|
+
meta: {}
|
|
1433
|
+
}))
|
|
1434
|
+
}));
|
|
1435
|
+
return metadata;
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Ends a voting process by setting its status to ENDED.
|
|
1439
|
+
* Returns an async generator that yields transaction status events.
|
|
1440
|
+
*
|
|
1441
|
+
* @param processId - The process ID to end
|
|
1442
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
1443
|
+
*
|
|
1444
|
+
* @example
|
|
1445
|
+
* ```typescript
|
|
1446
|
+
* const stream = sdk.endProcessStream("0x1234567890abcdef...");
|
|
1447
|
+
*
|
|
1448
|
+
* for await (const event of stream) {
|
|
1449
|
+
* switch (event.status) {
|
|
1450
|
+
* case "pending":
|
|
1451
|
+
* console.log("Transaction pending:", event.hash);
|
|
1452
|
+
* break;
|
|
1453
|
+
* case "completed":
|
|
1454
|
+
* console.log("Process ended successfully");
|
|
1455
|
+
* break;
|
|
1456
|
+
* case "failed":
|
|
1457
|
+
* console.error("Transaction failed:", event.error);
|
|
1458
|
+
* break;
|
|
1459
|
+
* case "reverted":
|
|
1460
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1461
|
+
* break;
|
|
1462
|
+
* }
|
|
1463
|
+
* }
|
|
1464
|
+
* ```
|
|
1465
|
+
*/
|
|
1466
|
+
async *endProcessStream(processId) {
|
|
1467
|
+
const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.ENDED);
|
|
1468
|
+
for await (const event of txStream) {
|
|
1469
|
+
if (event.status === TxStatus.Pending) {
|
|
1470
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1471
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1472
|
+
yield {
|
|
1473
|
+
status: TxStatus.Completed,
|
|
1474
|
+
response: { success: true }
|
|
1475
|
+
};
|
|
1476
|
+
break;
|
|
1477
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1478
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1479
|
+
break;
|
|
1480
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1481
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1482
|
+
break;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Ends a voting process by setting its status to ENDED.
|
|
1488
|
+
* This is a simplified method that waits for transaction completion.
|
|
1489
|
+
*
|
|
1490
|
+
* For real-time transaction status updates, use endProcessStream() instead.
|
|
1491
|
+
*
|
|
1492
|
+
* @param processId - The process ID to end
|
|
1493
|
+
* @returns Promise resolving when the process is ended
|
|
1494
|
+
*
|
|
1495
|
+
* @example
|
|
1496
|
+
* ```typescript
|
|
1497
|
+
* await sdk.endProcess("0x1234567890abcdef...");
|
|
1498
|
+
* console.log("Process ended successfully");
|
|
1499
|
+
* ```
|
|
1500
|
+
*/
|
|
1501
|
+
async endProcess(processId) {
|
|
1502
|
+
for await (const event of this.endProcessStream(processId)) {
|
|
1503
|
+
if (event.status === "completed") {
|
|
1504
|
+
return;
|
|
1505
|
+
} else if (event.status === "failed") {
|
|
1506
|
+
throw event.error;
|
|
1507
|
+
} else if (event.status === "reverted") {
|
|
1508
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
throw new Error("End process stream ended unexpectedly");
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Pauses a voting process by setting its status to PAUSED.
|
|
1515
|
+
* Returns an async generator that yields transaction status events.
|
|
1516
|
+
*
|
|
1517
|
+
* @param processId - The process ID to pause
|
|
1518
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
1519
|
+
*
|
|
1520
|
+
* @example
|
|
1521
|
+
* ```typescript
|
|
1522
|
+
* const stream = sdk.pauseProcessStream("0x1234567890abcdef...");
|
|
1523
|
+
*
|
|
1524
|
+
* for await (const event of stream) {
|
|
1525
|
+
* switch (event.status) {
|
|
1526
|
+
* case "pending":
|
|
1527
|
+
* console.log("Transaction pending:", event.hash);
|
|
1528
|
+
* break;
|
|
1529
|
+
* case "completed":
|
|
1530
|
+
* console.log("Process paused successfully");
|
|
1531
|
+
* break;
|
|
1532
|
+
* case "failed":
|
|
1533
|
+
* console.error("Transaction failed:", event.error);
|
|
1534
|
+
* break;
|
|
1535
|
+
* case "reverted":
|
|
1536
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1537
|
+
* break;
|
|
1538
|
+
* }
|
|
1539
|
+
* }
|
|
1540
|
+
* ```
|
|
1541
|
+
*/
|
|
1542
|
+
async *pauseProcessStream(processId) {
|
|
1543
|
+
const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.PAUSED);
|
|
1544
|
+
for await (const event of txStream) {
|
|
1545
|
+
if (event.status === TxStatus.Pending) {
|
|
1546
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1547
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1548
|
+
yield {
|
|
1549
|
+
status: TxStatus.Completed,
|
|
1550
|
+
response: { success: true }
|
|
1551
|
+
};
|
|
1552
|
+
break;
|
|
1553
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1554
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1555
|
+
break;
|
|
1556
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1557
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1558
|
+
break;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Pauses a voting process by setting its status to PAUSED.
|
|
1564
|
+
* This is a simplified method that waits for transaction completion.
|
|
1565
|
+
*
|
|
1566
|
+
* For real-time transaction status updates, use pauseProcessStream() instead.
|
|
1567
|
+
*
|
|
1568
|
+
* @param processId - The process ID to pause
|
|
1569
|
+
* @returns Promise resolving when the process is paused
|
|
1570
|
+
*
|
|
1571
|
+
* @example
|
|
1572
|
+
* ```typescript
|
|
1573
|
+
* await sdk.pauseProcess("0x1234567890abcdef...");
|
|
1574
|
+
* console.log("Process paused successfully");
|
|
1575
|
+
* ```
|
|
1576
|
+
*/
|
|
1577
|
+
async pauseProcess(processId) {
|
|
1578
|
+
for await (const event of this.pauseProcessStream(processId)) {
|
|
1579
|
+
if (event.status === "completed") {
|
|
1580
|
+
return;
|
|
1581
|
+
} else if (event.status === "failed") {
|
|
1582
|
+
throw event.error;
|
|
1583
|
+
} else if (event.status === "reverted") {
|
|
1584
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
throw new Error("Pause process stream ended unexpectedly");
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Cancels a voting process by setting its status to CANCELED.
|
|
1591
|
+
* Returns an async generator that yields transaction status events.
|
|
1592
|
+
*
|
|
1593
|
+
* @param processId - The process ID to cancel
|
|
1594
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
1595
|
+
*
|
|
1596
|
+
* @example
|
|
1597
|
+
* ```typescript
|
|
1598
|
+
* const stream = sdk.cancelProcessStream("0x1234567890abcdef...");
|
|
1599
|
+
*
|
|
1600
|
+
* for await (const event of stream) {
|
|
1601
|
+
* switch (event.status) {
|
|
1602
|
+
* case "pending":
|
|
1603
|
+
* console.log("Transaction pending:", event.hash);
|
|
1604
|
+
* break;
|
|
1605
|
+
* case "completed":
|
|
1606
|
+
* console.log("Process canceled successfully");
|
|
1607
|
+
* break;
|
|
1608
|
+
* case "failed":
|
|
1609
|
+
* console.error("Transaction failed:", event.error);
|
|
1610
|
+
* break;
|
|
1611
|
+
* case "reverted":
|
|
1612
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1613
|
+
* break;
|
|
1614
|
+
* }
|
|
1615
|
+
* }
|
|
1616
|
+
* ```
|
|
1617
|
+
*/
|
|
1618
|
+
async *cancelProcessStream(processId) {
|
|
1619
|
+
const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.CANCELED);
|
|
1620
|
+
for await (const event of txStream) {
|
|
1621
|
+
if (event.status === TxStatus.Pending) {
|
|
1622
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1623
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1624
|
+
yield {
|
|
1625
|
+
status: TxStatus.Completed,
|
|
1626
|
+
response: { success: true }
|
|
1627
|
+
};
|
|
1628
|
+
break;
|
|
1629
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1630
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1631
|
+
break;
|
|
1632
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1633
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Cancels a voting process by setting its status to CANCELED.
|
|
1640
|
+
* This is a simplified method that waits for transaction completion.
|
|
1641
|
+
*
|
|
1642
|
+
* For real-time transaction status updates, use cancelProcessStream() instead.
|
|
1643
|
+
*
|
|
1644
|
+
* @param processId - The process ID to cancel
|
|
1645
|
+
* @returns Promise resolving when the process is canceled
|
|
1646
|
+
*
|
|
1647
|
+
* @example
|
|
1648
|
+
* ```typescript
|
|
1649
|
+
* await sdk.cancelProcess("0x1234567890abcdef...");
|
|
1650
|
+
* console.log("Process canceled successfully");
|
|
1651
|
+
* ```
|
|
1652
|
+
*/
|
|
1653
|
+
async cancelProcess(processId) {
|
|
1654
|
+
for await (const event of this.cancelProcessStream(processId)) {
|
|
1655
|
+
if (event.status === "completed") {
|
|
1656
|
+
return;
|
|
1657
|
+
} else if (event.status === "failed") {
|
|
1658
|
+
throw event.error;
|
|
1659
|
+
} else if (event.status === "reverted") {
|
|
1660
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
throw new Error("Cancel process stream ended unexpectedly");
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Resumes a voting process by setting its status to READY.
|
|
1667
|
+
* This is typically used to resume a paused process.
|
|
1668
|
+
* Returns an async generator that yields transaction status events.
|
|
1669
|
+
*
|
|
1670
|
+
* @param processId - The process ID to resume
|
|
1671
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
1672
|
+
*
|
|
1673
|
+
* @example
|
|
1674
|
+
* ```typescript
|
|
1675
|
+
* const stream = sdk.resumeProcessStream("0x1234567890abcdef...");
|
|
1676
|
+
*
|
|
1677
|
+
* for await (const event of stream) {
|
|
1678
|
+
* switch (event.status) {
|
|
1679
|
+
* case "pending":
|
|
1680
|
+
* console.log("Transaction pending:", event.hash);
|
|
1681
|
+
* break;
|
|
1682
|
+
* case "completed":
|
|
1683
|
+
* console.log("Process resumed successfully");
|
|
1684
|
+
* break;
|
|
1685
|
+
* case "failed":
|
|
1686
|
+
* console.error("Transaction failed:", event.error);
|
|
1687
|
+
* break;
|
|
1688
|
+
* case "reverted":
|
|
1689
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1690
|
+
* break;
|
|
1691
|
+
* }
|
|
1692
|
+
* }
|
|
1693
|
+
* ```
|
|
1694
|
+
*/
|
|
1695
|
+
async *resumeProcessStream(processId) {
|
|
1696
|
+
const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.READY);
|
|
1697
|
+
for await (const event of txStream) {
|
|
1698
|
+
if (event.status === TxStatus.Pending) {
|
|
1699
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1700
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1701
|
+
yield {
|
|
1702
|
+
status: TxStatus.Completed,
|
|
1703
|
+
response: { success: true }
|
|
1704
|
+
};
|
|
1705
|
+
break;
|
|
1706
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1707
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1708
|
+
break;
|
|
1709
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1710
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1711
|
+
break;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Resumes a voting process by setting its status to READY.
|
|
1717
|
+
* This is typically used to resume a paused process.
|
|
1718
|
+
* This is a simplified method that waits for transaction completion.
|
|
1719
|
+
*
|
|
1720
|
+
* For real-time transaction status updates, use resumeProcessStream() instead.
|
|
1721
|
+
*
|
|
1722
|
+
* @param processId - The process ID to resume
|
|
1723
|
+
* @returns Promise resolving when the process is resumed
|
|
1724
|
+
*
|
|
1725
|
+
* @example
|
|
1726
|
+
* ```typescript
|
|
1727
|
+
* await sdk.resumeProcess("0x1234567890abcdef...");
|
|
1728
|
+
* console.log("Process resumed successfully");
|
|
1729
|
+
* ```
|
|
1730
|
+
*/
|
|
1731
|
+
async resumeProcess(processId) {
|
|
1732
|
+
for await (const event of this.resumeProcessStream(processId)) {
|
|
1733
|
+
if (event.status === "completed") {
|
|
1734
|
+
return;
|
|
1735
|
+
} else if (event.status === "failed") {
|
|
1736
|
+
throw event.error;
|
|
1737
|
+
} else if (event.status === "reverted") {
|
|
1738
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
throw new Error("Resume process stream ended unexpectedly");
|
|
984
1742
|
}
|
|
985
1743
|
}
|
|
986
1744
|
|
|
@@ -1051,11 +1809,7 @@ class CircomProof {
|
|
|
1051
1809
|
}
|
|
1052
1810
|
this.zkeyCache.set(zkeyUrl, zkeyBin);
|
|
1053
1811
|
}
|
|
1054
|
-
const { proof, publicSignals } = await groth16.fullProve(
|
|
1055
|
-
inputs,
|
|
1056
|
-
wasmBin,
|
|
1057
|
-
zkeyBin
|
|
1058
|
-
);
|
|
1812
|
+
const { proof, publicSignals } = await groth16.fullProve(inputs, wasmBin, zkeyBin);
|
|
1059
1813
|
return {
|
|
1060
1814
|
proof,
|
|
1061
1815
|
publicSignals
|
|
@@ -1090,11 +1844,13 @@ var VoteStatus = /* @__PURE__ */ ((VoteStatus2) => {
|
|
|
1090
1844
|
})(VoteStatus || {});
|
|
1091
1845
|
|
|
1092
1846
|
class VoteOrchestrationService {
|
|
1093
|
-
constructor(apiService, getCrypto, signer, censusProviders = {}) {
|
|
1847
|
+
constructor(apiService, getCrypto, signer, censusProviders = {}, config = {}) {
|
|
1094
1848
|
this.apiService = apiService;
|
|
1095
1849
|
this.getCrypto = getCrypto;
|
|
1096
1850
|
this.signer = signer;
|
|
1097
1851
|
this.censusProviders = censusProviders;
|
|
1852
|
+
this.verifyCircuitFiles = config.verifyCircuitFiles ?? true;
|
|
1853
|
+
this.verifyProof = config.verifyProof ?? true;
|
|
1098
1854
|
}
|
|
1099
1855
|
/**
|
|
1100
1856
|
* Submit a vote with simplified configuration
|
|
@@ -1103,7 +1859,7 @@ class VoteOrchestrationService {
|
|
|
1103
1859
|
* - Gets census proof (Merkle or CSP)
|
|
1104
1860
|
* - Generates cryptographic proofs
|
|
1105
1861
|
* - Signs and submits the vote
|
|
1106
|
-
*
|
|
1862
|
+
*
|
|
1107
1863
|
* @param config - Simplified vote configuration
|
|
1108
1864
|
* @returns Promise resolving to vote submission result
|
|
1109
1865
|
*/
|
|
@@ -1151,7 +1907,7 @@ class VoteOrchestrationService {
|
|
|
1151
1907
|
}
|
|
1152
1908
|
/**
|
|
1153
1909
|
* Get the status of a submitted vote
|
|
1154
|
-
*
|
|
1910
|
+
*
|
|
1155
1911
|
* @param processId - The process ID
|
|
1156
1912
|
* @param voteId - The vote ID
|
|
1157
1913
|
* @returns Promise resolving to vote status information
|
|
@@ -1166,7 +1922,7 @@ class VoteOrchestrationService {
|
|
|
1166
1922
|
}
|
|
1167
1923
|
/**
|
|
1168
1924
|
* Check if an address has voted in a process
|
|
1169
|
-
*
|
|
1925
|
+
*
|
|
1170
1926
|
* @param processId - The process ID
|
|
1171
1927
|
* @param address - The voter's address
|
|
1172
1928
|
* @returns Promise resolving to boolean indicating if the address has voted
|
|
@@ -1175,26 +1931,79 @@ class VoteOrchestrationService {
|
|
|
1175
1931
|
return this.apiService.sequencer.hasAddressVoted(processId, address);
|
|
1176
1932
|
}
|
|
1177
1933
|
/**
|
|
1178
|
-
*
|
|
1179
|
-
*
|
|
1934
|
+
* Watch vote status changes in real-time using an async generator.
|
|
1935
|
+
* Yields each status change as it happens, allowing for reactive UI updates.
|
|
1936
|
+
*
|
|
1180
1937
|
* @param processId - The process ID
|
|
1181
1938
|
* @param voteId - The vote ID
|
|
1182
|
-
* @param
|
|
1183
|
-
* @
|
|
1184
|
-
*
|
|
1185
|
-
* @
|
|
1939
|
+
* @param options - Optional configuration
|
|
1940
|
+
* @returns AsyncGenerator yielding vote status updates
|
|
1941
|
+
*
|
|
1942
|
+
* @example
|
|
1943
|
+
* ```typescript
|
|
1944
|
+
* const vote = await sdk.submitVote({ processId, choices: [1] });
|
|
1945
|
+
*
|
|
1946
|
+
* for await (const statusInfo of sdk.watchVoteStatus(vote.processId, vote.voteId)) {
|
|
1947
|
+
* console.log(`Vote status: ${statusInfo.status}`);
|
|
1948
|
+
*
|
|
1949
|
+
* switch (statusInfo.status) {
|
|
1950
|
+
* case VoteStatus.Pending:
|
|
1951
|
+
* console.log("⏳ Processing...");
|
|
1952
|
+
* break;
|
|
1953
|
+
* case VoteStatus.Verified:
|
|
1954
|
+
* console.log("✓ Verified");
|
|
1955
|
+
* break;
|
|
1956
|
+
* case VoteStatus.Settled:
|
|
1957
|
+
* console.log("✅ Settled");
|
|
1958
|
+
* break;
|
|
1959
|
+
* }
|
|
1960
|
+
* }
|
|
1961
|
+
* ```
|
|
1186
1962
|
*/
|
|
1187
|
-
async
|
|
1963
|
+
async *watchVoteStatus(processId, voteId, options) {
|
|
1964
|
+
const targetStatus = options?.targetStatus ?? VoteStatus.Settled;
|
|
1965
|
+
const timeoutMs = options?.timeoutMs ?? 3e5;
|
|
1966
|
+
const pollIntervalMs = options?.pollIntervalMs ?? 5e3;
|
|
1188
1967
|
const startTime = Date.now();
|
|
1968
|
+
let previousStatus = null;
|
|
1189
1969
|
while (Date.now() - startTime < timeoutMs) {
|
|
1190
1970
|
const statusInfo = await this.getVoteStatus(processId, voteId);
|
|
1191
|
-
if (statusInfo.status
|
|
1192
|
-
|
|
1971
|
+
if (statusInfo.status !== previousStatus) {
|
|
1972
|
+
previousStatus = statusInfo.status;
|
|
1973
|
+
yield statusInfo;
|
|
1974
|
+
if (statusInfo.status === targetStatus || statusInfo.status === VoteStatus.Error) {
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1193
1977
|
}
|
|
1194
1978
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
1195
1979
|
}
|
|
1196
1980
|
throw new Error(`Vote did not reach status ${targetStatus} within ${timeoutMs}ms`);
|
|
1197
1981
|
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Wait for a vote to reach a specific status.
|
|
1984
|
+
* This is a simpler alternative to watchVoteStatus() that returns only the final status.
|
|
1985
|
+
*
|
|
1986
|
+
* @param processId - The process ID
|
|
1987
|
+
* @param voteId - The vote ID
|
|
1988
|
+
* @param targetStatus - The target status to wait for (default: "settled")
|
|
1989
|
+
* @param timeoutMs - Maximum time to wait in milliseconds (default: 300000 = 5 minutes)
|
|
1990
|
+
* @param pollIntervalMs - Polling interval in milliseconds (default: 5000 = 5 seconds)
|
|
1991
|
+
* @returns Promise resolving to final vote status
|
|
1992
|
+
*/
|
|
1993
|
+
async waitForVoteStatus(processId, voteId, targetStatus = VoteStatus.Settled, timeoutMs = 3e5, pollIntervalMs = 5e3) {
|
|
1994
|
+
let finalStatus = null;
|
|
1995
|
+
for await (const statusInfo of this.watchVoteStatus(processId, voteId, {
|
|
1996
|
+
targetStatus,
|
|
1997
|
+
timeoutMs,
|
|
1998
|
+
pollIntervalMs
|
|
1999
|
+
})) {
|
|
2000
|
+
finalStatus = statusInfo;
|
|
2001
|
+
}
|
|
2002
|
+
if (!finalStatus) {
|
|
2003
|
+
throw new Error(`Vote did not reach status ${targetStatus} within ${timeoutMs}ms`);
|
|
2004
|
+
}
|
|
2005
|
+
return finalStatus;
|
|
2006
|
+
}
|
|
1198
2007
|
/**
|
|
1199
2008
|
* Get census proof based on census origin type
|
|
1200
2009
|
*/
|
|
@@ -1276,12 +2085,20 @@ class VoteOrchestrationService {
|
|
|
1276
2085
|
const circomProof = new CircomProof({
|
|
1277
2086
|
wasmUrl: info.circuitUrl,
|
|
1278
2087
|
zkeyUrl: info.provingKeyUrl,
|
|
1279
|
-
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
|
+
}
|
|
1280
2095
|
});
|
|
1281
2096
|
const { proof, publicSignals } = await circomProof.generate(circomInputs);
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
2097
|
+
if (this.verifyProof) {
|
|
2098
|
+
const isValid = await circomProof.verify(proof, publicSignals);
|
|
2099
|
+
if (!isValid) {
|
|
2100
|
+
throw new Error("Generated proof is invalid");
|
|
2101
|
+
}
|
|
1285
2102
|
}
|
|
1286
2103
|
return { proof, publicSignals };
|
|
1287
2104
|
}
|
|
@@ -1319,10 +2136,7 @@ class VoteOrchestrationService {
|
|
|
1319
2136
|
class OrganizationRegistryService extends SmartContractService {
|
|
1320
2137
|
constructor(contractAddress, runner) {
|
|
1321
2138
|
super();
|
|
1322
|
-
this.contract = OrganizationRegistry__factory.connect(
|
|
1323
|
-
contractAddress,
|
|
1324
|
-
runner
|
|
1325
|
-
);
|
|
2139
|
+
this.contract = OrganizationRegistry__factory.connect(contractAddress, runner);
|
|
1326
2140
|
}
|
|
1327
2141
|
// ─── READ OPERATIONS ───────────────────────────────────────────────────────
|
|
1328
2142
|
async getOrganization(id) {
|
|
@@ -1382,31 +2196,36 @@ class OrganizationRegistryService extends SmartContractService {
|
|
|
1382
2196
|
}
|
|
1383
2197
|
// ─── EVENT LISTENERS ───────────────────────────────────────────────────────
|
|
1384
2198
|
onOrganizationCreated(cb) {
|
|
1385
|
-
this.
|
|
2199
|
+
this.setupEventListener(
|
|
2200
|
+
this.contract,
|
|
1386
2201
|
this.contract.filters.OrganizationCreated(),
|
|
1387
|
-
|
|
1388
|
-
);
|
|
2202
|
+
cb
|
|
2203
|
+
).catch((err) => console.error("Error setting up OrganizationCreated listener:", err));
|
|
1389
2204
|
}
|
|
1390
2205
|
onOrganizationUpdated(cb) {
|
|
1391
|
-
this.
|
|
2206
|
+
this.setupEventListener(
|
|
2207
|
+
this.contract,
|
|
1392
2208
|
this.contract.filters.OrganizationUpdated(),
|
|
1393
|
-
|
|
1394
|
-
);
|
|
2209
|
+
cb
|
|
2210
|
+
).catch((err) => console.error("Error setting up OrganizationUpdated listener:", err));
|
|
1395
2211
|
}
|
|
1396
2212
|
onAdministratorAdded(cb) {
|
|
1397
|
-
this.
|
|
2213
|
+
this.setupEventListener(
|
|
2214
|
+
this.contract,
|
|
1398
2215
|
this.contract.filters.AdministratorAdded(),
|
|
1399
|
-
|
|
1400
|
-
);
|
|
2216
|
+
cb
|
|
2217
|
+
).catch((err) => console.error("Error setting up AdministratorAdded listener:", err));
|
|
1401
2218
|
}
|
|
1402
2219
|
onAdministratorRemoved(cb) {
|
|
1403
|
-
this.
|
|
2220
|
+
this.setupEventListener(
|
|
2221
|
+
this.contract,
|
|
1404
2222
|
this.contract.filters.AdministratorRemoved(),
|
|
1405
|
-
|
|
1406
|
-
);
|
|
2223
|
+
cb
|
|
2224
|
+
).catch((err) => console.error("Error setting up AdministratorRemoved listener:", err));
|
|
1407
2225
|
}
|
|
1408
2226
|
removeAllListeners() {
|
|
1409
2227
|
this.contract.removeAllListeners();
|
|
2228
|
+
this.clearPollingIntervals();
|
|
1410
2229
|
}
|
|
1411
2230
|
}
|
|
1412
2231
|
|
|
@@ -1593,25 +2412,23 @@ class DavinciCrypto {
|
|
|
1593
2412
|
class DavinciSDK {
|
|
1594
2413
|
constructor(config) {
|
|
1595
2414
|
this.initialized = false;
|
|
1596
|
-
const
|
|
1597
|
-
environment: config.environment,
|
|
1598
|
-
customUrls: {
|
|
1599
|
-
sequencer: config.sequencerUrl,
|
|
1600
|
-
census: config.censusUrl
|
|
1601
|
-
},
|
|
1602
|
-
customChain: config.chain
|
|
1603
|
-
});
|
|
2415
|
+
const hasCustomAddresses = !!config.addresses && Object.keys(config.addresses).length > 0;
|
|
1604
2416
|
this.config = {
|
|
1605
2417
|
signer: config.signer,
|
|
1606
|
-
sequencerUrl: config.sequencerUrl
|
|
1607
|
-
censusUrl: config.censusUrl
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
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
|
|
1611
2427
|
};
|
|
1612
2428
|
this.apiService = new VocdoniApiService({
|
|
1613
2429
|
sequencerURL: this.config.sequencerUrl,
|
|
1614
|
-
censusURL: this.config.censusUrl
|
|
2430
|
+
censusURL: this.config.censusUrl || ""
|
|
2431
|
+
// Use empty string if not provided
|
|
1615
2432
|
});
|
|
1616
2433
|
this.censusProviders = config.censusProviders || {};
|
|
1617
2434
|
}
|
|
@@ -1621,9 +2438,10 @@ class DavinciSDK {
|
|
|
1621
2438
|
*/
|
|
1622
2439
|
async init() {
|
|
1623
2440
|
if (this.initialized) return;
|
|
1624
|
-
if (this.config.
|
|
1625
|
-
await this.
|
|
2441
|
+
if (this.config.fetchAddressesFromSequencer) {
|
|
2442
|
+
await this.fetchContractAddressesFromSequencer();
|
|
1626
2443
|
}
|
|
2444
|
+
if (!this.config.censusUrl) ;
|
|
1627
2445
|
this.initialized = true;
|
|
1628
2446
|
}
|
|
1629
2447
|
/**
|
|
@@ -1633,22 +2451,36 @@ class DavinciSDK {
|
|
|
1633
2451
|
return this.apiService;
|
|
1634
2452
|
}
|
|
1635
2453
|
/**
|
|
1636
|
-
* Get the process registry service for process management
|
|
2454
|
+
* Get the process registry service for process management.
|
|
2455
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2456
|
+
*
|
|
2457
|
+
* @throws Error if signer does not have a provider
|
|
1637
2458
|
*/
|
|
1638
2459
|
get processes() {
|
|
2460
|
+
this.ensureProvider();
|
|
1639
2461
|
if (!this._processRegistry) {
|
|
1640
2462
|
const processRegistryAddress = this.resolveContractAddress("processRegistry");
|
|
1641
|
-
this._processRegistry = new ProcessRegistryService(
|
|
2463
|
+
this._processRegistry = new ProcessRegistryService(
|
|
2464
|
+
processRegistryAddress,
|
|
2465
|
+
this.config.signer
|
|
2466
|
+
);
|
|
1642
2467
|
}
|
|
1643
2468
|
return this._processRegistry;
|
|
1644
2469
|
}
|
|
1645
2470
|
/**
|
|
1646
|
-
* Get the organization registry service for organization management
|
|
2471
|
+
* Get the organization registry service for organization management.
|
|
2472
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2473
|
+
*
|
|
2474
|
+
* @throws Error if signer does not have a provider
|
|
1647
2475
|
*/
|
|
1648
2476
|
get organizations() {
|
|
2477
|
+
this.ensureProvider();
|
|
1649
2478
|
if (!this._organizationRegistry) {
|
|
1650
2479
|
const organizationRegistryAddress = this.resolveContractAddress("organizationRegistry");
|
|
1651
|
-
this._organizationRegistry = new OrganizationRegistryService(
|
|
2480
|
+
this._organizationRegistry = new OrganizationRegistryService(
|
|
2481
|
+
organizationRegistryAddress,
|
|
2482
|
+
this.config.signer
|
|
2483
|
+
);
|
|
1652
2484
|
}
|
|
1653
2485
|
return this._organizationRegistry;
|
|
1654
2486
|
}
|
|
@@ -1667,9 +2499,13 @@ class DavinciSDK {
|
|
|
1667
2499
|
return this.davinciCrypto;
|
|
1668
2500
|
}
|
|
1669
2501
|
/**
|
|
1670
|
-
* Get the process orchestration service for simplified process creation
|
|
2502
|
+
* Get the process orchestration service for simplified process creation.
|
|
2503
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2504
|
+
*
|
|
2505
|
+
* @throws Error if signer does not have a provider
|
|
1671
2506
|
*/
|
|
1672
2507
|
get processOrchestrator() {
|
|
2508
|
+
this.ensureProvider();
|
|
1673
2509
|
if (!this._processOrchestrator) {
|
|
1674
2510
|
this._processOrchestrator = new ProcessOrchestrationService(
|
|
1675
2511
|
this.processes,
|
|
@@ -1690,7 +2526,11 @@ class DavinciSDK {
|
|
|
1690
2526
|
this.apiService,
|
|
1691
2527
|
() => this.getCrypto(),
|
|
1692
2528
|
this.config.signer,
|
|
1693
|
-
this.censusProviders
|
|
2529
|
+
this.censusProviders,
|
|
2530
|
+
{
|
|
2531
|
+
verifyCircuitFiles: this.config.verifyCircuitFiles,
|
|
2532
|
+
verifyProof: this.config.verifyProof
|
|
2533
|
+
}
|
|
1694
2534
|
);
|
|
1695
2535
|
}
|
|
1696
2536
|
return this._voteOrchestrator;
|
|
@@ -1699,21 +2539,24 @@ class DavinciSDK {
|
|
|
1699
2539
|
* Gets user-friendly process information from the blockchain.
|
|
1700
2540
|
* This method fetches raw contract data and transforms it into a user-friendly format
|
|
1701
2541
|
* that matches the ProcessConfig interface used for creation, plus additional runtime data.
|
|
1702
|
-
*
|
|
2542
|
+
*
|
|
2543
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2544
|
+
*
|
|
1703
2545
|
* @param processId - The process ID to fetch
|
|
1704
2546
|
* @returns Promise resolving to user-friendly process information
|
|
1705
|
-
*
|
|
2547
|
+
* @throws Error if signer does not have a provider
|
|
2548
|
+
*
|
|
1706
2549
|
* @example
|
|
1707
2550
|
* ```typescript
|
|
1708
2551
|
* const processInfo = await sdk.getProcess("0x1234567890abcdef...");
|
|
1709
|
-
*
|
|
2552
|
+
*
|
|
1710
2553
|
* // Access the same fields as ProcessConfig
|
|
1711
2554
|
* console.log("Title:", processInfo.title);
|
|
1712
2555
|
* console.log("Description:", processInfo.description);
|
|
1713
2556
|
* console.log("Questions:", processInfo.questions);
|
|
1714
2557
|
* console.log("Census size:", processInfo.census.size);
|
|
1715
2558
|
* console.log("Ballot config:", processInfo.ballot);
|
|
1716
|
-
*
|
|
2559
|
+
*
|
|
1717
2560
|
* // Plus additional runtime information
|
|
1718
2561
|
* console.log("Status:", processInfo.status);
|
|
1719
2562
|
* console.log("Creator:", processInfo.creator);
|
|
@@ -1721,7 +2564,7 @@ class DavinciSDK {
|
|
|
1721
2564
|
* console.log("End date:", processInfo.endDate);
|
|
1722
2565
|
* console.log("Duration:", processInfo.duration, "seconds");
|
|
1723
2566
|
* console.log("Time remaining:", processInfo.timeRemaining, "seconds");
|
|
1724
|
-
*
|
|
2567
|
+
*
|
|
1725
2568
|
* // Access raw contract data if needed
|
|
1726
2569
|
* console.log("Raw data:", processInfo.raw);
|
|
1727
2570
|
* ```
|
|
@@ -1730,22 +2573,106 @@ class DavinciSDK {
|
|
|
1730
2573
|
if (!this.initialized) {
|
|
1731
2574
|
throw new Error("SDK must be initialized before getting processes. Call sdk.init() first.");
|
|
1732
2575
|
}
|
|
2576
|
+
this.ensureProvider();
|
|
1733
2577
|
return this.processOrchestrator.getProcess(processId);
|
|
1734
2578
|
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Creates a complete voting process and returns an async generator that yields transaction status events.
|
|
2581
|
+
* This method allows you to monitor the transaction progress in real-time, including pending, completed,
|
|
2582
|
+
* failed, and reverted states.
|
|
2583
|
+
*
|
|
2584
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2585
|
+
*
|
|
2586
|
+
* @param config - Simplified process configuration
|
|
2587
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
2588
|
+
* @throws Error if signer does not have a provider
|
|
2589
|
+
*
|
|
2590
|
+
* @example
|
|
2591
|
+
* ```typescript
|
|
2592
|
+
* const stream = sdk.createProcessStream({
|
|
2593
|
+
* title: "My Election",
|
|
2594
|
+
* description: "A simple election",
|
|
2595
|
+
* census: {
|
|
2596
|
+
* type: CensusOrigin.CensusOriginMerkleTree,
|
|
2597
|
+
* root: "0x1234...",
|
|
2598
|
+
* size: 100,
|
|
2599
|
+
* uri: "ipfs://..."
|
|
2600
|
+
* },
|
|
2601
|
+
* ballot: {
|
|
2602
|
+
* numFields: 2,
|
|
2603
|
+
* maxValue: "3",
|
|
2604
|
+
* minValue: "0",
|
|
2605
|
+
* uniqueValues: false,
|
|
2606
|
+
* costFromWeight: false,
|
|
2607
|
+
* costExponent: 10000,
|
|
2608
|
+
* maxValueSum: "6",
|
|
2609
|
+
* minValueSum: "0"
|
|
2610
|
+
* },
|
|
2611
|
+
* timing: {
|
|
2612
|
+
* startDate: new Date("2024-12-01T10:00:00Z"),
|
|
2613
|
+
* duration: 3600 * 24
|
|
2614
|
+
* },
|
|
2615
|
+
* questions: [
|
|
2616
|
+
* {
|
|
2617
|
+
* title: "What is your favorite color?",
|
|
2618
|
+
* choices: [
|
|
2619
|
+
* { title: "Red", value: 0 },
|
|
2620
|
+
* { title: "Blue", value: 1 }
|
|
2621
|
+
* ]
|
|
2622
|
+
* }
|
|
2623
|
+
* ]
|
|
2624
|
+
* });
|
|
2625
|
+
*
|
|
2626
|
+
* // Monitor transaction progress
|
|
2627
|
+
* for await (const event of stream) {
|
|
2628
|
+
* switch (event.status) {
|
|
2629
|
+
* case TxStatus.Pending:
|
|
2630
|
+
* console.log("Transaction pending:", event.hash);
|
|
2631
|
+
* // Update UI to show pending state
|
|
2632
|
+
* break;
|
|
2633
|
+
* case TxStatus.Completed:
|
|
2634
|
+
* console.log("Process created:", event.response.processId);
|
|
2635
|
+
* console.log("Transaction hash:", event.response.transactionHash);
|
|
2636
|
+
* // Update UI to show success
|
|
2637
|
+
* break;
|
|
2638
|
+
* case TxStatus.Failed:
|
|
2639
|
+
* console.error("Transaction failed:", event.error);
|
|
2640
|
+
* // Update UI to show error
|
|
2641
|
+
* break;
|
|
2642
|
+
* case TxStatus.Reverted:
|
|
2643
|
+
* console.error("Transaction reverted:", event.reason);
|
|
2644
|
+
* // Update UI to show revert reason
|
|
2645
|
+
* break;
|
|
2646
|
+
* }
|
|
2647
|
+
* }
|
|
2648
|
+
* ```
|
|
2649
|
+
*/
|
|
2650
|
+
createProcessStream(config) {
|
|
2651
|
+
if (!this.initialized) {
|
|
2652
|
+
throw new Error("SDK must be initialized before creating processes. Call sdk.init() first.");
|
|
2653
|
+
}
|
|
2654
|
+
this.ensureProvider();
|
|
2655
|
+
return this.processOrchestrator.createProcessStream(config);
|
|
2656
|
+
}
|
|
1735
2657
|
/**
|
|
1736
2658
|
* Creates a complete voting process with minimal configuration.
|
|
1737
2659
|
* This is the ultra-easy method for end users that handles all the complex orchestration internally.
|
|
1738
|
-
*
|
|
2660
|
+
*
|
|
2661
|
+
* For real-time transaction status updates, use createProcessStream() instead.
|
|
2662
|
+
*
|
|
2663
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2664
|
+
*
|
|
1739
2665
|
* The method automatically:
|
|
1740
2666
|
* - Gets encryption keys and initial state root from the sequencer
|
|
1741
2667
|
* - Handles process creation signatures
|
|
1742
2668
|
* - Coordinates between sequencer API and on-chain contract calls
|
|
1743
2669
|
* - Creates and pushes metadata
|
|
1744
2670
|
* - Submits the on-chain transaction
|
|
1745
|
-
*
|
|
2671
|
+
*
|
|
1746
2672
|
* @param config - Simplified process configuration
|
|
1747
2673
|
* @returns Promise resolving to the process creation result
|
|
1748
|
-
*
|
|
2674
|
+
* @throws Error if signer does not have a provider
|
|
2675
|
+
*
|
|
1749
2676
|
* @example
|
|
1750
2677
|
* ```typescript
|
|
1751
2678
|
* // Option 1: Using duration (traditional approach)
|
|
@@ -1782,7 +2709,7 @@ class DavinciSDK {
|
|
|
1782
2709
|
* }
|
|
1783
2710
|
* ]
|
|
1784
2711
|
* });
|
|
1785
|
-
*
|
|
2712
|
+
*
|
|
1786
2713
|
* // Option 2: Using start and end dates
|
|
1787
2714
|
* const result2 = await sdk.createProcess({
|
|
1788
2715
|
* title: "Weekend Vote",
|
|
@@ -1797,21 +2724,26 @@ class DavinciSDK {
|
|
|
1797
2724
|
if (!this.initialized) {
|
|
1798
2725
|
throw new Error("SDK must be initialized before creating processes. Call sdk.init() first.");
|
|
1799
2726
|
}
|
|
2727
|
+
this.ensureProvider();
|
|
1800
2728
|
return this.processOrchestrator.createProcess(config);
|
|
1801
2729
|
}
|
|
1802
2730
|
/**
|
|
1803
2731
|
* Submit a vote with simplified configuration.
|
|
1804
2732
|
* This is the ultra-easy method for end users that handles all the complex voting workflow internally.
|
|
1805
|
-
*
|
|
2733
|
+
*
|
|
2734
|
+
* Does NOT require a provider - can be used with a bare Wallet for signing only.
|
|
2735
|
+
* IMPORTANT: Requires censusUrl to be configured in the SDK for fetching census proofs (unless using custom census providers).
|
|
2736
|
+
*
|
|
1806
2737
|
* The method automatically:
|
|
1807
2738
|
* - Fetches process information and validates voting is allowed
|
|
1808
2739
|
* - Gets census proof (Merkle tree based)
|
|
1809
2740
|
* - Generates cryptographic proofs and encrypts the vote
|
|
1810
2741
|
* - Signs and submits the vote to the sequencer
|
|
1811
|
-
*
|
|
2742
|
+
*
|
|
1812
2743
|
* @param config - Simplified vote configuration
|
|
1813
2744
|
* @returns Promise resolving to vote submission result
|
|
1814
|
-
*
|
|
2745
|
+
* @throws Error if censusUrl is not configured (unless using custom census providers)
|
|
2746
|
+
*
|
|
1815
2747
|
* @example
|
|
1816
2748
|
* ```typescript
|
|
1817
2749
|
* // Submit a vote with voter's private key
|
|
@@ -1820,14 +2752,14 @@ class DavinciSDK {
|
|
|
1820
2752
|
* choices: [1, 0], // Vote for option 1 in question 1, option 0 in question 2
|
|
1821
2753
|
* voterKey: "0x1234567890abcdef..." // Voter's private key
|
|
1822
2754
|
* });
|
|
1823
|
-
*
|
|
2755
|
+
*
|
|
1824
2756
|
* console.log("Vote ID:", voteResult.voteId);
|
|
1825
2757
|
* console.log("Status:", voteResult.status);
|
|
1826
|
-
*
|
|
2758
|
+
*
|
|
1827
2759
|
* // Submit a vote with a Wallet instance
|
|
1828
2760
|
* import { Wallet } from "ethers";
|
|
1829
2761
|
* const voterWallet = new Wallet("0x...");
|
|
1830
|
-
*
|
|
2762
|
+
*
|
|
1831
2763
|
* const voteResult2 = await sdk.submitVote({
|
|
1832
2764
|
* processId: "0x1234567890abcdef...",
|
|
1833
2765
|
* choices: [2], // Single question vote
|
|
@@ -1839,15 +2771,22 @@ class DavinciSDK {
|
|
|
1839
2771
|
if (!this.initialized) {
|
|
1840
2772
|
throw new Error("SDK must be initialized before submitting votes. Call sdk.init() first.");
|
|
1841
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
|
+
}
|
|
1842
2779
|
return this.voteOrchestrator.submitVote(config);
|
|
1843
2780
|
}
|
|
1844
2781
|
/**
|
|
1845
2782
|
* Get the status of a submitted vote.
|
|
1846
|
-
*
|
|
2783
|
+
*
|
|
2784
|
+
* Does NOT require a provider - uses API calls only.
|
|
2785
|
+
*
|
|
1847
2786
|
* @param processId - The process ID
|
|
1848
2787
|
* @param voteId - The vote ID returned from submitVote()
|
|
1849
2788
|
* @returns Promise resolving to vote status information
|
|
1850
|
-
*
|
|
2789
|
+
*
|
|
1851
2790
|
* @example
|
|
1852
2791
|
* ```typescript
|
|
1853
2792
|
* const statusInfo = await sdk.getVoteStatus(processId, voteId);
|
|
@@ -1863,11 +2802,13 @@ class DavinciSDK {
|
|
|
1863
2802
|
}
|
|
1864
2803
|
/**
|
|
1865
2804
|
* Check if an address has voted in a process.
|
|
1866
|
-
*
|
|
2805
|
+
*
|
|
2806
|
+
* Does NOT require a provider - uses API calls only.
|
|
2807
|
+
*
|
|
1867
2808
|
* @param processId - The process ID
|
|
1868
2809
|
* @param address - The voter's address
|
|
1869
2810
|
* @returns Promise resolving to boolean indicating if the address has voted
|
|
1870
|
-
*
|
|
2811
|
+
*
|
|
1871
2812
|
* @example
|
|
1872
2813
|
* ```typescript
|
|
1873
2814
|
* const hasVoted = await sdk.hasAddressVoted(processId, "0x1234567890abcdef...");
|
|
@@ -1878,108 +2819,415 @@ class DavinciSDK {
|
|
|
1878
2819
|
*/
|
|
1879
2820
|
async hasAddressVoted(processId, address) {
|
|
1880
2821
|
if (!this.initialized) {
|
|
1881
|
-
throw new Error(
|
|
2822
|
+
throw new Error(
|
|
2823
|
+
"SDK must be initialized before checking vote status. Call sdk.init() first."
|
|
2824
|
+
);
|
|
1882
2825
|
}
|
|
1883
2826
|
return this.voteOrchestrator.hasAddressVoted(processId, address);
|
|
1884
2827
|
}
|
|
2828
|
+
/**
|
|
2829
|
+
* Watch vote status changes in real-time using an async generator.
|
|
2830
|
+
* This method yields each status change as it happens, perfect for showing
|
|
2831
|
+
* progress indicators in UI applications.
|
|
2832
|
+
*
|
|
2833
|
+
* Does NOT require a provider - uses API calls only.
|
|
2834
|
+
*
|
|
2835
|
+
* @param processId - The process ID
|
|
2836
|
+
* @param voteId - The vote ID
|
|
2837
|
+
* @param options - Optional configuration
|
|
2838
|
+
* @returns AsyncGenerator yielding vote status updates
|
|
2839
|
+
*
|
|
2840
|
+
* @example
|
|
2841
|
+
* ```typescript
|
|
2842
|
+
* // Submit vote
|
|
2843
|
+
* const voteResult = await sdk.submitVote({
|
|
2844
|
+
* processId: "0x1234567890abcdef...",
|
|
2845
|
+
* choices: [1]
|
|
2846
|
+
* });
|
|
2847
|
+
*
|
|
2848
|
+
* // Watch status changes in real-time
|
|
2849
|
+
* for await (const statusInfo of sdk.watchVoteStatus(voteResult.processId, voteResult.voteId)) {
|
|
2850
|
+
* console.log(`Vote status: ${statusInfo.status}`);
|
|
2851
|
+
*
|
|
2852
|
+
* switch (statusInfo.status) {
|
|
2853
|
+
* case VoteStatus.Pending:
|
|
2854
|
+
* console.log("⏳ Processing...");
|
|
2855
|
+
* break;
|
|
2856
|
+
* case VoteStatus.Verified:
|
|
2857
|
+
* console.log("✓ Vote verified");
|
|
2858
|
+
* break;
|
|
2859
|
+
* case VoteStatus.Aggregated:
|
|
2860
|
+
* console.log("📊 Vote aggregated");
|
|
2861
|
+
* break;
|
|
2862
|
+
* case VoteStatus.Settled:
|
|
2863
|
+
* console.log("✅ Vote settled");
|
|
2864
|
+
* break;
|
|
2865
|
+
* }
|
|
2866
|
+
* }
|
|
2867
|
+
* ```
|
|
2868
|
+
*/
|
|
2869
|
+
watchVoteStatus(processId, voteId, options) {
|
|
2870
|
+
if (!this.initialized) {
|
|
2871
|
+
throw new Error(
|
|
2872
|
+
"SDK must be initialized before watching vote status. Call sdk.init() first."
|
|
2873
|
+
);
|
|
2874
|
+
}
|
|
2875
|
+
return this.voteOrchestrator.watchVoteStatus(processId, voteId, options);
|
|
2876
|
+
}
|
|
1885
2877
|
/**
|
|
1886
2878
|
* Wait for a vote to reach a specific status.
|
|
1887
|
-
*
|
|
1888
|
-
*
|
|
2879
|
+
* This is a simpler alternative to watchVoteStatus() that returns only the final status.
|
|
2880
|
+
* Useful for waiting for vote confirmation and processing without needing to handle each intermediate status.
|
|
2881
|
+
*
|
|
2882
|
+
* Does NOT require a provider - uses API calls only.
|
|
2883
|
+
*
|
|
1889
2884
|
* @param processId - The process ID
|
|
1890
2885
|
* @param voteId - The vote ID
|
|
1891
2886
|
* @param targetStatus - The target status to wait for (default: "settled")
|
|
1892
2887
|
* @param timeoutMs - Maximum time to wait in milliseconds (default: 300000 = 5 minutes)
|
|
1893
2888
|
* @param pollIntervalMs - Polling interval in milliseconds (default: 5000 = 5 seconds)
|
|
1894
2889
|
* @returns Promise resolving to final vote status
|
|
1895
|
-
*
|
|
2890
|
+
*
|
|
1896
2891
|
* @example
|
|
1897
2892
|
* ```typescript
|
|
1898
2893
|
* // Submit vote and wait for it to be settled
|
|
1899
2894
|
* const voteResult = await sdk.submitVote({
|
|
1900
2895
|
* processId: "0x1234567890abcdef...",
|
|
1901
|
-
* choices: [1]
|
|
1902
|
-
* voterKey: "0x..."
|
|
2896
|
+
* choices: [1]
|
|
1903
2897
|
* });
|
|
1904
|
-
*
|
|
2898
|
+
*
|
|
1905
2899
|
* // Wait for the vote to be fully processed
|
|
1906
2900
|
* const finalStatus = await sdk.waitForVoteStatus(
|
|
1907
2901
|
* voteResult.processId,
|
|
1908
2902
|
* voteResult.voteId,
|
|
1909
|
-
*
|
|
2903
|
+
* VoteStatus.Settled, // Wait until vote is settled
|
|
1910
2904
|
* 300000, // 5 minute timeout
|
|
1911
2905
|
* 5000 // Check every 5 seconds
|
|
1912
2906
|
* );
|
|
1913
|
-
*
|
|
2907
|
+
*
|
|
1914
2908
|
* console.log("Vote final status:", finalStatus.status);
|
|
1915
2909
|
* ```
|
|
1916
2910
|
*/
|
|
1917
2911
|
async waitForVoteStatus(processId, voteId, targetStatus = VoteStatus.Settled, timeoutMs = 3e5, pollIntervalMs = 5e3) {
|
|
1918
2912
|
if (!this.initialized) {
|
|
1919
|
-
throw new Error(
|
|
2913
|
+
throw new Error(
|
|
2914
|
+
"SDK must be initialized before waiting for vote status. Call sdk.init() first."
|
|
2915
|
+
);
|
|
1920
2916
|
}
|
|
1921
|
-
return this.voteOrchestrator.waitForVoteStatus(
|
|
2917
|
+
return this.voteOrchestrator.waitForVoteStatus(
|
|
2918
|
+
processId,
|
|
2919
|
+
voteId,
|
|
2920
|
+
targetStatus,
|
|
2921
|
+
timeoutMs,
|
|
2922
|
+
pollIntervalMs
|
|
2923
|
+
);
|
|
1922
2924
|
}
|
|
1923
2925
|
/**
|
|
1924
|
-
*
|
|
1925
|
-
*
|
|
1926
|
-
*
|
|
1927
|
-
*
|
|
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
|
|
2928
|
+
* transaction progress in real-time.
|
|
2929
|
+
*
|
|
2930
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2931
|
+
*
|
|
2932
|
+
* @param processId - The process ID to end
|
|
2933
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
2934
|
+
* @throws Error if signer does not have a provider
|
|
2935
|
+
*
|
|
2936
|
+
* @example
|
|
2937
|
+
* ```typescript
|
|
2938
|
+
* const stream = sdk.endProcessStream("0x1234567890abcdef...");
|
|
2939
|
+
*
|
|
2940
|
+
* for await (const event of stream) {
|
|
2941
|
+
* switch (event.status) {
|
|
2942
|
+
* case TxStatus.Pending:
|
|
2943
|
+
* console.log("Transaction pending:", event.hash);
|
|
2944
|
+
* break;
|
|
2945
|
+
* case TxStatus.Completed:
|
|
2946
|
+
* console.log("Process ended successfully");
|
|
2947
|
+
* break;
|
|
2948
|
+
* case TxStatus.Failed:
|
|
2949
|
+
* console.error("Transaction failed:", event.error);
|
|
2950
|
+
* break;
|
|
2951
|
+
* case TxStatus.Reverted:
|
|
2952
|
+
* console.error("Transaction reverted:", event.reason);
|
|
2953
|
+
* break;
|
|
2954
|
+
* }
|
|
2955
|
+
* }
|
|
2956
|
+
* ```
|
|
1928
2957
|
*/
|
|
1929
|
-
|
|
1930
|
-
if (this.
|
|
1931
|
-
|
|
2958
|
+
endProcessStream(processId) {
|
|
2959
|
+
if (!this.initialized) {
|
|
2960
|
+
throw new Error("SDK must be initialized before ending processes. Call sdk.init() first.");
|
|
1932
2961
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2962
|
+
this.ensureProvider();
|
|
2963
|
+
return this.processOrchestrator.endProcessStream(processId);
|
|
2964
|
+
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Ends a voting process by setting its status to ENDED.
|
|
2967
|
+
* This is the simplified method that waits for transaction completion.
|
|
2968
|
+
*
|
|
2969
|
+
* For real-time transaction status updates, use endProcessStream() instead.
|
|
2970
|
+
*
|
|
2971
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2972
|
+
*
|
|
2973
|
+
* @param processId - The process ID to end
|
|
2974
|
+
* @returns Promise resolving when the process is ended
|
|
2975
|
+
* @throws Error if signer does not have a provider
|
|
2976
|
+
*
|
|
2977
|
+
* @example
|
|
2978
|
+
* ```typescript
|
|
2979
|
+
* await sdk.endProcess("0x1234567890abcdef...");
|
|
2980
|
+
* console.log("Process ended successfully");
|
|
2981
|
+
* ```
|
|
2982
|
+
*/
|
|
2983
|
+
async endProcess(processId) {
|
|
2984
|
+
if (!this.initialized) {
|
|
2985
|
+
throw new Error("SDK must be initialized before ending processes. Call sdk.init() first.");
|
|
1936
2986
|
}
|
|
1937
|
-
|
|
2987
|
+
this.ensureProvider();
|
|
2988
|
+
return this.processOrchestrator.endProcess(processId);
|
|
1938
2989
|
}
|
|
1939
2990
|
/**
|
|
1940
|
-
*
|
|
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
|
|
2993
|
+
* transaction progress in real-time.
|
|
2994
|
+
*
|
|
2995
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2996
|
+
*
|
|
2997
|
+
* @param processId - The process ID to pause
|
|
2998
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
2999
|
+
* @throws Error if signer does not have a provider
|
|
3000
|
+
*
|
|
3001
|
+
* @example
|
|
3002
|
+
* ```typescript
|
|
3003
|
+
* const stream = sdk.pauseProcessStream("0x1234567890abcdef...");
|
|
3004
|
+
*
|
|
3005
|
+
* for await (const event of stream) {
|
|
3006
|
+
* switch (event.status) {
|
|
3007
|
+
* case TxStatus.Pending:
|
|
3008
|
+
* console.log("Transaction pending:", event.hash);
|
|
3009
|
+
* break;
|
|
3010
|
+
* case TxStatus.Completed:
|
|
3011
|
+
* console.log("Process paused successfully");
|
|
3012
|
+
* break;
|
|
3013
|
+
* case TxStatus.Failed:
|
|
3014
|
+
* console.error("Transaction failed:", event.error);
|
|
3015
|
+
* break;
|
|
3016
|
+
* case TxStatus.Reverted:
|
|
3017
|
+
* console.error("Transaction reverted:", event.reason);
|
|
3018
|
+
* break;
|
|
3019
|
+
* }
|
|
3020
|
+
* }
|
|
3021
|
+
* ```
|
|
1941
3022
|
*/
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
3023
|
+
pauseProcessStream(processId) {
|
|
3024
|
+
if (!this.initialized) {
|
|
3025
|
+
throw new Error("SDK must be initialized before pausing processes. Call sdk.init() first.");
|
|
3026
|
+
}
|
|
3027
|
+
this.ensureProvider();
|
|
3028
|
+
return this.processOrchestrator.pauseProcessStream(processId);
|
|
3029
|
+
}
|
|
3030
|
+
/**
|
|
3031
|
+
* Pauses a voting process by setting its status to PAUSED.
|
|
3032
|
+
* This is the simplified method that waits for transaction completion.
|
|
3033
|
+
*
|
|
3034
|
+
* For real-time transaction status updates, use pauseProcessStream() instead.
|
|
3035
|
+
*
|
|
3036
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3037
|
+
*
|
|
3038
|
+
* @param processId - The process ID to pause
|
|
3039
|
+
* @returns Promise resolving when the process is paused
|
|
3040
|
+
* @throws Error if signer does not have a provider
|
|
3041
|
+
*
|
|
3042
|
+
* @example
|
|
3043
|
+
* ```typescript
|
|
3044
|
+
* await sdk.pauseProcess("0x1234567890abcdef...");
|
|
3045
|
+
* console.log("Process paused successfully");
|
|
3046
|
+
* ```
|
|
3047
|
+
*/
|
|
3048
|
+
async pauseProcess(processId) {
|
|
3049
|
+
if (!this.initialized) {
|
|
3050
|
+
throw new Error("SDK must be initialized before pausing processes. Call sdk.init() first.");
|
|
3051
|
+
}
|
|
3052
|
+
this.ensureProvider();
|
|
3053
|
+
return this.processOrchestrator.pauseProcess(processId);
|
|
3054
|
+
}
|
|
3055
|
+
/**
|
|
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
|
|
3058
|
+
* transaction progress in real-time.
|
|
3059
|
+
*
|
|
3060
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3061
|
+
*
|
|
3062
|
+
* @param processId - The process ID to cancel
|
|
3063
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
3064
|
+
* @throws Error if signer does not have a provider
|
|
3065
|
+
*
|
|
3066
|
+
* @example
|
|
3067
|
+
* ```typescript
|
|
3068
|
+
* const stream = sdk.cancelProcessStream("0x1234567890abcdef...");
|
|
3069
|
+
*
|
|
3070
|
+
* for await (const event of stream) {
|
|
3071
|
+
* switch (event.status) {
|
|
3072
|
+
* case TxStatus.Pending:
|
|
3073
|
+
* console.log("Transaction pending:", event.hash);
|
|
3074
|
+
* break;
|
|
3075
|
+
* case TxStatus.Completed:
|
|
3076
|
+
* console.log("Process canceled successfully");
|
|
3077
|
+
* break;
|
|
3078
|
+
* case TxStatus.Failed:
|
|
3079
|
+
* console.error("Transaction failed:", event.error);
|
|
3080
|
+
* break;
|
|
3081
|
+
* case TxStatus.Reverted:
|
|
3082
|
+
* console.error("Transaction reverted:", event.reason);
|
|
3083
|
+
* break;
|
|
3084
|
+
* }
|
|
3085
|
+
* }
|
|
3086
|
+
* ```
|
|
3087
|
+
*/
|
|
3088
|
+
cancelProcessStream(processId) {
|
|
3089
|
+
if (!this.initialized) {
|
|
3090
|
+
throw new Error("SDK must be initialized before canceling processes. Call sdk.init() first.");
|
|
3091
|
+
}
|
|
3092
|
+
this.ensureProvider();
|
|
3093
|
+
return this.processOrchestrator.cancelProcessStream(processId);
|
|
3094
|
+
}
|
|
3095
|
+
/**
|
|
3096
|
+
* Cancels a voting process by setting its status to CANCELED.
|
|
3097
|
+
* This is the simplified method that waits for transaction completion.
|
|
3098
|
+
*
|
|
3099
|
+
* For real-time transaction status updates, use cancelProcessStream() instead.
|
|
3100
|
+
*
|
|
3101
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3102
|
+
*
|
|
3103
|
+
* @param processId - The process ID to cancel
|
|
3104
|
+
* @returns Promise resolving when the process is canceled
|
|
3105
|
+
* @throws Error if signer does not have a provider
|
|
3106
|
+
*
|
|
3107
|
+
* @example
|
|
3108
|
+
* ```typescript
|
|
3109
|
+
* await sdk.cancelProcess("0x1234567890abcdef...");
|
|
3110
|
+
* console.log("Process canceled successfully");
|
|
3111
|
+
* ```
|
|
3112
|
+
*/
|
|
3113
|
+
async cancelProcess(processId) {
|
|
3114
|
+
if (!this.initialized) {
|
|
3115
|
+
throw new Error("SDK must be initialized before canceling processes. Call sdk.init() first.");
|
|
3116
|
+
}
|
|
3117
|
+
this.ensureProvider();
|
|
3118
|
+
return this.processOrchestrator.cancelProcess(processId);
|
|
3119
|
+
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Resumes a voting process by setting its status to READY and returns an async generator
|
|
3122
|
+
* that yields transaction status events. This is typically used to resume a paused process.
|
|
3123
|
+
*
|
|
3124
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3125
|
+
*
|
|
3126
|
+
* @param processId - The process ID to resume
|
|
3127
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
3128
|
+
* @throws Error if signer does not have a provider
|
|
3129
|
+
*
|
|
3130
|
+
* @example
|
|
3131
|
+
* ```typescript
|
|
3132
|
+
* const stream = sdk.resumeProcessStream("0x1234567890abcdef...");
|
|
3133
|
+
*
|
|
3134
|
+
* for await (const event of stream) {
|
|
3135
|
+
* switch (event.status) {
|
|
3136
|
+
* case TxStatus.Pending:
|
|
3137
|
+
* console.log("Transaction pending:", event.hash);
|
|
3138
|
+
* break;
|
|
3139
|
+
* case TxStatus.Completed:
|
|
3140
|
+
* console.log("Process resumed successfully");
|
|
3141
|
+
* break;
|
|
3142
|
+
* case TxStatus.Failed:
|
|
3143
|
+
* console.error("Transaction failed:", event.error);
|
|
3144
|
+
* break;
|
|
3145
|
+
* case TxStatus.Reverted:
|
|
3146
|
+
* console.error("Transaction reverted:", event.reason);
|
|
3147
|
+
* break;
|
|
3148
|
+
* }
|
|
3149
|
+
* }
|
|
3150
|
+
* ```
|
|
3151
|
+
*/
|
|
3152
|
+
resumeProcessStream(processId) {
|
|
3153
|
+
if (!this.initialized) {
|
|
3154
|
+
throw new Error("SDK must be initialized before resuming processes. Call sdk.init() first.");
|
|
3155
|
+
}
|
|
3156
|
+
this.ensureProvider();
|
|
3157
|
+
return this.processOrchestrator.resumeProcessStream(processId);
|
|
3158
|
+
}
|
|
3159
|
+
/**
|
|
3160
|
+
* Resumes a voting process by setting its status to READY.
|
|
3161
|
+
* This is typically used to resume a paused process.
|
|
3162
|
+
* This is the simplified method that waits for transaction completion.
|
|
3163
|
+
*
|
|
3164
|
+
* For real-time transaction status updates, use resumeProcessStream() instead.
|
|
3165
|
+
*
|
|
3166
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3167
|
+
*
|
|
3168
|
+
* @param processId - The process ID to resume
|
|
3169
|
+
* @returns Promise resolving when the process is resumed
|
|
3170
|
+
* @throws Error if signer does not have a provider
|
|
3171
|
+
*
|
|
3172
|
+
* @example
|
|
3173
|
+
* ```typescript
|
|
3174
|
+
* await sdk.resumeProcess("0x1234567890abcdef...");
|
|
3175
|
+
* console.log("Process resumed successfully");
|
|
3176
|
+
* ```
|
|
3177
|
+
*/
|
|
3178
|
+
async resumeProcess(processId) {
|
|
3179
|
+
if (!this.initialized) {
|
|
3180
|
+
throw new Error("SDK must be initialized before resuming processes. Call sdk.init() first.");
|
|
3181
|
+
}
|
|
3182
|
+
this.ensureProvider();
|
|
3183
|
+
return this.processOrchestrator.resumeProcess(processId);
|
|
3184
|
+
}
|
|
3185
|
+
/**
|
|
3186
|
+
* Resolve contract address based on configuration priority:
|
|
3187
|
+
* 1. Custom addresses from user config (if provided)
|
|
3188
|
+
* 2. Addresses from sequencer (fetched during init if no custom addresses provided)
|
|
3189
|
+
*/
|
|
3190
|
+
resolveContractAddress(contractName) {
|
|
3191
|
+
const customAddress = this.config.customAddresses[contractName];
|
|
3192
|
+
if (customAddress) {
|
|
3193
|
+
return customAddress;
|
|
1957
3194
|
}
|
|
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
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
return this.config.customAddresses[contractName];
|
|
1958
3201
|
}
|
|
1959
3202
|
/**
|
|
1960
|
-
*
|
|
1961
|
-
*
|
|
3203
|
+
* Fetch contract addresses from sequencer info
|
|
3204
|
+
* This is called during init() if custom addresses are not provided
|
|
1962
3205
|
*/
|
|
1963
|
-
async
|
|
3206
|
+
async fetchContractAddressesFromSequencer() {
|
|
1964
3207
|
try {
|
|
1965
3208
|
const info = await this.apiService.sequencer.getInfo();
|
|
1966
3209
|
const contracts = info.contracts;
|
|
1967
3210
|
if (contracts.process) {
|
|
3211
|
+
this.config.customAddresses.processRegistry = contracts.process;
|
|
1968
3212
|
this._processRegistry = new ProcessRegistryService(contracts.process, this.config.signer);
|
|
1969
|
-
this.config.contractAddresses.processRegistry = contracts.process;
|
|
1970
3213
|
}
|
|
1971
3214
|
if (contracts.organization) {
|
|
1972
|
-
this.
|
|
1973
|
-
this.
|
|
3215
|
+
this.config.customAddresses.organizationRegistry = contracts.organization;
|
|
3216
|
+
this._organizationRegistry = new OrganizationRegistryService(
|
|
3217
|
+
contracts.organization,
|
|
3218
|
+
this.config.signer
|
|
3219
|
+
);
|
|
1974
3220
|
}
|
|
1975
3221
|
if (contracts.stateTransitionVerifier) {
|
|
1976
|
-
this.config.
|
|
3222
|
+
this.config.customAddresses.stateTransitionVerifier = contracts.stateTransitionVerifier;
|
|
1977
3223
|
}
|
|
1978
3224
|
if (contracts.resultsVerifier) {
|
|
1979
|
-
this.config.
|
|
3225
|
+
this.config.customAddresses.resultsVerifier = contracts.resultsVerifier;
|
|
1980
3226
|
}
|
|
1981
3227
|
} catch (error) {
|
|
1982
|
-
|
|
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
|
+
);
|
|
1983
3231
|
}
|
|
1984
3232
|
}
|
|
1985
3233
|
/**
|
|
@@ -1994,7 +3242,19 @@ class DavinciSDK {
|
|
|
1994
3242
|
isInitialized() {
|
|
1995
3243
|
return this.initialized;
|
|
1996
3244
|
}
|
|
3245
|
+
/**
|
|
3246
|
+
* Ensures that the signer has a provider for blockchain operations.
|
|
3247
|
+
* @throws Error if the signer does not have a provider
|
|
3248
|
+
* @private
|
|
3249
|
+
*/
|
|
3250
|
+
ensureProvider() {
|
|
3251
|
+
if (!this.config.signer.provider) {
|
|
3252
|
+
throw new Error(
|
|
3253
|
+
"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."
|
|
3254
|
+
);
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
1997
3257
|
}
|
|
1998
3258
|
|
|
1999
|
-
export { BaseService, CensusOrigin, CircomProof, ContractServiceError,
|
|
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 };
|
|
2000
3260
|
//# sourceMappingURL=index.mjs.map
|