@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.js
CHANGED
|
@@ -215,6 +215,281 @@ function assertCSPCensusProof(proof) {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
var CensusType = /* @__PURE__ */ ((CensusType2) => {
|
|
219
|
+
CensusType2["PLAIN"] = "plain";
|
|
220
|
+
CensusType2["WEIGHTED"] = "weighted";
|
|
221
|
+
CensusType2["CSP"] = "csp";
|
|
222
|
+
return CensusType2;
|
|
223
|
+
})(CensusType || {});
|
|
224
|
+
class Census {
|
|
225
|
+
constructor(type) {
|
|
226
|
+
this._censusId = null;
|
|
227
|
+
this._censusRoot = null;
|
|
228
|
+
this._censusURI = null;
|
|
229
|
+
this._size = null;
|
|
230
|
+
this._type = type;
|
|
231
|
+
}
|
|
232
|
+
get censusId() {
|
|
233
|
+
return this._censusId;
|
|
234
|
+
}
|
|
235
|
+
get censusRoot() {
|
|
236
|
+
return this._censusRoot;
|
|
237
|
+
}
|
|
238
|
+
get censusURI() {
|
|
239
|
+
return this._censusURI;
|
|
240
|
+
}
|
|
241
|
+
get type() {
|
|
242
|
+
return this._type;
|
|
243
|
+
}
|
|
244
|
+
get size() {
|
|
245
|
+
return this._size;
|
|
246
|
+
}
|
|
247
|
+
get isPublished() {
|
|
248
|
+
return this._censusRoot !== null && this._censusURI !== null;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Convert CensusType to CensusOrigin enum for API compatibility
|
|
252
|
+
*/
|
|
253
|
+
get censusOrigin() {
|
|
254
|
+
switch (this._type) {
|
|
255
|
+
case "plain" /* PLAIN */:
|
|
256
|
+
case "weighted" /* WEIGHTED */:
|
|
257
|
+
return CensusOrigin.CensusOriginMerkleTree;
|
|
258
|
+
case "csp" /* CSP */:
|
|
259
|
+
return CensusOrigin.CensusOriginCSP;
|
|
260
|
+
default:
|
|
261
|
+
throw new Error(`Unknown census type: ${this._type}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
class PlainCensus extends Census {
|
|
267
|
+
constructor() {
|
|
268
|
+
super(CensusType.PLAIN);
|
|
269
|
+
this._participants = /* @__PURE__ */ new Set();
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Add participant(s) with automatic weight=1
|
|
273
|
+
* @param addresses - Single address or array of addresses
|
|
274
|
+
*/
|
|
275
|
+
add(addresses) {
|
|
276
|
+
const toAdd = Array.isArray(addresses) ? addresses : [addresses];
|
|
277
|
+
for (const address of toAdd) {
|
|
278
|
+
this.validateAddress(address);
|
|
279
|
+
this._participants.add(address.toLowerCase());
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Remove participant by address
|
|
284
|
+
*/
|
|
285
|
+
remove(address) {
|
|
286
|
+
this._participants.delete(address.toLowerCase());
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Get all participants as CensusParticipant array (for API)
|
|
290
|
+
* All participants have weight="1"
|
|
291
|
+
*/
|
|
292
|
+
get participants() {
|
|
293
|
+
return Array.from(this._participants).map((key) => ({
|
|
294
|
+
key,
|
|
295
|
+
weight: "1"
|
|
296
|
+
// Everyone has weight=1 in plain census
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get addresses only
|
|
301
|
+
*/
|
|
302
|
+
get addresses() {
|
|
303
|
+
return Array.from(this._participants);
|
|
304
|
+
}
|
|
305
|
+
validateAddress(address) {
|
|
306
|
+
if (!address || typeof address !== "string") {
|
|
307
|
+
throw new Error("Address is required and must be a string");
|
|
308
|
+
}
|
|
309
|
+
if (!/^(0x)?[0-9a-fA-F]{40}$/i.test(address)) {
|
|
310
|
+
throw new Error(`Invalid Ethereum address format: ${address}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Internal method called after publishing
|
|
315
|
+
* @internal
|
|
316
|
+
*/
|
|
317
|
+
_setPublishedData(root, uri, size, censusId) {
|
|
318
|
+
this._censusRoot = root;
|
|
319
|
+
this._censusURI = uri;
|
|
320
|
+
this._size = size;
|
|
321
|
+
if (censusId) this._censusId = censusId;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
class WeightedCensus extends Census {
|
|
326
|
+
constructor() {
|
|
327
|
+
super(CensusType.WEIGHTED);
|
|
328
|
+
this._participants = /* @__PURE__ */ new Map();
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Add participant(s) with custom weights
|
|
332
|
+
* Weight can be provided as string, number, or bigint - will be converted to string internally
|
|
333
|
+
* @param participant - Single participant or array of participants with custom weights
|
|
334
|
+
*/
|
|
335
|
+
add(participant) {
|
|
336
|
+
const toAdd = Array.isArray(participant) ? participant : [participant];
|
|
337
|
+
for (const p of toAdd) {
|
|
338
|
+
this.validateParticipant(p);
|
|
339
|
+
const weightString = this.normalizeWeight(p.weight);
|
|
340
|
+
this._participants.set(p.key.toLowerCase(), weightString);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Remove participant by address
|
|
345
|
+
*/
|
|
346
|
+
remove(address) {
|
|
347
|
+
this._participants.delete(address.toLowerCase());
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Get all participants as CensusParticipant array
|
|
351
|
+
*/
|
|
352
|
+
get participants() {
|
|
353
|
+
return Array.from(this._participants.entries()).map(([key, weight]) => ({
|
|
354
|
+
key,
|
|
355
|
+
weight
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Get participant addresses
|
|
360
|
+
*/
|
|
361
|
+
get addresses() {
|
|
362
|
+
return Array.from(this._participants.keys());
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Get weight for specific address
|
|
366
|
+
*/
|
|
367
|
+
getWeight(address) {
|
|
368
|
+
return this._participants.get(address.toLowerCase());
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Normalizes weight from string, number, or bigint to string
|
|
372
|
+
*/
|
|
373
|
+
normalizeWeight(weight) {
|
|
374
|
+
if (typeof weight === "string") {
|
|
375
|
+
if (!/^\d+$/.test(weight)) {
|
|
376
|
+
throw new Error(`Invalid weight format: ${weight}. Must be a positive integer.`);
|
|
377
|
+
}
|
|
378
|
+
return weight;
|
|
379
|
+
}
|
|
380
|
+
if (typeof weight === "number") {
|
|
381
|
+
if (!Number.isInteger(weight) || weight < 0) {
|
|
382
|
+
throw new Error(`Invalid weight: ${weight}. Must be a positive integer.`);
|
|
383
|
+
}
|
|
384
|
+
return weight.toString();
|
|
385
|
+
}
|
|
386
|
+
if (typeof weight === "bigint") {
|
|
387
|
+
if (weight < 0n) {
|
|
388
|
+
throw new Error(`Invalid weight: ${weight}. Must be a positive integer.`);
|
|
389
|
+
}
|
|
390
|
+
return weight.toString();
|
|
391
|
+
}
|
|
392
|
+
throw new Error(`Invalid weight type. Must be string, number, or bigint.`);
|
|
393
|
+
}
|
|
394
|
+
validateParticipant(participant) {
|
|
395
|
+
if (!participant.key || typeof participant.key !== "string") {
|
|
396
|
+
throw new Error("Participant key (address) is required");
|
|
397
|
+
}
|
|
398
|
+
if (!/^(0x)?[0-9a-fA-F]{40}$/i.test(participant.key)) {
|
|
399
|
+
throw new Error(`Invalid Ethereum address format: ${participant.key}`);
|
|
400
|
+
}
|
|
401
|
+
if (participant.weight === void 0 || participant.weight === null) {
|
|
402
|
+
throw new Error("Participant weight is required");
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Internal method called after publishing
|
|
407
|
+
* @internal
|
|
408
|
+
*/
|
|
409
|
+
_setPublishedData(root, uri, size, censusId) {
|
|
410
|
+
this._censusRoot = root;
|
|
411
|
+
this._censusURI = uri;
|
|
412
|
+
this._size = size;
|
|
413
|
+
if (censusId) this._censusId = censusId;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
class CspCensus extends Census {
|
|
418
|
+
constructor(publicKey, cspURI, size) {
|
|
419
|
+
super(CensusType.CSP);
|
|
420
|
+
if (!/^(0x)?[0-9a-fA-F]+$/.test(publicKey)) {
|
|
421
|
+
throw new Error("Public key is missing or invalid");
|
|
422
|
+
}
|
|
423
|
+
try {
|
|
424
|
+
new URL(cspURI);
|
|
425
|
+
} catch {
|
|
426
|
+
throw new Error("CSP URI is missing or invalid");
|
|
427
|
+
}
|
|
428
|
+
this._publicKey = publicKey;
|
|
429
|
+
this._cspURI = cspURI;
|
|
430
|
+
this._censusRoot = publicKey;
|
|
431
|
+
this._censusURI = cspURI;
|
|
432
|
+
this._size = size;
|
|
433
|
+
}
|
|
434
|
+
get publicKey() {
|
|
435
|
+
return this._publicKey;
|
|
436
|
+
}
|
|
437
|
+
get cspURI() {
|
|
438
|
+
return this._cspURI;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
class PublishedCensus extends Census {
|
|
443
|
+
constructor(type, root, uri, size) {
|
|
444
|
+
super(type);
|
|
445
|
+
this._censusRoot = root;
|
|
446
|
+
this._censusURI = uri;
|
|
447
|
+
this._size = size;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
class CensusOrchestrator {
|
|
452
|
+
constructor(censusService) {
|
|
453
|
+
this.censusService = censusService;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Publishes a PlainCensus or WeightedCensus
|
|
457
|
+
* Creates a working census, adds participants, and publishes it
|
|
458
|
+
*/
|
|
459
|
+
async publish(census) {
|
|
460
|
+
if (census.isPublished) {
|
|
461
|
+
throw new Error("Census is already published");
|
|
462
|
+
}
|
|
463
|
+
if (census.participants.length === 0) {
|
|
464
|
+
throw new Error("Cannot publish empty census");
|
|
465
|
+
}
|
|
466
|
+
const censusId = await this.censusService.createCensus();
|
|
467
|
+
await this.censusService.addParticipants(censusId, census.participants);
|
|
468
|
+
const publishResponse = await this.censusService.publishCensus(censusId);
|
|
469
|
+
census._setPublishedData(
|
|
470
|
+
publishResponse.root,
|
|
471
|
+
publishResponse.uri,
|
|
472
|
+
publishResponse.participantCount,
|
|
473
|
+
censusId
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Gets census data for process creation
|
|
478
|
+
* Throws if census is not published
|
|
479
|
+
*/
|
|
480
|
+
getCensusData(census) {
|
|
481
|
+
if (!census.isPublished) {
|
|
482
|
+
throw new Error("Census must be published before creating a process");
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
type: census.censusOrigin,
|
|
486
|
+
root: census.censusRoot,
|
|
487
|
+
uri: census.censusURI,
|
|
488
|
+
size: census.size
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
218
493
|
function createProcessSignatureMessage(processId) {
|
|
219
494
|
const cleanProcessId = processId.replace(/^0x/, "").toLowerCase();
|
|
220
495
|
return `I am creating a new voting process for the davinci.vote protocol identified with id ${cleanProcessId}`;
|
|
@@ -305,11 +580,15 @@ class VocdoniSequencerService extends BaseService {
|
|
|
305
580
|
try {
|
|
306
581
|
const response = await fetch(hashOrUrl);
|
|
307
582
|
if (!response.ok) {
|
|
308
|
-
throw new Error(
|
|
583
|
+
throw new Error(
|
|
584
|
+
`Failed to fetch metadata from URL: ${response.status} ${response.statusText}`
|
|
585
|
+
);
|
|
309
586
|
}
|
|
310
587
|
return await response.json();
|
|
311
588
|
} catch (error) {
|
|
312
|
-
throw new Error(
|
|
589
|
+
throw new Error(
|
|
590
|
+
`Failed to fetch metadata from URL: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
591
|
+
);
|
|
313
592
|
}
|
|
314
593
|
}
|
|
315
594
|
if (!isHexString(hashOrUrl)) {
|
|
@@ -345,96 +624,6 @@ class VocdoniApiService {
|
|
|
345
624
|
}
|
|
346
625
|
}
|
|
347
626
|
|
|
348
|
-
const DEFAULT_ENVIRONMENT_URLS = {
|
|
349
|
-
dev: {
|
|
350
|
-
sequencer: "https://sequencer-dev.davinci.vote",
|
|
351
|
-
census: "https://c3-dev.davinci.vote",
|
|
352
|
-
chain: "sepolia"
|
|
353
|
-
},
|
|
354
|
-
stg: {
|
|
355
|
-
sequencer: "https://sequencer1.davinci.vote",
|
|
356
|
-
census: "https://c3.davinci.vote",
|
|
357
|
-
chain: "sepolia"
|
|
358
|
-
},
|
|
359
|
-
prod: {
|
|
360
|
-
// TODO: Add production URLs when available
|
|
361
|
-
sequencer: "",
|
|
362
|
-
census: "",
|
|
363
|
-
chain: "mainnet"
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
function getEnvironmentConfig(environment) {
|
|
367
|
-
return DEFAULT_ENVIRONMENT_URLS[environment];
|
|
368
|
-
}
|
|
369
|
-
function getEnvironmentUrls(environment) {
|
|
370
|
-
const config = DEFAULT_ENVIRONMENT_URLS[environment];
|
|
371
|
-
return {
|
|
372
|
-
sequencer: config.sequencer,
|
|
373
|
-
census: config.census
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
function getEnvironmentChain(environment) {
|
|
377
|
-
return DEFAULT_ENVIRONMENT_URLS[environment].chain;
|
|
378
|
-
}
|
|
379
|
-
function resolveConfiguration(options = {}) {
|
|
380
|
-
const environment = options.environment || "prod";
|
|
381
|
-
const defaultConfig = getEnvironmentConfig(environment);
|
|
382
|
-
const resolvedConfig = { ...defaultConfig };
|
|
383
|
-
if (options.customUrls) {
|
|
384
|
-
if (options.customUrls.sequencer !== void 0) {
|
|
385
|
-
resolvedConfig.sequencer = options.customUrls.sequencer;
|
|
386
|
-
}
|
|
387
|
-
if (options.customUrls.census !== void 0) {
|
|
388
|
-
resolvedConfig.census = options.customUrls.census;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
if (options.customChain) {
|
|
392
|
-
resolvedConfig.chain = options.customChain;
|
|
393
|
-
}
|
|
394
|
-
return resolvedConfig;
|
|
395
|
-
}
|
|
396
|
-
function resolveUrls(options = {}) {
|
|
397
|
-
const config = resolveConfiguration(options);
|
|
398
|
-
return {
|
|
399
|
-
sequencer: config.sequencer,
|
|
400
|
-
census: config.census
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
var processRegistry = {
|
|
405
|
-
sepolia: "0x40939Ec9FD872eb79A1723B559572dfD71a05d11",
|
|
406
|
-
uzh: "0x69B16f67Bd2fB18bD720379E9C1Ef5EaD3872d67",
|
|
407
|
-
mainnet: "0x0"
|
|
408
|
-
};
|
|
409
|
-
var organizationRegistry = {
|
|
410
|
-
sepolia: "0xe7136ED5a7b0e995A8fe35d8B1B815E4160cB491",
|
|
411
|
-
uzh: "0xf7BCE4546805547bE526Ca864d6722Ed193E51Aa",
|
|
412
|
-
mainnet: "0x0"
|
|
413
|
-
};
|
|
414
|
-
var stateTransitionVerifierGroth16 = {
|
|
415
|
-
sepolia: "0xb7A142D24b9220eCBC4f7fcB89Ee952a6C7E332a",
|
|
416
|
-
uzh: "0x5e4673CD378F05cc3Ae25804539c91E711548741",
|
|
417
|
-
mainnet: "0x0"
|
|
418
|
-
};
|
|
419
|
-
var resultsVerifierGroth16 = {
|
|
420
|
-
sepolia: "0x1188cEbB56ecc90e2bAe5c914274C81Fe1a22e67",
|
|
421
|
-
uzh: "0x00c7F87731346F592197E49A90Ad6EC236Ad9985",
|
|
422
|
-
mainnet: "0x0"
|
|
423
|
-
};
|
|
424
|
-
var sequencerRegistry = {
|
|
425
|
-
sepolia: "0x0",
|
|
426
|
-
uzh: "0x0",
|
|
427
|
-
mainnet: "0x0"
|
|
428
|
-
};
|
|
429
|
-
var addressesJson = {
|
|
430
|
-
processRegistry: processRegistry,
|
|
431
|
-
organizationRegistry: organizationRegistry,
|
|
432
|
-
stateTransitionVerifierGroth16: stateTransitionVerifierGroth16,
|
|
433
|
-
resultsVerifierGroth16: resultsVerifierGroth16,
|
|
434
|
-
sequencerRegistry: sequencerRegistry
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
const deployedAddresses = addressesJson;
|
|
438
627
|
var TxStatus = /* @__PURE__ */ ((TxStatus2) => {
|
|
439
628
|
TxStatus2["Pending"] = "pending";
|
|
440
629
|
TxStatus2["Completed"] = "completed";
|
|
@@ -443,23 +632,29 @@ var TxStatus = /* @__PURE__ */ ((TxStatus2) => {
|
|
|
443
632
|
return TxStatus2;
|
|
444
633
|
})(TxStatus || {});
|
|
445
634
|
class SmartContractService {
|
|
635
|
+
constructor() {
|
|
636
|
+
/** Active polling intervals for event listeners using fallback mode */
|
|
637
|
+
this.pollingIntervals = [];
|
|
638
|
+
/** Default polling interval in milliseconds for event listener fallback */
|
|
639
|
+
this.eventPollingInterval = 5e3;
|
|
640
|
+
}
|
|
446
641
|
/**
|
|
447
642
|
* Sends a transaction and yields status events during its lifecycle.
|
|
448
643
|
* This method handles the complete transaction flow from submission to completion,
|
|
449
644
|
* including error handling and status updates.
|
|
450
|
-
*
|
|
645
|
+
*
|
|
451
646
|
* @template T - The type of the successful response data
|
|
452
647
|
* @param txPromise - Promise resolving to the transaction response
|
|
453
648
|
* @param responseHandler - Function to process the successful transaction result
|
|
454
649
|
* @returns AsyncGenerator yielding transaction status events
|
|
455
|
-
*
|
|
650
|
+
*
|
|
456
651
|
* @example
|
|
457
652
|
* ```typescript
|
|
458
653
|
* const txStream = await this.sendTx(
|
|
459
654
|
* contract.someMethod(),
|
|
460
655
|
* async () => await contract.getUpdatedValue()
|
|
461
656
|
* );
|
|
462
|
-
*
|
|
657
|
+
*
|
|
463
658
|
* for await (const event of txStream) {
|
|
464
659
|
* switch (event.status) {
|
|
465
660
|
* case TxStatus.Pending:
|
|
@@ -496,12 +691,12 @@ class SmartContractService {
|
|
|
496
691
|
* Executes a transaction stream and returns the result or throws an error.
|
|
497
692
|
* This is a convenience method that processes a transaction stream and either
|
|
498
693
|
* returns the successful result or throws an appropriate error.
|
|
499
|
-
*
|
|
694
|
+
*
|
|
500
695
|
* @template T - The type of the successful response data
|
|
501
696
|
* @param stream - AsyncGenerator of transaction status events
|
|
502
697
|
* @returns Promise resolving to the successful response data
|
|
503
698
|
* @throws Error if the transaction fails or reverts
|
|
504
|
-
*
|
|
699
|
+
*
|
|
505
700
|
* @example
|
|
506
701
|
* ```typescript
|
|
507
702
|
* try {
|
|
@@ -531,11 +726,11 @@ class SmartContractService {
|
|
|
531
726
|
* Normalizes event listener arguments between different ethers.js versions.
|
|
532
727
|
* This helper method ensures consistent event argument handling regardless of
|
|
533
728
|
* whether the event payload follows ethers v5 or v6 format.
|
|
534
|
-
*
|
|
729
|
+
*
|
|
535
730
|
* @template Args - Tuple type representing the expected event arguments
|
|
536
731
|
* @param callback - The event callback function to normalize
|
|
537
732
|
* @returns Normalized event listener function
|
|
538
|
-
*
|
|
733
|
+
*
|
|
539
734
|
* @example
|
|
540
735
|
* ```typescript
|
|
541
736
|
* contract.on('Transfer', this.normalizeListener((from: string, to: string, amount: BigInt) => {
|
|
@@ -554,12 +749,153 @@ class SmartContractService {
|
|
|
554
749
|
callback(...args);
|
|
555
750
|
};
|
|
556
751
|
}
|
|
752
|
+
/**
|
|
753
|
+
* Sets up an event listener with automatic fallback for RPCs that don't support eth_newFilter.
|
|
754
|
+
* First attempts to use contract.on() which relies on eth_newFilter. If the RPC doesn't support
|
|
755
|
+
* this method (error code -32601), automatically falls back to polling with queryFilter.
|
|
756
|
+
*
|
|
757
|
+
* @template Args - Tuple type representing the event arguments
|
|
758
|
+
* @param contract - The contract instance to listen to
|
|
759
|
+
* @param eventFilter - The event filter to listen for
|
|
760
|
+
* @param callback - The callback function to invoke when the event occurs
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```typescript
|
|
764
|
+
* this.setupEventListener(
|
|
765
|
+
* this.contract,
|
|
766
|
+
* this.contract.filters.Transfer(),
|
|
767
|
+
* (from: string, to: string, amount: bigint) => {
|
|
768
|
+
* console.log(`Transfer: ${from} -> ${to}: ${amount}`);
|
|
769
|
+
* }
|
|
770
|
+
* );
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
async setupEventListener(contract, eventFilter, callback) {
|
|
774
|
+
const normalizedCallback = this.normalizeListener(callback);
|
|
775
|
+
const provider = contract.runner?.provider;
|
|
776
|
+
if (!provider) {
|
|
777
|
+
console.warn("No provider available for event listeners");
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
try {
|
|
781
|
+
const testFilter = {
|
|
782
|
+
address: await contract.getAddress(),
|
|
783
|
+
topics: []
|
|
784
|
+
};
|
|
785
|
+
if ("send" in provider && typeof provider.send === "function") {
|
|
786
|
+
try {
|
|
787
|
+
const filterId = await provider.send("eth_newFilter", [testFilter]);
|
|
788
|
+
await provider.send("eth_getFilterChanges", [filterId]);
|
|
789
|
+
contract.on(eventFilter, normalizedCallback);
|
|
790
|
+
return;
|
|
791
|
+
} catch (error) {
|
|
792
|
+
if (this.isUnsupportedMethodError(error)) {
|
|
793
|
+
console.warn(
|
|
794
|
+
"RPC does not fully support eth_newFilter/eth_getFilterChanges, falling back to polling for events. This may result in delayed event notifications."
|
|
795
|
+
);
|
|
796
|
+
this.setupPollingListener(contract, eventFilter, callback);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
const errorHandler = (error) => {
|
|
802
|
+
if (this.isUnsupportedMethodError(error)) {
|
|
803
|
+
contract.off(eventFilter, normalizedCallback);
|
|
804
|
+
contract.off("error", errorHandler);
|
|
805
|
+
console.warn(
|
|
806
|
+
"RPC does not support eth_newFilter, falling back to polling for events. This may result in delayed event notifications."
|
|
807
|
+
);
|
|
808
|
+
this.setupPollingListener(contract, eventFilter, callback);
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
contract.once("error", errorHandler);
|
|
812
|
+
contract.on(eventFilter, normalizedCallback);
|
|
813
|
+
} catch (error) {
|
|
814
|
+
console.warn("Error setting up event listener, falling back to polling:", error.message);
|
|
815
|
+
this.setupPollingListener(contract, eventFilter, callback);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Checks if an error indicates that the RPC method is unsupported or filter operations are not working.
|
|
820
|
+
* This includes:
|
|
821
|
+
* - Method not found (-32601): RPC doesn't support eth_newFilter
|
|
822
|
+
* - Filter not found (-32000): RPC doesn't properly maintain filters
|
|
823
|
+
*
|
|
824
|
+
* @param error - The error to check
|
|
825
|
+
* @returns true if the error indicates unsupported or broken filter functionality
|
|
826
|
+
*/
|
|
827
|
+
isUnsupportedMethodError(error) {
|
|
828
|
+
const isMethodNotFound = error?.code === -32601 || error?.error?.code === -32601 || error?.data?.code === -32601 || typeof error?.message === "string" && error.message.includes("unsupported method");
|
|
829
|
+
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"));
|
|
830
|
+
return isMethodNotFound || isFilterNotFound;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Sets up a polling-based event listener as fallback when eth_newFilter is not supported.
|
|
834
|
+
* Periodically queries for new events and invokes the callback for each new event found.
|
|
835
|
+
*
|
|
836
|
+
* @template Args - Tuple type representing the event arguments
|
|
837
|
+
* @param contract - The contract instance to poll
|
|
838
|
+
* @param eventFilter - The event filter to poll for
|
|
839
|
+
* @param callback - The callback function to invoke for each event
|
|
840
|
+
*/
|
|
841
|
+
setupPollingListener(contract, eventFilter, callback) {
|
|
842
|
+
let lastProcessedBlock = 0;
|
|
843
|
+
const poll = async () => {
|
|
844
|
+
try {
|
|
845
|
+
const provider = contract.runner?.provider;
|
|
846
|
+
if (!provider) {
|
|
847
|
+
console.warn("No provider available for polling events");
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
const currentBlock = await provider.getBlockNumber();
|
|
851
|
+
if (lastProcessedBlock === 0) {
|
|
852
|
+
lastProcessedBlock = currentBlock - 1;
|
|
853
|
+
}
|
|
854
|
+
if (currentBlock > lastProcessedBlock) {
|
|
855
|
+
const events = await contract.queryFilter(
|
|
856
|
+
eventFilter,
|
|
857
|
+
lastProcessedBlock + 1,
|
|
858
|
+
currentBlock
|
|
859
|
+
);
|
|
860
|
+
for (const event of events) {
|
|
861
|
+
if ("args" in event && event.args) {
|
|
862
|
+
callback(...event.args);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
lastProcessedBlock = currentBlock;
|
|
866
|
+
}
|
|
867
|
+
} catch (error) {
|
|
868
|
+
console.error("Error polling for events:", error);
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
const intervalId = setInterval(poll, this.eventPollingInterval);
|
|
872
|
+
this.pollingIntervals.push(intervalId);
|
|
873
|
+
poll();
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Clears all active polling intervals.
|
|
877
|
+
* Should be called when removing all listeners or cleaning up the service.
|
|
878
|
+
*/
|
|
879
|
+
clearPollingIntervals() {
|
|
880
|
+
for (const intervalId of this.pollingIntervals) {
|
|
881
|
+
clearInterval(intervalId);
|
|
882
|
+
}
|
|
883
|
+
this.pollingIntervals = [];
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Sets the polling interval for event listeners using the fallback mechanism.
|
|
887
|
+
*
|
|
888
|
+
* @param intervalMs - Polling interval in milliseconds
|
|
889
|
+
*/
|
|
890
|
+
setEventPollingInterval(intervalMs) {
|
|
891
|
+
this.eventPollingInterval = intervalMs;
|
|
892
|
+
}
|
|
557
893
|
}
|
|
558
894
|
|
|
559
895
|
class ContractServiceError extends Error {
|
|
560
896
|
/**
|
|
561
897
|
* Creates a new ContractServiceError instance.
|
|
562
|
-
*
|
|
898
|
+
*
|
|
563
899
|
* @param message - The error message describing what went wrong
|
|
564
900
|
* @param operation - The operation that was being performed when the error occurred
|
|
565
901
|
*/
|
|
@@ -712,7 +1048,7 @@ class ProcessRegistryService extends SmartContractService {
|
|
|
712
1048
|
}
|
|
713
1049
|
/**
|
|
714
1050
|
* Sets the results for a voting process.
|
|
715
|
-
*
|
|
1051
|
+
*
|
|
716
1052
|
* @param processID - The ID of the process to set results for
|
|
717
1053
|
* @param proof - Zero-knowledge proof validating the results
|
|
718
1054
|
* @param input - Input data for the proof verification
|
|
@@ -720,11 +1056,7 @@ class ProcessRegistryService extends SmartContractService {
|
|
|
720
1056
|
*/
|
|
721
1057
|
setProcessResults(processID, proof, input) {
|
|
722
1058
|
return this.sendTx(
|
|
723
|
-
this.contract.setProcessResults(
|
|
724
|
-
processID,
|
|
725
|
-
proof,
|
|
726
|
-
input
|
|
727
|
-
).catch((e) => {
|
|
1059
|
+
this.contract.setProcessResults(processID, proof, input).catch((e) => {
|
|
728
1060
|
throw new ProcessResultError(e.message, "setResults");
|
|
729
1061
|
}),
|
|
730
1062
|
async () => ({ success: true })
|
|
@@ -732,43 +1064,50 @@ class ProcessRegistryService extends SmartContractService {
|
|
|
732
1064
|
}
|
|
733
1065
|
// ─── EVENT LISTENERS ───────────────────────────────────────────────────────
|
|
734
1066
|
onProcessCreated(cb) {
|
|
735
|
-
this.
|
|
1067
|
+
this.setupEventListener(
|
|
1068
|
+
this.contract,
|
|
736
1069
|
this.contract.filters.ProcessCreated(),
|
|
737
|
-
|
|
738
|
-
);
|
|
1070
|
+
cb
|
|
1071
|
+
).catch((err) => console.error("Error setting up ProcessCreated listener:", err));
|
|
739
1072
|
}
|
|
740
1073
|
onProcessStatusChanged(cb) {
|
|
741
|
-
this.
|
|
1074
|
+
this.setupEventListener(
|
|
1075
|
+
this.contract,
|
|
742
1076
|
this.contract.filters.ProcessStatusChanged(),
|
|
743
|
-
|
|
744
|
-
);
|
|
1077
|
+
cb
|
|
1078
|
+
).catch((err) => console.error("Error setting up ProcessStatusChanged listener:", err));
|
|
745
1079
|
}
|
|
746
1080
|
onCensusUpdated(cb) {
|
|
747
|
-
this.
|
|
1081
|
+
this.setupEventListener(
|
|
1082
|
+
this.contract,
|
|
748
1083
|
this.contract.filters.CensusUpdated(),
|
|
749
|
-
|
|
750
|
-
);
|
|
1084
|
+
cb
|
|
1085
|
+
).catch((err) => console.error("Error setting up CensusUpdated listener:", err));
|
|
751
1086
|
}
|
|
752
1087
|
onProcessDurationChanged(cb) {
|
|
753
|
-
this.
|
|
1088
|
+
this.setupEventListener(
|
|
1089
|
+
this.contract,
|
|
754
1090
|
this.contract.filters.ProcessDurationChanged(),
|
|
755
|
-
|
|
756
|
-
);
|
|
1091
|
+
cb
|
|
1092
|
+
).catch((err) => console.error("Error setting up ProcessDurationChanged listener:", err));
|
|
757
1093
|
}
|
|
758
1094
|
onStateRootUpdated(cb) {
|
|
759
|
-
this.
|
|
1095
|
+
this.setupEventListener(
|
|
1096
|
+
this.contract,
|
|
760
1097
|
this.contract.filters.ProcessStateRootUpdated(),
|
|
761
|
-
|
|
762
|
-
);
|
|
1098
|
+
cb
|
|
1099
|
+
).catch((err) => console.error("Error setting up StateRootUpdated listener:", err));
|
|
763
1100
|
}
|
|
764
1101
|
onProcessResultsSet(cb) {
|
|
765
|
-
this.
|
|
1102
|
+
this.setupEventListener(
|
|
1103
|
+
this.contract,
|
|
766
1104
|
this.contract.filters.ProcessResultsSet(),
|
|
767
|
-
|
|
768
|
-
);
|
|
1105
|
+
cb
|
|
1106
|
+
).catch((err) => console.error("Error setting up ProcessResultsSet listener:", err));
|
|
769
1107
|
}
|
|
770
1108
|
removeAllListeners() {
|
|
771
1109
|
this.contract.removeAllListeners();
|
|
1110
|
+
this.clearPollingIntervals();
|
|
772
1111
|
}
|
|
773
1112
|
}
|
|
774
1113
|
|
|
@@ -779,6 +1118,34 @@ class ProcessOrchestrationService {
|
|
|
779
1118
|
this.organizationRegistry = organizationRegistry;
|
|
780
1119
|
this.getCrypto = getCrypto;
|
|
781
1120
|
this.signer = signer;
|
|
1121
|
+
this.censusOrchestrator = new CensusOrchestrator(apiService.census);
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Handles census - auto-publishes if needed and returns census config
|
|
1125
|
+
* @private
|
|
1126
|
+
*/
|
|
1127
|
+
async handleCensus(census) {
|
|
1128
|
+
if ("isPublished" in census) {
|
|
1129
|
+
if (census instanceof PlainCensus || census instanceof WeightedCensus) {
|
|
1130
|
+
if (!census.isPublished) {
|
|
1131
|
+
const censusBaseURL = this.apiService.census?.["axios"]?.defaults?.baseURL;
|
|
1132
|
+
if (!censusBaseURL || censusBaseURL === "" || censusBaseURL === "undefined") {
|
|
1133
|
+
throw new Error(
|
|
1134
|
+
'Census API URL is required to publish PlainCensus or WeightedCensus. Please provide "censusUrl" when initializing DavinciSDK, or use a pre-published census.'
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
await this.censusOrchestrator.publish(census);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
const censusData = this.censusOrchestrator.getCensusData(census);
|
|
1141
|
+
return {
|
|
1142
|
+
type: censusData.type,
|
|
1143
|
+
root: censusData.root,
|
|
1144
|
+
size: censusData.size,
|
|
1145
|
+
uri: censusData.uri
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
return census;
|
|
782
1149
|
}
|
|
783
1150
|
/**
|
|
784
1151
|
* Gets user-friendly process information by transforming raw contract data
|
|
@@ -852,14 +1219,119 @@ class ProcessOrchestrationService {
|
|
|
852
1219
|
};
|
|
853
1220
|
}
|
|
854
1221
|
/**
|
|
855
|
-
* Creates a complete voting process
|
|
856
|
-
* This method
|
|
1222
|
+
* Creates a complete voting process and returns an async generator that yields transaction status events.
|
|
1223
|
+
* This method allows you to monitor the transaction progress in real-time.
|
|
1224
|
+
*
|
|
1225
|
+
* @param config - Process configuration
|
|
1226
|
+
* @returns AsyncGenerator yielding transaction status events with ProcessCreationResult
|
|
1227
|
+
*
|
|
1228
|
+
* @example
|
|
1229
|
+
* ```typescript
|
|
1230
|
+
* const stream = sdk.createProcessStream({
|
|
1231
|
+
* title: "My Election",
|
|
1232
|
+
* description: "A simple election",
|
|
1233
|
+
* census: { ... },
|
|
1234
|
+
* ballot: { ... },
|
|
1235
|
+
* timing: { ... },
|
|
1236
|
+
* questions: [ ... ]
|
|
1237
|
+
* });
|
|
1238
|
+
*
|
|
1239
|
+
* for await (const event of stream) {
|
|
1240
|
+
* switch (event.status) {
|
|
1241
|
+
* case "pending":
|
|
1242
|
+
* console.log("Transaction pending:", event.hash);
|
|
1243
|
+
* break;
|
|
1244
|
+
* case "completed":
|
|
1245
|
+
* console.log("Process created:", event.response.processId);
|
|
1246
|
+
* console.log("Transaction hash:", event.response.transactionHash);
|
|
1247
|
+
* break;
|
|
1248
|
+
* case "failed":
|
|
1249
|
+
* console.error("Transaction failed:", event.error);
|
|
1250
|
+
* break;
|
|
1251
|
+
* case "reverted":
|
|
1252
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1253
|
+
* break;
|
|
1254
|
+
* }
|
|
1255
|
+
* }
|
|
1256
|
+
* ```
|
|
1257
|
+
*/
|
|
1258
|
+
async *createProcessStream(config) {
|
|
1259
|
+
const data = await this.prepareProcessCreation(config);
|
|
1260
|
+
const encryptionKey = {
|
|
1261
|
+
x: data.sequencerResult.encryptionPubKey[0],
|
|
1262
|
+
y: data.sequencerResult.encryptionPubKey[1]
|
|
1263
|
+
};
|
|
1264
|
+
const txStream = this.processRegistry.newProcess(
|
|
1265
|
+
ProcessStatus.READY,
|
|
1266
|
+
data.startTime,
|
|
1267
|
+
data.duration,
|
|
1268
|
+
data.ballotMode,
|
|
1269
|
+
data.census,
|
|
1270
|
+
data.metadataUri,
|
|
1271
|
+
encryptionKey,
|
|
1272
|
+
BigInt(data.sequencerResult.stateRoot)
|
|
1273
|
+
);
|
|
1274
|
+
let transactionHash = "unknown";
|
|
1275
|
+
for await (const event of txStream) {
|
|
1276
|
+
if (event.status === TxStatus.Pending) {
|
|
1277
|
+
transactionHash = event.hash;
|
|
1278
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1279
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1280
|
+
yield {
|
|
1281
|
+
status: TxStatus.Completed,
|
|
1282
|
+
response: {
|
|
1283
|
+
processId: data.processId,
|
|
1284
|
+
transactionHash
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
break;
|
|
1288
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1289
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1290
|
+
break;
|
|
1291
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1292
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1293
|
+
break;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Creates a complete voting process with minimal configuration.
|
|
1299
|
+
* This is the ultra-easy method for end users that handles all the complex orchestration internally.
|
|
1300
|
+
*
|
|
1301
|
+
* For real-time transaction status updates, use createProcessStream() instead.
|
|
1302
|
+
*
|
|
1303
|
+
* The method automatically:
|
|
1304
|
+
* - Gets encryption keys and initial state root from the sequencer
|
|
1305
|
+
* - Handles process creation signatures
|
|
1306
|
+
* - Coordinates between sequencer API and on-chain contract calls
|
|
1307
|
+
* - Creates and pushes metadata
|
|
1308
|
+
* - Submits the on-chain transaction
|
|
1309
|
+
*
|
|
1310
|
+
* @param config - Simplified process configuration
|
|
1311
|
+
* @returns Promise resolving to the process creation result
|
|
857
1312
|
*/
|
|
858
1313
|
async createProcess(config) {
|
|
1314
|
+
for await (const event of this.createProcessStream(config)) {
|
|
1315
|
+
if (event.status === "completed") {
|
|
1316
|
+
return event.response;
|
|
1317
|
+
} else if (event.status === "failed") {
|
|
1318
|
+
throw event.error;
|
|
1319
|
+
} else if (event.status === "reverted") {
|
|
1320
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
throw new Error("Process creation stream ended unexpectedly");
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Prepares all data needed for process creation
|
|
1327
|
+
* @private
|
|
1328
|
+
*/
|
|
1329
|
+
async prepareProcessCreation(config) {
|
|
859
1330
|
const { startTime, duration } = this.calculateTiming(config.timing);
|
|
860
1331
|
const signerAddress = await this.signer.getAddress();
|
|
861
1332
|
const processId = await this.processRegistry.getNextProcessId(signerAddress);
|
|
862
|
-
const
|
|
1333
|
+
const censusConfig = await this.handleCensus(config.census);
|
|
1334
|
+
const censusRoot = censusConfig.root;
|
|
863
1335
|
const ballotMode = config.ballot;
|
|
864
1336
|
const metadata = this.createMetadata(config);
|
|
865
1337
|
const metadataHash = await this.apiService.sequencer.pushMetadata(metadata);
|
|
@@ -870,43 +1342,23 @@ class ProcessOrchestrationService {
|
|
|
870
1342
|
censusRoot,
|
|
871
1343
|
ballotMode,
|
|
872
1344
|
signature,
|
|
873
|
-
censusOrigin:
|
|
1345
|
+
censusOrigin: censusConfig.type
|
|
874
1346
|
});
|
|
875
1347
|
const census = {
|
|
876
|
-
censusOrigin:
|
|
877
|
-
maxVotes:
|
|
1348
|
+
censusOrigin: censusConfig.type,
|
|
1349
|
+
maxVotes: censusConfig.size.toString(),
|
|
878
1350
|
censusRoot,
|
|
879
|
-
censusURI:
|
|
1351
|
+
censusURI: censusConfig.uri
|
|
880
1352
|
};
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
y: sequencerResult.encryptionPubKey[1]
|
|
884
|
-
};
|
|
885
|
-
const txStream = this.processRegistry.newProcess(
|
|
886
|
-
ProcessStatus.READY,
|
|
1353
|
+
return {
|
|
1354
|
+
processId,
|
|
887
1355
|
startTime,
|
|
888
1356
|
duration,
|
|
1357
|
+
censusRoot,
|
|
889
1358
|
ballotMode,
|
|
890
|
-
census,
|
|
891
1359
|
metadataUri,
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
);
|
|
895
|
-
let transactionHash = "unknown";
|
|
896
|
-
for await (const event of txStream) {
|
|
897
|
-
if (event.status === "pending") {
|
|
898
|
-
transactionHash = event.hash;
|
|
899
|
-
} else if (event.status === "completed") {
|
|
900
|
-
break;
|
|
901
|
-
} else if (event.status === "failed") {
|
|
902
|
-
throw event.error;
|
|
903
|
-
} else if (event.status === "reverted") {
|
|
904
|
-
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
return {
|
|
908
|
-
processId,
|
|
909
|
-
transactionHash
|
|
1360
|
+
sequencerResult,
|
|
1361
|
+
census
|
|
910
1362
|
};
|
|
911
1363
|
}
|
|
912
1364
|
/**
|
|
@@ -972,17 +1424,323 @@ class ProcessOrchestrationService {
|
|
|
972
1424
|
if (!config.questions || config.questions.length === 0) {
|
|
973
1425
|
throw new Error("Questions are required. Please provide at least one question with choices.");
|
|
974
1426
|
}
|
|
975
|
-
metadata.questions = config.questions.map((q) => ({
|
|
976
|
-
title: { default: q.title },
|
|
977
|
-
description: { default: q.description || "" },
|
|
978
|
-
meta: {},
|
|
979
|
-
choices: q.choices.map((c) => ({
|
|
980
|
-
title: { default: c.title },
|
|
981
|
-
value: c.value,
|
|
982
|
-
meta: {}
|
|
983
|
-
}))
|
|
984
|
-
}));
|
|
985
|
-
return metadata;
|
|
1427
|
+
metadata.questions = config.questions.map((q) => ({
|
|
1428
|
+
title: { default: q.title },
|
|
1429
|
+
description: { default: q.description || "" },
|
|
1430
|
+
meta: {},
|
|
1431
|
+
choices: q.choices.map((c) => ({
|
|
1432
|
+
title: { default: c.title },
|
|
1433
|
+
value: c.value,
|
|
1434
|
+
meta: {}
|
|
1435
|
+
}))
|
|
1436
|
+
}));
|
|
1437
|
+
return metadata;
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Ends a voting process by setting its status to ENDED.
|
|
1441
|
+
* Returns an async generator that yields transaction status events.
|
|
1442
|
+
*
|
|
1443
|
+
* @param processId - The process ID to end
|
|
1444
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
1445
|
+
*
|
|
1446
|
+
* @example
|
|
1447
|
+
* ```typescript
|
|
1448
|
+
* const stream = sdk.endProcessStream("0x1234567890abcdef...");
|
|
1449
|
+
*
|
|
1450
|
+
* for await (const event of stream) {
|
|
1451
|
+
* switch (event.status) {
|
|
1452
|
+
* case "pending":
|
|
1453
|
+
* console.log("Transaction pending:", event.hash);
|
|
1454
|
+
* break;
|
|
1455
|
+
* case "completed":
|
|
1456
|
+
* console.log("Process ended successfully");
|
|
1457
|
+
* break;
|
|
1458
|
+
* case "failed":
|
|
1459
|
+
* console.error("Transaction failed:", event.error);
|
|
1460
|
+
* break;
|
|
1461
|
+
* case "reverted":
|
|
1462
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1463
|
+
* break;
|
|
1464
|
+
* }
|
|
1465
|
+
* }
|
|
1466
|
+
* ```
|
|
1467
|
+
*/
|
|
1468
|
+
async *endProcessStream(processId) {
|
|
1469
|
+
const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.ENDED);
|
|
1470
|
+
for await (const event of txStream) {
|
|
1471
|
+
if (event.status === TxStatus.Pending) {
|
|
1472
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1473
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1474
|
+
yield {
|
|
1475
|
+
status: TxStatus.Completed,
|
|
1476
|
+
response: { success: true }
|
|
1477
|
+
};
|
|
1478
|
+
break;
|
|
1479
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1480
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1481
|
+
break;
|
|
1482
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1483
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1484
|
+
break;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Ends a voting process by setting its status to ENDED.
|
|
1490
|
+
* This is a simplified method that waits for transaction completion.
|
|
1491
|
+
*
|
|
1492
|
+
* For real-time transaction status updates, use endProcessStream() instead.
|
|
1493
|
+
*
|
|
1494
|
+
* @param processId - The process ID to end
|
|
1495
|
+
* @returns Promise resolving when the process is ended
|
|
1496
|
+
*
|
|
1497
|
+
* @example
|
|
1498
|
+
* ```typescript
|
|
1499
|
+
* await sdk.endProcess("0x1234567890abcdef...");
|
|
1500
|
+
* console.log("Process ended successfully");
|
|
1501
|
+
* ```
|
|
1502
|
+
*/
|
|
1503
|
+
async endProcess(processId) {
|
|
1504
|
+
for await (const event of this.endProcessStream(processId)) {
|
|
1505
|
+
if (event.status === "completed") {
|
|
1506
|
+
return;
|
|
1507
|
+
} else if (event.status === "failed") {
|
|
1508
|
+
throw event.error;
|
|
1509
|
+
} else if (event.status === "reverted") {
|
|
1510
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
throw new Error("End process stream ended unexpectedly");
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Pauses a voting process by setting its status to PAUSED.
|
|
1517
|
+
* Returns an async generator that yields transaction status events.
|
|
1518
|
+
*
|
|
1519
|
+
* @param processId - The process ID to pause
|
|
1520
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
1521
|
+
*
|
|
1522
|
+
* @example
|
|
1523
|
+
* ```typescript
|
|
1524
|
+
* const stream = sdk.pauseProcessStream("0x1234567890abcdef...");
|
|
1525
|
+
*
|
|
1526
|
+
* for await (const event of stream) {
|
|
1527
|
+
* switch (event.status) {
|
|
1528
|
+
* case "pending":
|
|
1529
|
+
* console.log("Transaction pending:", event.hash);
|
|
1530
|
+
* break;
|
|
1531
|
+
* case "completed":
|
|
1532
|
+
* console.log("Process paused successfully");
|
|
1533
|
+
* break;
|
|
1534
|
+
* case "failed":
|
|
1535
|
+
* console.error("Transaction failed:", event.error);
|
|
1536
|
+
* break;
|
|
1537
|
+
* case "reverted":
|
|
1538
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1539
|
+
* break;
|
|
1540
|
+
* }
|
|
1541
|
+
* }
|
|
1542
|
+
* ```
|
|
1543
|
+
*/
|
|
1544
|
+
async *pauseProcessStream(processId) {
|
|
1545
|
+
const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.PAUSED);
|
|
1546
|
+
for await (const event of txStream) {
|
|
1547
|
+
if (event.status === TxStatus.Pending) {
|
|
1548
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1549
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1550
|
+
yield {
|
|
1551
|
+
status: TxStatus.Completed,
|
|
1552
|
+
response: { success: true }
|
|
1553
|
+
};
|
|
1554
|
+
break;
|
|
1555
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1556
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1557
|
+
break;
|
|
1558
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1559
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1560
|
+
break;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Pauses a voting process by setting its status to PAUSED.
|
|
1566
|
+
* This is a simplified method that waits for transaction completion.
|
|
1567
|
+
*
|
|
1568
|
+
* For real-time transaction status updates, use pauseProcessStream() instead.
|
|
1569
|
+
*
|
|
1570
|
+
* @param processId - The process ID to pause
|
|
1571
|
+
* @returns Promise resolving when the process is paused
|
|
1572
|
+
*
|
|
1573
|
+
* @example
|
|
1574
|
+
* ```typescript
|
|
1575
|
+
* await sdk.pauseProcess("0x1234567890abcdef...");
|
|
1576
|
+
* console.log("Process paused successfully");
|
|
1577
|
+
* ```
|
|
1578
|
+
*/
|
|
1579
|
+
async pauseProcess(processId) {
|
|
1580
|
+
for await (const event of this.pauseProcessStream(processId)) {
|
|
1581
|
+
if (event.status === "completed") {
|
|
1582
|
+
return;
|
|
1583
|
+
} else if (event.status === "failed") {
|
|
1584
|
+
throw event.error;
|
|
1585
|
+
} else if (event.status === "reverted") {
|
|
1586
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
throw new Error("Pause process stream ended unexpectedly");
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Cancels a voting process by setting its status to CANCELED.
|
|
1593
|
+
* Returns an async generator that yields transaction status events.
|
|
1594
|
+
*
|
|
1595
|
+
* @param processId - The process ID to cancel
|
|
1596
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
1597
|
+
*
|
|
1598
|
+
* @example
|
|
1599
|
+
* ```typescript
|
|
1600
|
+
* const stream = sdk.cancelProcessStream("0x1234567890abcdef...");
|
|
1601
|
+
*
|
|
1602
|
+
* for await (const event of stream) {
|
|
1603
|
+
* switch (event.status) {
|
|
1604
|
+
* case "pending":
|
|
1605
|
+
* console.log("Transaction pending:", event.hash);
|
|
1606
|
+
* break;
|
|
1607
|
+
* case "completed":
|
|
1608
|
+
* console.log("Process canceled successfully");
|
|
1609
|
+
* break;
|
|
1610
|
+
* case "failed":
|
|
1611
|
+
* console.error("Transaction failed:", event.error);
|
|
1612
|
+
* break;
|
|
1613
|
+
* case "reverted":
|
|
1614
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1615
|
+
* break;
|
|
1616
|
+
* }
|
|
1617
|
+
* }
|
|
1618
|
+
* ```
|
|
1619
|
+
*/
|
|
1620
|
+
async *cancelProcessStream(processId) {
|
|
1621
|
+
const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.CANCELED);
|
|
1622
|
+
for await (const event of txStream) {
|
|
1623
|
+
if (event.status === TxStatus.Pending) {
|
|
1624
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1625
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1626
|
+
yield {
|
|
1627
|
+
status: TxStatus.Completed,
|
|
1628
|
+
response: { success: true }
|
|
1629
|
+
};
|
|
1630
|
+
break;
|
|
1631
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1632
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1633
|
+
break;
|
|
1634
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1635
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1636
|
+
break;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Cancels a voting process by setting its status to CANCELED.
|
|
1642
|
+
* This is a simplified method that waits for transaction completion.
|
|
1643
|
+
*
|
|
1644
|
+
* For real-time transaction status updates, use cancelProcessStream() instead.
|
|
1645
|
+
*
|
|
1646
|
+
* @param processId - The process ID to cancel
|
|
1647
|
+
* @returns Promise resolving when the process is canceled
|
|
1648
|
+
*
|
|
1649
|
+
* @example
|
|
1650
|
+
* ```typescript
|
|
1651
|
+
* await sdk.cancelProcess("0x1234567890abcdef...");
|
|
1652
|
+
* console.log("Process canceled successfully");
|
|
1653
|
+
* ```
|
|
1654
|
+
*/
|
|
1655
|
+
async cancelProcess(processId) {
|
|
1656
|
+
for await (const event of this.cancelProcessStream(processId)) {
|
|
1657
|
+
if (event.status === "completed") {
|
|
1658
|
+
return;
|
|
1659
|
+
} else if (event.status === "failed") {
|
|
1660
|
+
throw event.error;
|
|
1661
|
+
} else if (event.status === "reverted") {
|
|
1662
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
throw new Error("Cancel process stream ended unexpectedly");
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Resumes a voting process by setting its status to READY.
|
|
1669
|
+
* This is typically used to resume a paused process.
|
|
1670
|
+
* Returns an async generator that yields transaction status events.
|
|
1671
|
+
*
|
|
1672
|
+
* @param processId - The process ID to resume
|
|
1673
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
1674
|
+
*
|
|
1675
|
+
* @example
|
|
1676
|
+
* ```typescript
|
|
1677
|
+
* const stream = sdk.resumeProcessStream("0x1234567890abcdef...");
|
|
1678
|
+
*
|
|
1679
|
+
* for await (const event of stream) {
|
|
1680
|
+
* switch (event.status) {
|
|
1681
|
+
* case "pending":
|
|
1682
|
+
* console.log("Transaction pending:", event.hash);
|
|
1683
|
+
* break;
|
|
1684
|
+
* case "completed":
|
|
1685
|
+
* console.log("Process resumed successfully");
|
|
1686
|
+
* break;
|
|
1687
|
+
* case "failed":
|
|
1688
|
+
* console.error("Transaction failed:", event.error);
|
|
1689
|
+
* break;
|
|
1690
|
+
* case "reverted":
|
|
1691
|
+
* console.error("Transaction reverted:", event.reason);
|
|
1692
|
+
* break;
|
|
1693
|
+
* }
|
|
1694
|
+
* }
|
|
1695
|
+
* ```
|
|
1696
|
+
*/
|
|
1697
|
+
async *resumeProcessStream(processId) {
|
|
1698
|
+
const txStream = this.processRegistry.setProcessStatus(processId, ProcessStatus.READY);
|
|
1699
|
+
for await (const event of txStream) {
|
|
1700
|
+
if (event.status === TxStatus.Pending) {
|
|
1701
|
+
yield { status: TxStatus.Pending, hash: event.hash };
|
|
1702
|
+
} else if (event.status === TxStatus.Completed) {
|
|
1703
|
+
yield {
|
|
1704
|
+
status: TxStatus.Completed,
|
|
1705
|
+
response: { success: true }
|
|
1706
|
+
};
|
|
1707
|
+
break;
|
|
1708
|
+
} else if (event.status === TxStatus.Failed) {
|
|
1709
|
+
yield { status: TxStatus.Failed, error: event.error };
|
|
1710
|
+
break;
|
|
1711
|
+
} else if (event.status === TxStatus.Reverted) {
|
|
1712
|
+
yield { status: TxStatus.Reverted, reason: event.reason };
|
|
1713
|
+
break;
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Resumes a voting process by setting its status to READY.
|
|
1719
|
+
* This is typically used to resume a paused process.
|
|
1720
|
+
* This is a simplified method that waits for transaction completion.
|
|
1721
|
+
*
|
|
1722
|
+
* For real-time transaction status updates, use resumeProcessStream() instead.
|
|
1723
|
+
*
|
|
1724
|
+
* @param processId - The process ID to resume
|
|
1725
|
+
* @returns Promise resolving when the process is resumed
|
|
1726
|
+
*
|
|
1727
|
+
* @example
|
|
1728
|
+
* ```typescript
|
|
1729
|
+
* await sdk.resumeProcess("0x1234567890abcdef...");
|
|
1730
|
+
* console.log("Process resumed successfully");
|
|
1731
|
+
* ```
|
|
1732
|
+
*/
|
|
1733
|
+
async resumeProcess(processId) {
|
|
1734
|
+
for await (const event of this.resumeProcessStream(processId)) {
|
|
1735
|
+
if (event.status === "completed") {
|
|
1736
|
+
return;
|
|
1737
|
+
} else if (event.status === "failed") {
|
|
1738
|
+
throw event.error;
|
|
1739
|
+
} else if (event.status === "reverted") {
|
|
1740
|
+
throw new Error(`Transaction reverted: ${event.reason || "unknown reason"}`);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
throw new Error("Resume process stream ended unexpectedly");
|
|
986
1744
|
}
|
|
987
1745
|
}
|
|
988
1746
|
|
|
@@ -1053,11 +1811,7 @@ class CircomProof {
|
|
|
1053
1811
|
}
|
|
1054
1812
|
this.zkeyCache.set(zkeyUrl, zkeyBin);
|
|
1055
1813
|
}
|
|
1056
|
-
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
|
|
1057
|
-
inputs,
|
|
1058
|
-
wasmBin,
|
|
1059
|
-
zkeyBin
|
|
1060
|
-
);
|
|
1814
|
+
const { proof, publicSignals } = await snarkjs.groth16.fullProve(inputs, wasmBin, zkeyBin);
|
|
1061
1815
|
return {
|
|
1062
1816
|
proof,
|
|
1063
1817
|
publicSignals
|
|
@@ -1092,11 +1846,13 @@ var VoteStatus = /* @__PURE__ */ ((VoteStatus2) => {
|
|
|
1092
1846
|
})(VoteStatus || {});
|
|
1093
1847
|
|
|
1094
1848
|
class VoteOrchestrationService {
|
|
1095
|
-
constructor(apiService, getCrypto, signer, censusProviders = {}) {
|
|
1849
|
+
constructor(apiService, getCrypto, signer, censusProviders = {}, config = {}) {
|
|
1096
1850
|
this.apiService = apiService;
|
|
1097
1851
|
this.getCrypto = getCrypto;
|
|
1098
1852
|
this.signer = signer;
|
|
1099
1853
|
this.censusProviders = censusProviders;
|
|
1854
|
+
this.verifyCircuitFiles = config.verifyCircuitFiles ?? true;
|
|
1855
|
+
this.verifyProof = config.verifyProof ?? true;
|
|
1100
1856
|
}
|
|
1101
1857
|
/**
|
|
1102
1858
|
* Submit a vote with simplified configuration
|
|
@@ -1105,7 +1861,7 @@ class VoteOrchestrationService {
|
|
|
1105
1861
|
* - Gets census proof (Merkle or CSP)
|
|
1106
1862
|
* - Generates cryptographic proofs
|
|
1107
1863
|
* - Signs and submits the vote
|
|
1108
|
-
*
|
|
1864
|
+
*
|
|
1109
1865
|
* @param config - Simplified vote configuration
|
|
1110
1866
|
* @returns Promise resolving to vote submission result
|
|
1111
1867
|
*/
|
|
@@ -1153,7 +1909,7 @@ class VoteOrchestrationService {
|
|
|
1153
1909
|
}
|
|
1154
1910
|
/**
|
|
1155
1911
|
* Get the status of a submitted vote
|
|
1156
|
-
*
|
|
1912
|
+
*
|
|
1157
1913
|
* @param processId - The process ID
|
|
1158
1914
|
* @param voteId - The vote ID
|
|
1159
1915
|
* @returns Promise resolving to vote status information
|
|
@@ -1168,7 +1924,7 @@ class VoteOrchestrationService {
|
|
|
1168
1924
|
}
|
|
1169
1925
|
/**
|
|
1170
1926
|
* Check if an address has voted in a process
|
|
1171
|
-
*
|
|
1927
|
+
*
|
|
1172
1928
|
* @param processId - The process ID
|
|
1173
1929
|
* @param address - The voter's address
|
|
1174
1930
|
* @returns Promise resolving to boolean indicating if the address has voted
|
|
@@ -1177,26 +1933,79 @@ class VoteOrchestrationService {
|
|
|
1177
1933
|
return this.apiService.sequencer.hasAddressVoted(processId, address);
|
|
1178
1934
|
}
|
|
1179
1935
|
/**
|
|
1180
|
-
*
|
|
1181
|
-
*
|
|
1936
|
+
* Watch vote status changes in real-time using an async generator.
|
|
1937
|
+
* Yields each status change as it happens, allowing for reactive UI updates.
|
|
1938
|
+
*
|
|
1182
1939
|
* @param processId - The process ID
|
|
1183
1940
|
* @param voteId - The vote ID
|
|
1184
|
-
* @param
|
|
1185
|
-
* @
|
|
1186
|
-
*
|
|
1187
|
-
* @
|
|
1941
|
+
* @param options - Optional configuration
|
|
1942
|
+
* @returns AsyncGenerator yielding vote status updates
|
|
1943
|
+
*
|
|
1944
|
+
* @example
|
|
1945
|
+
* ```typescript
|
|
1946
|
+
* const vote = await sdk.submitVote({ processId, choices: [1] });
|
|
1947
|
+
*
|
|
1948
|
+
* for await (const statusInfo of sdk.watchVoteStatus(vote.processId, vote.voteId)) {
|
|
1949
|
+
* console.log(`Vote status: ${statusInfo.status}`);
|
|
1950
|
+
*
|
|
1951
|
+
* switch (statusInfo.status) {
|
|
1952
|
+
* case VoteStatus.Pending:
|
|
1953
|
+
* console.log("⏳ Processing...");
|
|
1954
|
+
* break;
|
|
1955
|
+
* case VoteStatus.Verified:
|
|
1956
|
+
* console.log("✓ Verified");
|
|
1957
|
+
* break;
|
|
1958
|
+
* case VoteStatus.Settled:
|
|
1959
|
+
* console.log("✅ Settled");
|
|
1960
|
+
* break;
|
|
1961
|
+
* }
|
|
1962
|
+
* }
|
|
1963
|
+
* ```
|
|
1188
1964
|
*/
|
|
1189
|
-
async
|
|
1965
|
+
async *watchVoteStatus(processId, voteId, options) {
|
|
1966
|
+
const targetStatus = options?.targetStatus ?? VoteStatus.Settled;
|
|
1967
|
+
const timeoutMs = options?.timeoutMs ?? 3e5;
|
|
1968
|
+
const pollIntervalMs = options?.pollIntervalMs ?? 5e3;
|
|
1190
1969
|
const startTime = Date.now();
|
|
1970
|
+
let previousStatus = null;
|
|
1191
1971
|
while (Date.now() - startTime < timeoutMs) {
|
|
1192
1972
|
const statusInfo = await this.getVoteStatus(processId, voteId);
|
|
1193
|
-
if (statusInfo.status
|
|
1194
|
-
|
|
1973
|
+
if (statusInfo.status !== previousStatus) {
|
|
1974
|
+
previousStatus = statusInfo.status;
|
|
1975
|
+
yield statusInfo;
|
|
1976
|
+
if (statusInfo.status === targetStatus || statusInfo.status === VoteStatus.Error) {
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1195
1979
|
}
|
|
1196
1980
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
1197
1981
|
}
|
|
1198
1982
|
throw new Error(`Vote did not reach status ${targetStatus} within ${timeoutMs}ms`);
|
|
1199
1983
|
}
|
|
1984
|
+
/**
|
|
1985
|
+
* Wait for a vote to reach a specific status.
|
|
1986
|
+
* This is a simpler alternative to watchVoteStatus() that returns only the final status.
|
|
1987
|
+
*
|
|
1988
|
+
* @param processId - The process ID
|
|
1989
|
+
* @param voteId - The vote ID
|
|
1990
|
+
* @param targetStatus - The target status to wait for (default: "settled")
|
|
1991
|
+
* @param timeoutMs - Maximum time to wait in milliseconds (default: 300000 = 5 minutes)
|
|
1992
|
+
* @param pollIntervalMs - Polling interval in milliseconds (default: 5000 = 5 seconds)
|
|
1993
|
+
* @returns Promise resolving to final vote status
|
|
1994
|
+
*/
|
|
1995
|
+
async waitForVoteStatus(processId, voteId, targetStatus = VoteStatus.Settled, timeoutMs = 3e5, pollIntervalMs = 5e3) {
|
|
1996
|
+
let finalStatus = null;
|
|
1997
|
+
for await (const statusInfo of this.watchVoteStatus(processId, voteId, {
|
|
1998
|
+
targetStatus,
|
|
1999
|
+
timeoutMs,
|
|
2000
|
+
pollIntervalMs
|
|
2001
|
+
})) {
|
|
2002
|
+
finalStatus = statusInfo;
|
|
2003
|
+
}
|
|
2004
|
+
if (!finalStatus) {
|
|
2005
|
+
throw new Error(`Vote did not reach status ${targetStatus} within ${timeoutMs}ms`);
|
|
2006
|
+
}
|
|
2007
|
+
return finalStatus;
|
|
2008
|
+
}
|
|
1200
2009
|
/**
|
|
1201
2010
|
* Get census proof based on census origin type
|
|
1202
2011
|
*/
|
|
@@ -1278,12 +2087,20 @@ class VoteOrchestrationService {
|
|
|
1278
2087
|
const circomProof = new CircomProof({
|
|
1279
2088
|
wasmUrl: info.circuitUrl,
|
|
1280
2089
|
zkeyUrl: info.provingKeyUrl,
|
|
1281
|
-
vkeyUrl: info.verificationKeyUrl
|
|
2090
|
+
vkeyUrl: info.verificationKeyUrl,
|
|
2091
|
+
// Only pass hashes if verifyCircuitFiles is enabled
|
|
2092
|
+
...this.verifyCircuitFiles && {
|
|
2093
|
+
wasmHash: info.circuitHash,
|
|
2094
|
+
zkeyHash: info.provingKeyHash,
|
|
2095
|
+
vkeyHash: info.verificationKeyHash
|
|
2096
|
+
}
|
|
1282
2097
|
});
|
|
1283
2098
|
const { proof, publicSignals } = await circomProof.generate(circomInputs);
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
2099
|
+
if (this.verifyProof) {
|
|
2100
|
+
const isValid = await circomProof.verify(proof, publicSignals);
|
|
2101
|
+
if (!isValid) {
|
|
2102
|
+
throw new Error("Generated proof is invalid");
|
|
2103
|
+
}
|
|
1287
2104
|
}
|
|
1288
2105
|
return { proof, publicSignals };
|
|
1289
2106
|
}
|
|
@@ -1321,10 +2138,7 @@ class VoteOrchestrationService {
|
|
|
1321
2138
|
class OrganizationRegistryService extends SmartContractService {
|
|
1322
2139
|
constructor(contractAddress, runner) {
|
|
1323
2140
|
super();
|
|
1324
|
-
this.contract = davinciContracts.OrganizationRegistry__factory.connect(
|
|
1325
|
-
contractAddress,
|
|
1326
|
-
runner
|
|
1327
|
-
);
|
|
2141
|
+
this.contract = davinciContracts.OrganizationRegistry__factory.connect(contractAddress, runner);
|
|
1328
2142
|
}
|
|
1329
2143
|
// ─── READ OPERATIONS ───────────────────────────────────────────────────────
|
|
1330
2144
|
async getOrganization(id) {
|
|
@@ -1384,31 +2198,36 @@ class OrganizationRegistryService extends SmartContractService {
|
|
|
1384
2198
|
}
|
|
1385
2199
|
// ─── EVENT LISTENERS ───────────────────────────────────────────────────────
|
|
1386
2200
|
onOrganizationCreated(cb) {
|
|
1387
|
-
this.
|
|
2201
|
+
this.setupEventListener(
|
|
2202
|
+
this.contract,
|
|
1388
2203
|
this.contract.filters.OrganizationCreated(),
|
|
1389
|
-
|
|
1390
|
-
);
|
|
2204
|
+
cb
|
|
2205
|
+
).catch((err) => console.error("Error setting up OrganizationCreated listener:", err));
|
|
1391
2206
|
}
|
|
1392
2207
|
onOrganizationUpdated(cb) {
|
|
1393
|
-
this.
|
|
2208
|
+
this.setupEventListener(
|
|
2209
|
+
this.contract,
|
|
1394
2210
|
this.contract.filters.OrganizationUpdated(),
|
|
1395
|
-
|
|
1396
|
-
);
|
|
2211
|
+
cb
|
|
2212
|
+
).catch((err) => console.error("Error setting up OrganizationUpdated listener:", err));
|
|
1397
2213
|
}
|
|
1398
2214
|
onAdministratorAdded(cb) {
|
|
1399
|
-
this.
|
|
2215
|
+
this.setupEventListener(
|
|
2216
|
+
this.contract,
|
|
1400
2217
|
this.contract.filters.AdministratorAdded(),
|
|
1401
|
-
|
|
1402
|
-
);
|
|
2218
|
+
cb
|
|
2219
|
+
).catch((err) => console.error("Error setting up AdministratorAdded listener:", err));
|
|
1403
2220
|
}
|
|
1404
2221
|
onAdministratorRemoved(cb) {
|
|
1405
|
-
this.
|
|
2222
|
+
this.setupEventListener(
|
|
2223
|
+
this.contract,
|
|
1406
2224
|
this.contract.filters.AdministratorRemoved(),
|
|
1407
|
-
|
|
1408
|
-
);
|
|
2225
|
+
cb
|
|
2226
|
+
).catch((err) => console.error("Error setting up AdministratorRemoved listener:", err));
|
|
1409
2227
|
}
|
|
1410
2228
|
removeAllListeners() {
|
|
1411
2229
|
this.contract.removeAllListeners();
|
|
2230
|
+
this.clearPollingIntervals();
|
|
1412
2231
|
}
|
|
1413
2232
|
}
|
|
1414
2233
|
|
|
@@ -1595,25 +2414,23 @@ class DavinciCrypto {
|
|
|
1595
2414
|
class DavinciSDK {
|
|
1596
2415
|
constructor(config) {
|
|
1597
2416
|
this.initialized = false;
|
|
1598
|
-
const
|
|
1599
|
-
environment: config.environment,
|
|
1600
|
-
customUrls: {
|
|
1601
|
-
sequencer: config.sequencerUrl,
|
|
1602
|
-
census: config.censusUrl
|
|
1603
|
-
},
|
|
1604
|
-
customChain: config.chain
|
|
1605
|
-
});
|
|
2417
|
+
const hasCustomAddresses = !!config.addresses && Object.keys(config.addresses).length > 0;
|
|
1606
2418
|
this.config = {
|
|
1607
2419
|
signer: config.signer,
|
|
1608
|
-
sequencerUrl: config.sequencerUrl
|
|
1609
|
-
censusUrl: config.censusUrl
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
2420
|
+
sequencerUrl: config.sequencerUrl,
|
|
2421
|
+
censusUrl: config.censusUrl,
|
|
2422
|
+
customAddresses: config.addresses || {},
|
|
2423
|
+
fetchAddressesFromSequencer: !hasCustomAddresses,
|
|
2424
|
+
// Automatic: fetch if no custom addresses
|
|
2425
|
+
verifyCircuitFiles: config.verifyCircuitFiles ?? true,
|
|
2426
|
+
// Default to true for security
|
|
2427
|
+
verifyProof: config.verifyProof ?? true
|
|
2428
|
+
// Default to true for security
|
|
1613
2429
|
};
|
|
1614
2430
|
this.apiService = new VocdoniApiService({
|
|
1615
2431
|
sequencerURL: this.config.sequencerUrl,
|
|
1616
|
-
censusURL: this.config.censusUrl
|
|
2432
|
+
censusURL: this.config.censusUrl || ""
|
|
2433
|
+
// Use empty string if not provided
|
|
1617
2434
|
});
|
|
1618
2435
|
this.censusProviders = config.censusProviders || {};
|
|
1619
2436
|
}
|
|
@@ -1623,9 +2440,10 @@ class DavinciSDK {
|
|
|
1623
2440
|
*/
|
|
1624
2441
|
async init() {
|
|
1625
2442
|
if (this.initialized) return;
|
|
1626
|
-
if (this.config.
|
|
1627
|
-
await this.
|
|
2443
|
+
if (this.config.fetchAddressesFromSequencer) {
|
|
2444
|
+
await this.fetchContractAddressesFromSequencer();
|
|
1628
2445
|
}
|
|
2446
|
+
if (!this.config.censusUrl) ;
|
|
1629
2447
|
this.initialized = true;
|
|
1630
2448
|
}
|
|
1631
2449
|
/**
|
|
@@ -1635,22 +2453,36 @@ class DavinciSDK {
|
|
|
1635
2453
|
return this.apiService;
|
|
1636
2454
|
}
|
|
1637
2455
|
/**
|
|
1638
|
-
* Get the process registry service for process management
|
|
2456
|
+
* Get the process registry service for process management.
|
|
2457
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2458
|
+
*
|
|
2459
|
+
* @throws Error if signer does not have a provider
|
|
1639
2460
|
*/
|
|
1640
2461
|
get processes() {
|
|
2462
|
+
this.ensureProvider();
|
|
1641
2463
|
if (!this._processRegistry) {
|
|
1642
2464
|
const processRegistryAddress = this.resolveContractAddress("processRegistry");
|
|
1643
|
-
this._processRegistry = new ProcessRegistryService(
|
|
2465
|
+
this._processRegistry = new ProcessRegistryService(
|
|
2466
|
+
processRegistryAddress,
|
|
2467
|
+
this.config.signer
|
|
2468
|
+
);
|
|
1644
2469
|
}
|
|
1645
2470
|
return this._processRegistry;
|
|
1646
2471
|
}
|
|
1647
2472
|
/**
|
|
1648
|
-
* Get the organization registry service for organization management
|
|
2473
|
+
* Get the organization registry service for organization management.
|
|
2474
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2475
|
+
*
|
|
2476
|
+
* @throws Error if signer does not have a provider
|
|
1649
2477
|
*/
|
|
1650
2478
|
get organizations() {
|
|
2479
|
+
this.ensureProvider();
|
|
1651
2480
|
if (!this._organizationRegistry) {
|
|
1652
2481
|
const organizationRegistryAddress = this.resolveContractAddress("organizationRegistry");
|
|
1653
|
-
this._organizationRegistry = new OrganizationRegistryService(
|
|
2482
|
+
this._organizationRegistry = new OrganizationRegistryService(
|
|
2483
|
+
organizationRegistryAddress,
|
|
2484
|
+
this.config.signer
|
|
2485
|
+
);
|
|
1654
2486
|
}
|
|
1655
2487
|
return this._organizationRegistry;
|
|
1656
2488
|
}
|
|
@@ -1669,9 +2501,13 @@ class DavinciSDK {
|
|
|
1669
2501
|
return this.davinciCrypto;
|
|
1670
2502
|
}
|
|
1671
2503
|
/**
|
|
1672
|
-
* Get the process orchestration service for simplified process creation
|
|
2504
|
+
* Get the process orchestration service for simplified process creation.
|
|
2505
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2506
|
+
*
|
|
2507
|
+
* @throws Error if signer does not have a provider
|
|
1673
2508
|
*/
|
|
1674
2509
|
get processOrchestrator() {
|
|
2510
|
+
this.ensureProvider();
|
|
1675
2511
|
if (!this._processOrchestrator) {
|
|
1676
2512
|
this._processOrchestrator = new ProcessOrchestrationService(
|
|
1677
2513
|
this.processes,
|
|
@@ -1692,7 +2528,11 @@ class DavinciSDK {
|
|
|
1692
2528
|
this.apiService,
|
|
1693
2529
|
() => this.getCrypto(),
|
|
1694
2530
|
this.config.signer,
|
|
1695
|
-
this.censusProviders
|
|
2531
|
+
this.censusProviders,
|
|
2532
|
+
{
|
|
2533
|
+
verifyCircuitFiles: this.config.verifyCircuitFiles,
|
|
2534
|
+
verifyProof: this.config.verifyProof
|
|
2535
|
+
}
|
|
1696
2536
|
);
|
|
1697
2537
|
}
|
|
1698
2538
|
return this._voteOrchestrator;
|
|
@@ -1701,21 +2541,24 @@ class DavinciSDK {
|
|
|
1701
2541
|
* Gets user-friendly process information from the blockchain.
|
|
1702
2542
|
* This method fetches raw contract data and transforms it into a user-friendly format
|
|
1703
2543
|
* that matches the ProcessConfig interface used for creation, plus additional runtime data.
|
|
1704
|
-
*
|
|
2544
|
+
*
|
|
2545
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2546
|
+
*
|
|
1705
2547
|
* @param processId - The process ID to fetch
|
|
1706
2548
|
* @returns Promise resolving to user-friendly process information
|
|
1707
|
-
*
|
|
2549
|
+
* @throws Error if signer does not have a provider
|
|
2550
|
+
*
|
|
1708
2551
|
* @example
|
|
1709
2552
|
* ```typescript
|
|
1710
2553
|
* const processInfo = await sdk.getProcess("0x1234567890abcdef...");
|
|
1711
|
-
*
|
|
2554
|
+
*
|
|
1712
2555
|
* // Access the same fields as ProcessConfig
|
|
1713
2556
|
* console.log("Title:", processInfo.title);
|
|
1714
2557
|
* console.log("Description:", processInfo.description);
|
|
1715
2558
|
* console.log("Questions:", processInfo.questions);
|
|
1716
2559
|
* console.log("Census size:", processInfo.census.size);
|
|
1717
2560
|
* console.log("Ballot config:", processInfo.ballot);
|
|
1718
|
-
*
|
|
2561
|
+
*
|
|
1719
2562
|
* // Plus additional runtime information
|
|
1720
2563
|
* console.log("Status:", processInfo.status);
|
|
1721
2564
|
* console.log("Creator:", processInfo.creator);
|
|
@@ -1723,7 +2566,7 @@ class DavinciSDK {
|
|
|
1723
2566
|
* console.log("End date:", processInfo.endDate);
|
|
1724
2567
|
* console.log("Duration:", processInfo.duration, "seconds");
|
|
1725
2568
|
* console.log("Time remaining:", processInfo.timeRemaining, "seconds");
|
|
1726
|
-
*
|
|
2569
|
+
*
|
|
1727
2570
|
* // Access raw contract data if needed
|
|
1728
2571
|
* console.log("Raw data:", processInfo.raw);
|
|
1729
2572
|
* ```
|
|
@@ -1732,22 +2575,106 @@ class DavinciSDK {
|
|
|
1732
2575
|
if (!this.initialized) {
|
|
1733
2576
|
throw new Error("SDK must be initialized before getting processes. Call sdk.init() first.");
|
|
1734
2577
|
}
|
|
2578
|
+
this.ensureProvider();
|
|
1735
2579
|
return this.processOrchestrator.getProcess(processId);
|
|
1736
2580
|
}
|
|
2581
|
+
/**
|
|
2582
|
+
* Creates a complete voting process and returns an async generator that yields transaction status events.
|
|
2583
|
+
* This method allows you to monitor the transaction progress in real-time, including pending, completed,
|
|
2584
|
+
* failed, and reverted states.
|
|
2585
|
+
*
|
|
2586
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2587
|
+
*
|
|
2588
|
+
* @param config - Simplified process configuration
|
|
2589
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
2590
|
+
* @throws Error if signer does not have a provider
|
|
2591
|
+
*
|
|
2592
|
+
* @example
|
|
2593
|
+
* ```typescript
|
|
2594
|
+
* const stream = sdk.createProcessStream({
|
|
2595
|
+
* title: "My Election",
|
|
2596
|
+
* description: "A simple election",
|
|
2597
|
+
* census: {
|
|
2598
|
+
* type: CensusOrigin.CensusOriginMerkleTree,
|
|
2599
|
+
* root: "0x1234...",
|
|
2600
|
+
* size: 100,
|
|
2601
|
+
* uri: "ipfs://..."
|
|
2602
|
+
* },
|
|
2603
|
+
* ballot: {
|
|
2604
|
+
* numFields: 2,
|
|
2605
|
+
* maxValue: "3",
|
|
2606
|
+
* minValue: "0",
|
|
2607
|
+
* uniqueValues: false,
|
|
2608
|
+
* costFromWeight: false,
|
|
2609
|
+
* costExponent: 10000,
|
|
2610
|
+
* maxValueSum: "6",
|
|
2611
|
+
* minValueSum: "0"
|
|
2612
|
+
* },
|
|
2613
|
+
* timing: {
|
|
2614
|
+
* startDate: new Date("2024-12-01T10:00:00Z"),
|
|
2615
|
+
* duration: 3600 * 24
|
|
2616
|
+
* },
|
|
2617
|
+
* questions: [
|
|
2618
|
+
* {
|
|
2619
|
+
* title: "What is your favorite color?",
|
|
2620
|
+
* choices: [
|
|
2621
|
+
* { title: "Red", value: 0 },
|
|
2622
|
+
* { title: "Blue", value: 1 }
|
|
2623
|
+
* ]
|
|
2624
|
+
* }
|
|
2625
|
+
* ]
|
|
2626
|
+
* });
|
|
2627
|
+
*
|
|
2628
|
+
* // Monitor transaction progress
|
|
2629
|
+
* for await (const event of stream) {
|
|
2630
|
+
* switch (event.status) {
|
|
2631
|
+
* case TxStatus.Pending:
|
|
2632
|
+
* console.log("Transaction pending:", event.hash);
|
|
2633
|
+
* // Update UI to show pending state
|
|
2634
|
+
* break;
|
|
2635
|
+
* case TxStatus.Completed:
|
|
2636
|
+
* console.log("Process created:", event.response.processId);
|
|
2637
|
+
* console.log("Transaction hash:", event.response.transactionHash);
|
|
2638
|
+
* // Update UI to show success
|
|
2639
|
+
* break;
|
|
2640
|
+
* case TxStatus.Failed:
|
|
2641
|
+
* console.error("Transaction failed:", event.error);
|
|
2642
|
+
* // Update UI to show error
|
|
2643
|
+
* break;
|
|
2644
|
+
* case TxStatus.Reverted:
|
|
2645
|
+
* console.error("Transaction reverted:", event.reason);
|
|
2646
|
+
* // Update UI to show revert reason
|
|
2647
|
+
* break;
|
|
2648
|
+
* }
|
|
2649
|
+
* }
|
|
2650
|
+
* ```
|
|
2651
|
+
*/
|
|
2652
|
+
createProcessStream(config) {
|
|
2653
|
+
if (!this.initialized) {
|
|
2654
|
+
throw new Error("SDK must be initialized before creating processes. Call sdk.init() first.");
|
|
2655
|
+
}
|
|
2656
|
+
this.ensureProvider();
|
|
2657
|
+
return this.processOrchestrator.createProcessStream(config);
|
|
2658
|
+
}
|
|
1737
2659
|
/**
|
|
1738
2660
|
* Creates a complete voting process with minimal configuration.
|
|
1739
2661
|
* This is the ultra-easy method for end users that handles all the complex orchestration internally.
|
|
1740
|
-
*
|
|
2662
|
+
*
|
|
2663
|
+
* For real-time transaction status updates, use createProcessStream() instead.
|
|
2664
|
+
*
|
|
2665
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2666
|
+
*
|
|
1741
2667
|
* The method automatically:
|
|
1742
2668
|
* - Gets encryption keys and initial state root from the sequencer
|
|
1743
2669
|
* - Handles process creation signatures
|
|
1744
2670
|
* - Coordinates between sequencer API and on-chain contract calls
|
|
1745
2671
|
* - Creates and pushes metadata
|
|
1746
2672
|
* - Submits the on-chain transaction
|
|
1747
|
-
*
|
|
2673
|
+
*
|
|
1748
2674
|
* @param config - Simplified process configuration
|
|
1749
2675
|
* @returns Promise resolving to the process creation result
|
|
1750
|
-
*
|
|
2676
|
+
* @throws Error if signer does not have a provider
|
|
2677
|
+
*
|
|
1751
2678
|
* @example
|
|
1752
2679
|
* ```typescript
|
|
1753
2680
|
* // Option 1: Using duration (traditional approach)
|
|
@@ -1784,7 +2711,7 @@ class DavinciSDK {
|
|
|
1784
2711
|
* }
|
|
1785
2712
|
* ]
|
|
1786
2713
|
* });
|
|
1787
|
-
*
|
|
2714
|
+
*
|
|
1788
2715
|
* // Option 2: Using start and end dates
|
|
1789
2716
|
* const result2 = await sdk.createProcess({
|
|
1790
2717
|
* title: "Weekend Vote",
|
|
@@ -1799,21 +2726,26 @@ class DavinciSDK {
|
|
|
1799
2726
|
if (!this.initialized) {
|
|
1800
2727
|
throw new Error("SDK must be initialized before creating processes. Call sdk.init() first.");
|
|
1801
2728
|
}
|
|
2729
|
+
this.ensureProvider();
|
|
1802
2730
|
return this.processOrchestrator.createProcess(config);
|
|
1803
2731
|
}
|
|
1804
2732
|
/**
|
|
1805
2733
|
* Submit a vote with simplified configuration.
|
|
1806
2734
|
* This is the ultra-easy method for end users that handles all the complex voting workflow internally.
|
|
1807
|
-
*
|
|
2735
|
+
*
|
|
2736
|
+
* Does NOT require a provider - can be used with a bare Wallet for signing only.
|
|
2737
|
+
* IMPORTANT: Requires censusUrl to be configured in the SDK for fetching census proofs (unless using custom census providers).
|
|
2738
|
+
*
|
|
1808
2739
|
* The method automatically:
|
|
1809
2740
|
* - Fetches process information and validates voting is allowed
|
|
1810
2741
|
* - Gets census proof (Merkle tree based)
|
|
1811
2742
|
* - Generates cryptographic proofs and encrypts the vote
|
|
1812
2743
|
* - Signs and submits the vote to the sequencer
|
|
1813
|
-
*
|
|
2744
|
+
*
|
|
1814
2745
|
* @param config - Simplified vote configuration
|
|
1815
2746
|
* @returns Promise resolving to vote submission result
|
|
1816
|
-
*
|
|
2747
|
+
* @throws Error if censusUrl is not configured (unless using custom census providers)
|
|
2748
|
+
*
|
|
1817
2749
|
* @example
|
|
1818
2750
|
* ```typescript
|
|
1819
2751
|
* // Submit a vote with voter's private key
|
|
@@ -1822,14 +2754,14 @@ class DavinciSDK {
|
|
|
1822
2754
|
* choices: [1, 0], // Vote for option 1 in question 1, option 0 in question 2
|
|
1823
2755
|
* voterKey: "0x1234567890abcdef..." // Voter's private key
|
|
1824
2756
|
* });
|
|
1825
|
-
*
|
|
2757
|
+
*
|
|
1826
2758
|
* console.log("Vote ID:", voteResult.voteId);
|
|
1827
2759
|
* console.log("Status:", voteResult.status);
|
|
1828
|
-
*
|
|
2760
|
+
*
|
|
1829
2761
|
* // Submit a vote with a Wallet instance
|
|
1830
2762
|
* import { Wallet } from "ethers";
|
|
1831
2763
|
* const voterWallet = new Wallet("0x...");
|
|
1832
|
-
*
|
|
2764
|
+
*
|
|
1833
2765
|
* const voteResult2 = await sdk.submitVote({
|
|
1834
2766
|
* processId: "0x1234567890abcdef...",
|
|
1835
2767
|
* choices: [2], // Single question vote
|
|
@@ -1841,15 +2773,22 @@ class DavinciSDK {
|
|
|
1841
2773
|
if (!this.initialized) {
|
|
1842
2774
|
throw new Error("SDK must be initialized before submitting votes. Call sdk.init() first.");
|
|
1843
2775
|
}
|
|
2776
|
+
if (!this.config.censusUrl && !this.censusProviders.merkle && !this.censusProviders.csp) {
|
|
2777
|
+
throw new Error(
|
|
2778
|
+
"Census URL is required for voting. Provide censusUrl in the SDK constructor config, or use custom census providers."
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
1844
2781
|
return this.voteOrchestrator.submitVote(config);
|
|
1845
2782
|
}
|
|
1846
2783
|
/**
|
|
1847
2784
|
* Get the status of a submitted vote.
|
|
1848
|
-
*
|
|
2785
|
+
*
|
|
2786
|
+
* Does NOT require a provider - uses API calls only.
|
|
2787
|
+
*
|
|
1849
2788
|
* @param processId - The process ID
|
|
1850
2789
|
* @param voteId - The vote ID returned from submitVote()
|
|
1851
2790
|
* @returns Promise resolving to vote status information
|
|
1852
|
-
*
|
|
2791
|
+
*
|
|
1853
2792
|
* @example
|
|
1854
2793
|
* ```typescript
|
|
1855
2794
|
* const statusInfo = await sdk.getVoteStatus(processId, voteId);
|
|
@@ -1865,11 +2804,13 @@ class DavinciSDK {
|
|
|
1865
2804
|
}
|
|
1866
2805
|
/**
|
|
1867
2806
|
* Check if an address has voted in a process.
|
|
1868
|
-
*
|
|
2807
|
+
*
|
|
2808
|
+
* Does NOT require a provider - uses API calls only.
|
|
2809
|
+
*
|
|
1869
2810
|
* @param processId - The process ID
|
|
1870
2811
|
* @param address - The voter's address
|
|
1871
2812
|
* @returns Promise resolving to boolean indicating if the address has voted
|
|
1872
|
-
*
|
|
2813
|
+
*
|
|
1873
2814
|
* @example
|
|
1874
2815
|
* ```typescript
|
|
1875
2816
|
* const hasVoted = await sdk.hasAddressVoted(processId, "0x1234567890abcdef...");
|
|
@@ -1880,108 +2821,415 @@ class DavinciSDK {
|
|
|
1880
2821
|
*/
|
|
1881
2822
|
async hasAddressVoted(processId, address) {
|
|
1882
2823
|
if (!this.initialized) {
|
|
1883
|
-
throw new Error(
|
|
2824
|
+
throw new Error(
|
|
2825
|
+
"SDK must be initialized before checking vote status. Call sdk.init() first."
|
|
2826
|
+
);
|
|
1884
2827
|
}
|
|
1885
2828
|
return this.voteOrchestrator.hasAddressVoted(processId, address);
|
|
1886
2829
|
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Watch vote status changes in real-time using an async generator.
|
|
2832
|
+
* This method yields each status change as it happens, perfect for showing
|
|
2833
|
+
* progress indicators in UI applications.
|
|
2834
|
+
*
|
|
2835
|
+
* Does NOT require a provider - uses API calls only.
|
|
2836
|
+
*
|
|
2837
|
+
* @param processId - The process ID
|
|
2838
|
+
* @param voteId - The vote ID
|
|
2839
|
+
* @param options - Optional configuration
|
|
2840
|
+
* @returns AsyncGenerator yielding vote status updates
|
|
2841
|
+
*
|
|
2842
|
+
* @example
|
|
2843
|
+
* ```typescript
|
|
2844
|
+
* // Submit vote
|
|
2845
|
+
* const voteResult = await sdk.submitVote({
|
|
2846
|
+
* processId: "0x1234567890abcdef...",
|
|
2847
|
+
* choices: [1]
|
|
2848
|
+
* });
|
|
2849
|
+
*
|
|
2850
|
+
* // Watch status changes in real-time
|
|
2851
|
+
* for await (const statusInfo of sdk.watchVoteStatus(voteResult.processId, voteResult.voteId)) {
|
|
2852
|
+
* console.log(`Vote status: ${statusInfo.status}`);
|
|
2853
|
+
*
|
|
2854
|
+
* switch (statusInfo.status) {
|
|
2855
|
+
* case VoteStatus.Pending:
|
|
2856
|
+
* console.log("⏳ Processing...");
|
|
2857
|
+
* break;
|
|
2858
|
+
* case VoteStatus.Verified:
|
|
2859
|
+
* console.log("✓ Vote verified");
|
|
2860
|
+
* break;
|
|
2861
|
+
* case VoteStatus.Aggregated:
|
|
2862
|
+
* console.log("📊 Vote aggregated");
|
|
2863
|
+
* break;
|
|
2864
|
+
* case VoteStatus.Settled:
|
|
2865
|
+
* console.log("✅ Vote settled");
|
|
2866
|
+
* break;
|
|
2867
|
+
* }
|
|
2868
|
+
* }
|
|
2869
|
+
* ```
|
|
2870
|
+
*/
|
|
2871
|
+
watchVoteStatus(processId, voteId, options) {
|
|
2872
|
+
if (!this.initialized) {
|
|
2873
|
+
throw new Error(
|
|
2874
|
+
"SDK must be initialized before watching vote status. Call sdk.init() first."
|
|
2875
|
+
);
|
|
2876
|
+
}
|
|
2877
|
+
return this.voteOrchestrator.watchVoteStatus(processId, voteId, options);
|
|
2878
|
+
}
|
|
1887
2879
|
/**
|
|
1888
2880
|
* Wait for a vote to reach a specific status.
|
|
1889
|
-
*
|
|
1890
|
-
*
|
|
2881
|
+
* This is a simpler alternative to watchVoteStatus() that returns only the final status.
|
|
2882
|
+
* Useful for waiting for vote confirmation and processing without needing to handle each intermediate status.
|
|
2883
|
+
*
|
|
2884
|
+
* Does NOT require a provider - uses API calls only.
|
|
2885
|
+
*
|
|
1891
2886
|
* @param processId - The process ID
|
|
1892
2887
|
* @param voteId - The vote ID
|
|
1893
2888
|
* @param targetStatus - The target status to wait for (default: "settled")
|
|
1894
2889
|
* @param timeoutMs - Maximum time to wait in milliseconds (default: 300000 = 5 minutes)
|
|
1895
2890
|
* @param pollIntervalMs - Polling interval in milliseconds (default: 5000 = 5 seconds)
|
|
1896
2891
|
* @returns Promise resolving to final vote status
|
|
1897
|
-
*
|
|
2892
|
+
*
|
|
1898
2893
|
* @example
|
|
1899
2894
|
* ```typescript
|
|
1900
2895
|
* // Submit vote and wait for it to be settled
|
|
1901
2896
|
* const voteResult = await sdk.submitVote({
|
|
1902
2897
|
* processId: "0x1234567890abcdef...",
|
|
1903
|
-
* choices: [1]
|
|
1904
|
-
* voterKey: "0x..."
|
|
2898
|
+
* choices: [1]
|
|
1905
2899
|
* });
|
|
1906
|
-
*
|
|
2900
|
+
*
|
|
1907
2901
|
* // Wait for the vote to be fully processed
|
|
1908
2902
|
* const finalStatus = await sdk.waitForVoteStatus(
|
|
1909
2903
|
* voteResult.processId,
|
|
1910
2904
|
* voteResult.voteId,
|
|
1911
|
-
*
|
|
2905
|
+
* VoteStatus.Settled, // Wait until vote is settled
|
|
1912
2906
|
* 300000, // 5 minute timeout
|
|
1913
2907
|
* 5000 // Check every 5 seconds
|
|
1914
2908
|
* );
|
|
1915
|
-
*
|
|
2909
|
+
*
|
|
1916
2910
|
* console.log("Vote final status:", finalStatus.status);
|
|
1917
2911
|
* ```
|
|
1918
2912
|
*/
|
|
1919
2913
|
async waitForVoteStatus(processId, voteId, targetStatus = VoteStatus.Settled, timeoutMs = 3e5, pollIntervalMs = 5e3) {
|
|
1920
2914
|
if (!this.initialized) {
|
|
1921
|
-
throw new Error(
|
|
2915
|
+
throw new Error(
|
|
2916
|
+
"SDK must be initialized before waiting for vote status. Call sdk.init() first."
|
|
2917
|
+
);
|
|
1922
2918
|
}
|
|
1923
|
-
return this.voteOrchestrator.waitForVoteStatus(
|
|
2919
|
+
return this.voteOrchestrator.waitForVoteStatus(
|
|
2920
|
+
processId,
|
|
2921
|
+
voteId,
|
|
2922
|
+
targetStatus,
|
|
2923
|
+
timeoutMs,
|
|
2924
|
+
pollIntervalMs
|
|
2925
|
+
);
|
|
1924
2926
|
}
|
|
1925
2927
|
/**
|
|
1926
|
-
*
|
|
1927
|
-
*
|
|
1928
|
-
*
|
|
1929
|
-
*
|
|
2928
|
+
* Ends a voting process by setting its status to ENDED and returns an async generator
|
|
2929
|
+
* that yields transaction status events. This method allows you to monitor the
|
|
2930
|
+
* transaction progress in real-time.
|
|
2931
|
+
*
|
|
2932
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2933
|
+
*
|
|
2934
|
+
* @param processId - The process ID to end
|
|
2935
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
2936
|
+
* @throws Error if signer does not have a provider
|
|
2937
|
+
*
|
|
2938
|
+
* @example
|
|
2939
|
+
* ```typescript
|
|
2940
|
+
* const stream = sdk.endProcessStream("0x1234567890abcdef...");
|
|
2941
|
+
*
|
|
2942
|
+
* for await (const event of stream) {
|
|
2943
|
+
* switch (event.status) {
|
|
2944
|
+
* case TxStatus.Pending:
|
|
2945
|
+
* console.log("Transaction pending:", event.hash);
|
|
2946
|
+
* break;
|
|
2947
|
+
* case TxStatus.Completed:
|
|
2948
|
+
* console.log("Process ended successfully");
|
|
2949
|
+
* break;
|
|
2950
|
+
* case TxStatus.Failed:
|
|
2951
|
+
* console.error("Transaction failed:", event.error);
|
|
2952
|
+
* break;
|
|
2953
|
+
* case TxStatus.Reverted:
|
|
2954
|
+
* console.error("Transaction reverted:", event.reason);
|
|
2955
|
+
* break;
|
|
2956
|
+
* }
|
|
2957
|
+
* }
|
|
2958
|
+
* ```
|
|
1930
2959
|
*/
|
|
1931
|
-
|
|
1932
|
-
if (this.
|
|
1933
|
-
|
|
2960
|
+
endProcessStream(processId) {
|
|
2961
|
+
if (!this.initialized) {
|
|
2962
|
+
throw new Error("SDK must be initialized before ending processes. Call sdk.init() first.");
|
|
1934
2963
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
2964
|
+
this.ensureProvider();
|
|
2965
|
+
return this.processOrchestrator.endProcessStream(processId);
|
|
2966
|
+
}
|
|
2967
|
+
/**
|
|
2968
|
+
* Ends a voting process by setting its status to ENDED.
|
|
2969
|
+
* This is the simplified method that waits for transaction completion.
|
|
2970
|
+
*
|
|
2971
|
+
* For real-time transaction status updates, use endProcessStream() instead.
|
|
2972
|
+
*
|
|
2973
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2974
|
+
*
|
|
2975
|
+
* @param processId - The process ID to end
|
|
2976
|
+
* @returns Promise resolving when the process is ended
|
|
2977
|
+
* @throws Error if signer does not have a provider
|
|
2978
|
+
*
|
|
2979
|
+
* @example
|
|
2980
|
+
* ```typescript
|
|
2981
|
+
* await sdk.endProcess("0x1234567890abcdef...");
|
|
2982
|
+
* console.log("Process ended successfully");
|
|
2983
|
+
* ```
|
|
2984
|
+
*/
|
|
2985
|
+
async endProcess(processId) {
|
|
2986
|
+
if (!this.initialized) {
|
|
2987
|
+
throw new Error("SDK must be initialized before ending processes. Call sdk.init() first.");
|
|
1938
2988
|
}
|
|
1939
|
-
|
|
2989
|
+
this.ensureProvider();
|
|
2990
|
+
return this.processOrchestrator.endProcess(processId);
|
|
1940
2991
|
}
|
|
1941
2992
|
/**
|
|
1942
|
-
*
|
|
2993
|
+
* Pauses a voting process by setting its status to PAUSED and returns an async generator
|
|
2994
|
+
* that yields transaction status events. This method allows you to monitor the
|
|
2995
|
+
* transaction progress in real-time.
|
|
2996
|
+
*
|
|
2997
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
2998
|
+
*
|
|
2999
|
+
* @param processId - The process ID to pause
|
|
3000
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
3001
|
+
* @throws Error if signer does not have a provider
|
|
3002
|
+
*
|
|
3003
|
+
* @example
|
|
3004
|
+
* ```typescript
|
|
3005
|
+
* const stream = sdk.pauseProcessStream("0x1234567890abcdef...");
|
|
3006
|
+
*
|
|
3007
|
+
* for await (const event of stream) {
|
|
3008
|
+
* switch (event.status) {
|
|
3009
|
+
* case TxStatus.Pending:
|
|
3010
|
+
* console.log("Transaction pending:", event.hash);
|
|
3011
|
+
* break;
|
|
3012
|
+
* case TxStatus.Completed:
|
|
3013
|
+
* console.log("Process paused successfully");
|
|
3014
|
+
* break;
|
|
3015
|
+
* case TxStatus.Failed:
|
|
3016
|
+
* console.error("Transaction failed:", event.error);
|
|
3017
|
+
* break;
|
|
3018
|
+
* case TxStatus.Reverted:
|
|
3019
|
+
* console.error("Transaction reverted:", event.reason);
|
|
3020
|
+
* break;
|
|
3021
|
+
* }
|
|
3022
|
+
* }
|
|
3023
|
+
* ```
|
|
1943
3024
|
*/
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
3025
|
+
pauseProcessStream(processId) {
|
|
3026
|
+
if (!this.initialized) {
|
|
3027
|
+
throw new Error("SDK must be initialized before pausing processes. Call sdk.init() first.");
|
|
3028
|
+
}
|
|
3029
|
+
this.ensureProvider();
|
|
3030
|
+
return this.processOrchestrator.pauseProcessStream(processId);
|
|
3031
|
+
}
|
|
3032
|
+
/**
|
|
3033
|
+
* Pauses a voting process by setting its status to PAUSED.
|
|
3034
|
+
* This is the simplified method that waits for transaction completion.
|
|
3035
|
+
*
|
|
3036
|
+
* For real-time transaction status updates, use pauseProcessStream() instead.
|
|
3037
|
+
*
|
|
3038
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3039
|
+
*
|
|
3040
|
+
* @param processId - The process ID to pause
|
|
3041
|
+
* @returns Promise resolving when the process is paused
|
|
3042
|
+
* @throws Error if signer does not have a provider
|
|
3043
|
+
*
|
|
3044
|
+
* @example
|
|
3045
|
+
* ```typescript
|
|
3046
|
+
* await sdk.pauseProcess("0x1234567890abcdef...");
|
|
3047
|
+
* console.log("Process paused successfully");
|
|
3048
|
+
* ```
|
|
3049
|
+
*/
|
|
3050
|
+
async pauseProcess(processId) {
|
|
3051
|
+
if (!this.initialized) {
|
|
3052
|
+
throw new Error("SDK must be initialized before pausing processes. Call sdk.init() first.");
|
|
3053
|
+
}
|
|
3054
|
+
this.ensureProvider();
|
|
3055
|
+
return this.processOrchestrator.pauseProcess(processId);
|
|
3056
|
+
}
|
|
3057
|
+
/**
|
|
3058
|
+
* Cancels a voting process by setting its status to CANCELED and returns an async generator
|
|
3059
|
+
* that yields transaction status events. This method allows you to monitor the
|
|
3060
|
+
* transaction progress in real-time.
|
|
3061
|
+
*
|
|
3062
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3063
|
+
*
|
|
3064
|
+
* @param processId - The process ID to cancel
|
|
3065
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
3066
|
+
* @throws Error if signer does not have a provider
|
|
3067
|
+
*
|
|
3068
|
+
* @example
|
|
3069
|
+
* ```typescript
|
|
3070
|
+
* const stream = sdk.cancelProcessStream("0x1234567890abcdef...");
|
|
3071
|
+
*
|
|
3072
|
+
* for await (const event of stream) {
|
|
3073
|
+
* switch (event.status) {
|
|
3074
|
+
* case TxStatus.Pending:
|
|
3075
|
+
* console.log("Transaction pending:", event.hash);
|
|
3076
|
+
* break;
|
|
3077
|
+
* case TxStatus.Completed:
|
|
3078
|
+
* console.log("Process canceled successfully");
|
|
3079
|
+
* break;
|
|
3080
|
+
* case TxStatus.Failed:
|
|
3081
|
+
* console.error("Transaction failed:", event.error);
|
|
3082
|
+
* break;
|
|
3083
|
+
* case TxStatus.Reverted:
|
|
3084
|
+
* console.error("Transaction reverted:", event.reason);
|
|
3085
|
+
* break;
|
|
3086
|
+
* }
|
|
3087
|
+
* }
|
|
3088
|
+
* ```
|
|
3089
|
+
*/
|
|
3090
|
+
cancelProcessStream(processId) {
|
|
3091
|
+
if (!this.initialized) {
|
|
3092
|
+
throw new Error("SDK must be initialized before canceling processes. Call sdk.init() first.");
|
|
3093
|
+
}
|
|
3094
|
+
this.ensureProvider();
|
|
3095
|
+
return this.processOrchestrator.cancelProcessStream(processId);
|
|
3096
|
+
}
|
|
3097
|
+
/**
|
|
3098
|
+
* Cancels a voting process by setting its status to CANCELED.
|
|
3099
|
+
* This is the simplified method that waits for transaction completion.
|
|
3100
|
+
*
|
|
3101
|
+
* For real-time transaction status updates, use cancelProcessStream() instead.
|
|
3102
|
+
*
|
|
3103
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3104
|
+
*
|
|
3105
|
+
* @param processId - The process ID to cancel
|
|
3106
|
+
* @returns Promise resolving when the process is canceled
|
|
3107
|
+
* @throws Error if signer does not have a provider
|
|
3108
|
+
*
|
|
3109
|
+
* @example
|
|
3110
|
+
* ```typescript
|
|
3111
|
+
* await sdk.cancelProcess("0x1234567890abcdef...");
|
|
3112
|
+
* console.log("Process canceled successfully");
|
|
3113
|
+
* ```
|
|
3114
|
+
*/
|
|
3115
|
+
async cancelProcess(processId) {
|
|
3116
|
+
if (!this.initialized) {
|
|
3117
|
+
throw new Error("SDK must be initialized before canceling processes. Call sdk.init() first.");
|
|
3118
|
+
}
|
|
3119
|
+
this.ensureProvider();
|
|
3120
|
+
return this.processOrchestrator.cancelProcess(processId);
|
|
3121
|
+
}
|
|
3122
|
+
/**
|
|
3123
|
+
* Resumes a voting process by setting its status to READY and returns an async generator
|
|
3124
|
+
* that yields transaction status events. This is typically used to resume a paused process.
|
|
3125
|
+
*
|
|
3126
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3127
|
+
*
|
|
3128
|
+
* @param processId - The process ID to resume
|
|
3129
|
+
* @returns AsyncGenerator yielding transaction status events
|
|
3130
|
+
* @throws Error if signer does not have a provider
|
|
3131
|
+
*
|
|
3132
|
+
* @example
|
|
3133
|
+
* ```typescript
|
|
3134
|
+
* const stream = sdk.resumeProcessStream("0x1234567890abcdef...");
|
|
3135
|
+
*
|
|
3136
|
+
* for await (const event of stream) {
|
|
3137
|
+
* switch (event.status) {
|
|
3138
|
+
* case TxStatus.Pending:
|
|
3139
|
+
* console.log("Transaction pending:", event.hash);
|
|
3140
|
+
* break;
|
|
3141
|
+
* case TxStatus.Completed:
|
|
3142
|
+
* console.log("Process resumed successfully");
|
|
3143
|
+
* break;
|
|
3144
|
+
* case TxStatus.Failed:
|
|
3145
|
+
* console.error("Transaction failed:", event.error);
|
|
3146
|
+
* break;
|
|
3147
|
+
* case TxStatus.Reverted:
|
|
3148
|
+
* console.error("Transaction reverted:", event.reason);
|
|
3149
|
+
* break;
|
|
3150
|
+
* }
|
|
3151
|
+
* }
|
|
3152
|
+
* ```
|
|
3153
|
+
*/
|
|
3154
|
+
resumeProcessStream(processId) {
|
|
3155
|
+
if (!this.initialized) {
|
|
3156
|
+
throw new Error("SDK must be initialized before resuming processes. Call sdk.init() first.");
|
|
3157
|
+
}
|
|
3158
|
+
this.ensureProvider();
|
|
3159
|
+
return this.processOrchestrator.resumeProcessStream(processId);
|
|
3160
|
+
}
|
|
3161
|
+
/**
|
|
3162
|
+
* Resumes a voting process by setting its status to READY.
|
|
3163
|
+
* This is typically used to resume a paused process.
|
|
3164
|
+
* This is the simplified method that waits for transaction completion.
|
|
3165
|
+
*
|
|
3166
|
+
* For real-time transaction status updates, use resumeProcessStream() instead.
|
|
3167
|
+
*
|
|
3168
|
+
* Requires a signer with a provider for blockchain interactions.
|
|
3169
|
+
*
|
|
3170
|
+
* @param processId - The process ID to resume
|
|
3171
|
+
* @returns Promise resolving when the process is resumed
|
|
3172
|
+
* @throws Error if signer does not have a provider
|
|
3173
|
+
*
|
|
3174
|
+
* @example
|
|
3175
|
+
* ```typescript
|
|
3176
|
+
* await sdk.resumeProcess("0x1234567890abcdef...");
|
|
3177
|
+
* console.log("Process resumed successfully");
|
|
3178
|
+
* ```
|
|
3179
|
+
*/
|
|
3180
|
+
async resumeProcess(processId) {
|
|
3181
|
+
if (!this.initialized) {
|
|
3182
|
+
throw new Error("SDK must be initialized before resuming processes. Call sdk.init() first.");
|
|
3183
|
+
}
|
|
3184
|
+
this.ensureProvider();
|
|
3185
|
+
return this.processOrchestrator.resumeProcess(processId);
|
|
3186
|
+
}
|
|
3187
|
+
/**
|
|
3188
|
+
* Resolve contract address based on configuration priority:
|
|
3189
|
+
* 1. Custom addresses from user config (if provided)
|
|
3190
|
+
* 2. Addresses from sequencer (fetched during init if no custom addresses provided)
|
|
3191
|
+
*/
|
|
3192
|
+
resolveContractAddress(contractName) {
|
|
3193
|
+
const customAddress = this.config.customAddresses[contractName];
|
|
3194
|
+
if (customAddress) {
|
|
3195
|
+
return customAddress;
|
|
1959
3196
|
}
|
|
3197
|
+
if (!this.config.customAddresses[contractName]) {
|
|
3198
|
+
throw new Error(
|
|
3199
|
+
`Contract address for '${contractName}' not found. Make sure SDK is initialized with sdk.init() or provide custom addresses in config.`
|
|
3200
|
+
);
|
|
3201
|
+
}
|
|
3202
|
+
return this.config.customAddresses[contractName];
|
|
1960
3203
|
}
|
|
1961
3204
|
/**
|
|
1962
|
-
*
|
|
1963
|
-
*
|
|
3205
|
+
* Fetch contract addresses from sequencer info
|
|
3206
|
+
* This is called during init() if custom addresses are not provided
|
|
1964
3207
|
*/
|
|
1965
|
-
async
|
|
3208
|
+
async fetchContractAddressesFromSequencer() {
|
|
1966
3209
|
try {
|
|
1967
3210
|
const info = await this.apiService.sequencer.getInfo();
|
|
1968
3211
|
const contracts = info.contracts;
|
|
1969
3212
|
if (contracts.process) {
|
|
3213
|
+
this.config.customAddresses.processRegistry = contracts.process;
|
|
1970
3214
|
this._processRegistry = new ProcessRegistryService(contracts.process, this.config.signer);
|
|
1971
|
-
this.config.contractAddresses.processRegistry = contracts.process;
|
|
1972
3215
|
}
|
|
1973
3216
|
if (contracts.organization) {
|
|
1974
|
-
this.
|
|
1975
|
-
this.
|
|
3217
|
+
this.config.customAddresses.organizationRegistry = contracts.organization;
|
|
3218
|
+
this._organizationRegistry = new OrganizationRegistryService(
|
|
3219
|
+
contracts.organization,
|
|
3220
|
+
this.config.signer
|
|
3221
|
+
);
|
|
1976
3222
|
}
|
|
1977
3223
|
if (contracts.stateTransitionVerifier) {
|
|
1978
|
-
this.config.
|
|
3224
|
+
this.config.customAddresses.stateTransitionVerifier = contracts.stateTransitionVerifier;
|
|
1979
3225
|
}
|
|
1980
3226
|
if (contracts.resultsVerifier) {
|
|
1981
|
-
this.config.
|
|
3227
|
+
this.config.customAddresses.resultsVerifier = contracts.resultsVerifier;
|
|
1982
3228
|
}
|
|
1983
3229
|
} catch (error) {
|
|
1984
|
-
|
|
3230
|
+
throw new Error(
|
|
3231
|
+
`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.`
|
|
3232
|
+
);
|
|
1985
3233
|
}
|
|
1986
3234
|
}
|
|
1987
3235
|
/**
|
|
@@ -1996,13 +3244,28 @@ class DavinciSDK {
|
|
|
1996
3244
|
isInitialized() {
|
|
1997
3245
|
return this.initialized;
|
|
1998
3246
|
}
|
|
3247
|
+
/**
|
|
3248
|
+
* Ensures that the signer has a provider for blockchain operations.
|
|
3249
|
+
* @throws Error if the signer does not have a provider
|
|
3250
|
+
* @private
|
|
3251
|
+
*/
|
|
3252
|
+
ensureProvider() {
|
|
3253
|
+
if (!this.config.signer.provider) {
|
|
3254
|
+
throw new Error(
|
|
3255
|
+
"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."
|
|
3256
|
+
);
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
1999
3259
|
}
|
|
2000
3260
|
|
|
2001
3261
|
exports.BaseService = BaseService;
|
|
3262
|
+
exports.Census = Census;
|
|
3263
|
+
exports.CensusOrchestrator = CensusOrchestrator;
|
|
2002
3264
|
exports.CensusOrigin = CensusOrigin;
|
|
3265
|
+
exports.CensusType = CensusType;
|
|
2003
3266
|
exports.CircomProof = CircomProof;
|
|
2004
3267
|
exports.ContractServiceError = ContractServiceError;
|
|
2005
|
-
exports.
|
|
3268
|
+
exports.CspCensus = CspCensus;
|
|
2006
3269
|
exports.DavinciCrypto = DavinciCrypto;
|
|
2007
3270
|
exports.DavinciSDK = DavinciSDK;
|
|
2008
3271
|
exports.ElectionMetadataTemplate = ElectionMetadataTemplate;
|
|
@@ -2012,6 +3275,7 @@ exports.OrganizationCreateError = OrganizationCreateError;
|
|
|
2012
3275
|
exports.OrganizationDeleteError = OrganizationDeleteError;
|
|
2013
3276
|
exports.OrganizationRegistryService = OrganizationRegistryService;
|
|
2014
3277
|
exports.OrganizationUpdateError = OrganizationUpdateError;
|
|
3278
|
+
exports.PlainCensus = PlainCensus;
|
|
2015
3279
|
exports.ProcessCensusError = ProcessCensusError;
|
|
2016
3280
|
exports.ProcessCreateError = ProcessCreateError;
|
|
2017
3281
|
exports.ProcessDurationError = ProcessDurationError;
|
|
@@ -2021,6 +3285,7 @@ exports.ProcessResultError = ProcessResultError;
|
|
|
2021
3285
|
exports.ProcessStateTransitionError = ProcessStateTransitionError;
|
|
2022
3286
|
exports.ProcessStatus = ProcessStatus;
|
|
2023
3287
|
exports.ProcessStatusError = ProcessStatusError;
|
|
3288
|
+
exports.PublishedCensus = PublishedCensus;
|
|
2024
3289
|
exports.SmartContractService = SmartContractService;
|
|
2025
3290
|
exports.TxStatus = TxStatus;
|
|
2026
3291
|
exports.VocdoniApiService = VocdoniApiService;
|
|
@@ -2028,18 +3293,13 @@ exports.VocdoniCensusService = VocdoniCensusService;
|
|
|
2028
3293
|
exports.VocdoniSequencerService = VocdoniSequencerService;
|
|
2029
3294
|
exports.VoteOrchestrationService = VoteOrchestrationService;
|
|
2030
3295
|
exports.VoteStatus = VoteStatus;
|
|
3296
|
+
exports.WeightedCensus = WeightedCensus;
|
|
2031
3297
|
exports.assertCSPCensusProof = assertCSPCensusProof;
|
|
2032
3298
|
exports.assertMerkleCensusProof = assertMerkleCensusProof;
|
|
2033
3299
|
exports.createProcessSignatureMessage = createProcessSignatureMessage;
|
|
2034
|
-
exports.deployedAddresses = deployedAddresses;
|
|
2035
3300
|
exports.getElectionMetadataTemplate = getElectionMetadataTemplate;
|
|
2036
|
-
exports.getEnvironmentChain = getEnvironmentChain;
|
|
2037
|
-
exports.getEnvironmentConfig = getEnvironmentConfig;
|
|
2038
|
-
exports.getEnvironmentUrls = getEnvironmentUrls;
|
|
2039
3301
|
exports.isCSPCensusProof = isCSPCensusProof;
|
|
2040
3302
|
exports.isMerkleCensusProof = isMerkleCensusProof;
|
|
2041
|
-
exports.resolveConfiguration = resolveConfiguration;
|
|
2042
|
-
exports.resolveUrls = resolveUrls;
|
|
2043
3303
|
exports.signProcessCreation = signProcessCreation;
|
|
2044
3304
|
exports.validateProcessId = validateProcessId;
|
|
2045
3305
|
//# sourceMappingURL=index.js.map
|