@vocdoni/davinci-sdk 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -220,34 +220,12 @@ function assertCSPCensusProof(proof) {
220
220
  }
221
221
  }
222
222
 
223
- var CensusType = /* @__PURE__ */ ((CensusType2) => {
224
- CensusType2["PLAIN"] = "plain";
225
- CensusType2["WEIGHTED"] = "weighted";
226
- CensusType2["CSP"] = "csp";
227
- return CensusType2;
228
- })(CensusType || {});
229
223
  class Census {
230
- constructor(type, censusOrigin) {
224
+ constructor(censusOrigin) {
231
225
  this._censusId = null;
232
226
  this._censusRoot = null;
233
227
  this._censusURI = null;
234
- this._size = null;
235
- this._type = type;
236
- if (censusOrigin !== void 0) {
237
- this._censusOrigin = censusOrigin;
238
- } else {
239
- switch (type) {
240
- case "plain" /* PLAIN */:
241
- case "weighted" /* WEIGHTED */:
242
- this._censusOrigin = CensusOrigin.OffchainStatic;
243
- break;
244
- case "csp" /* CSP */:
245
- this._censusOrigin = CensusOrigin.CSP;
246
- break;
247
- default:
248
- throw new Error(`Unknown census type: ${type}`);
249
- }
250
- }
228
+ this._censusOrigin = censusOrigin;
251
229
  }
252
230
  get censusId() {
253
231
  return this._censusId;
@@ -258,12 +236,6 @@ class Census {
258
236
  get censusURI() {
259
237
  return this._censusURI;
260
238
  }
261
- get type() {
262
- return this._type;
263
- }
264
- get size() {
265
- return this._size;
266
- }
267
239
  get isPublished() {
268
240
  return this._censusRoot !== null && this._censusURI !== null;
269
241
  }
@@ -273,91 +245,41 @@ class Census {
273
245
  get censusOrigin() {
274
246
  return this._censusOrigin;
275
247
  }
276
- }
277
-
278
- class PlainCensus extends Census {
279
- /**
280
- * Creates a new PlainCensus
281
- * @param censusOrigin - The census origin (defaults to OffchainStatic for backward compatibility)
282
- */
283
- constructor(censusOrigin) {
284
- super(CensusType.PLAIN, censusOrigin);
285
- this._participants = /* @__PURE__ */ new Set();
286
- }
287
248
  /**
288
- * Add participant(s) with automatic weight=1
289
- * @param addresses - Single address or array of addresses
249
+ * Check if this census requires publishing via the Census API
250
+ * Merkle censuses (OffchainStatic, OffchainDynamic) need to be published
251
+ * Onchain and CSP censuses are ready immediately upon construction
290
252
  */
291
- add(addresses) {
292
- const toAdd = Array.isArray(addresses) ? addresses : [addresses];
293
- for (const address of toAdd) {
294
- this.validateAddress(address);
295
- this._participants.add(address.toLowerCase());
296
- }
297
- }
298
- /**
299
- * Remove participant by address
300
- */
301
- remove(address) {
302
- this._participants.delete(address.toLowerCase());
303
- }
304
- /**
305
- * Get all participants as CensusParticipant array (for API)
306
- * All participants have weight="1"
307
- */
308
- get participants() {
309
- return Array.from(this._participants).map((key) => ({
310
- key,
311
- weight: "1"
312
- // Everyone has weight=1 in plain census
313
- }));
314
- }
315
- /**
316
- * Get addresses only
317
- */
318
- get addresses() {
319
- return Array.from(this._participants);
320
- }
321
- validateAddress(address) {
322
- if (!address || typeof address !== "string") {
323
- throw new Error("Address is required and must be a string");
324
- }
325
- if (!/^(0x)?[0-9a-fA-F]{40}$/i.test(address)) {
326
- throw new Error(`Invalid Ethereum address format: ${address}`);
327
- }
328
- }
329
- /**
330
- * Internal method called after publishing
331
- * @internal
332
- */
333
- _setPublishedData(root, uri, size, censusId) {
334
- this._censusRoot = root;
335
- this._censusURI = uri;
336
- this._size = size;
337
- if (censusId) this._censusId = censusId;
253
+ get requiresPublishing() {
254
+ return this._censusOrigin === CensusOrigin.OffchainStatic || this._censusOrigin === CensusOrigin.OffchainDynamic;
338
255
  }
339
256
  }
340
257
 
341
- class WeightedCensus extends Census {
342
- /**
343
- * Creates a new WeightedCensus
344
- * @param censusOrigin - The census origin (defaults to OffchainStatic for backward compatibility)
345
- */
258
+ class MerkleCensus extends Census {
346
259
  constructor(censusOrigin) {
347
- super(CensusType.WEIGHTED, censusOrigin);
260
+ super(censusOrigin);
348
261
  this._participants = /* @__PURE__ */ new Map();
349
262
  }
350
263
  /**
351
- * Add participant(s) with custom weights
352
- * Weight can be provided as string, number, or bigint - will be converted to string internally
353
- * @param participant - Single participant or array of participants with custom weights
264
+ * Add participant(s) - supports both plain addresses and weighted participants
265
+ * @param data - Can be:
266
+ * - string: single address (weight=1)
267
+ * - string[]: array of addresses (weight=1 for all)
268
+ * - {key: string, weight: string|number|bigint}: single weighted participant
269
+ * - Array of weighted participants
354
270
  */
355
- add(participant) {
356
- const toAdd = Array.isArray(participant) ? participant : [participant];
357
- for (const p of toAdd) {
358
- this.validateParticipant(p);
359
- const weightString = this.normalizeWeight(p.weight);
360
- this._participants.set(p.key.toLowerCase(), weightString);
271
+ add(data) {
272
+ if (typeof data === "string") {
273
+ this.addAddress(data, "1");
274
+ } else if (Array.isArray(data)) {
275
+ if (data.length === 0) return;
276
+ if (typeof data[0] === "string") {
277
+ data.forEach((addr) => this.addAddress(addr, "1"));
278
+ } else {
279
+ data.forEach((p) => this.addParticipant(p));
280
+ }
281
+ } else {
282
+ this.addParticipant(data);
361
283
  }
362
284
  }
363
285
  /**
@@ -387,6 +309,21 @@ class WeightedCensus extends Census {
387
309
  getWeight(address) {
388
310
  return this._participants.get(address.toLowerCase());
389
311
  }
312
+ /**
313
+ * Internal method to add a plain address with a given weight
314
+ */
315
+ addAddress(address, weight) {
316
+ this.validateAddress(address);
317
+ this._participants.set(address.toLowerCase(), weight);
318
+ }
319
+ /**
320
+ * Internal method to add a weighted participant
321
+ */
322
+ addParticipant(participant) {
323
+ this.validateAddress(participant.key);
324
+ const weight = this.normalizeWeight(participant.weight);
325
+ this._participants.set(participant.key.toLowerCase(), weight);
326
+ }
390
327
  /**
391
328
  * Normalizes weight from string, number, or bigint to string
392
329
  */
@@ -411,32 +348,63 @@ class WeightedCensus extends Census {
411
348
  }
412
349
  throw new Error(`Invalid weight type. Must be string, number, or bigint.`);
413
350
  }
414
- validateParticipant(participant) {
415
- if (!participant.key || typeof participant.key !== "string") {
416
- throw new Error("Participant key (address) is required");
417
- }
418
- if (!/^(0x)?[0-9a-fA-F]{40}$/i.test(participant.key)) {
419
- throw new Error(`Invalid Ethereum address format: ${participant.key}`);
351
+ /**
352
+ * Validates Ethereum address format
353
+ */
354
+ validateAddress(address) {
355
+ if (!address || typeof address !== "string") {
356
+ throw new Error("Address is required and must be a string");
420
357
  }
421
- if (participant.weight === void 0 || participant.weight === null) {
422
- throw new Error("Participant weight is required");
358
+ if (!/^(0x)?[0-9a-fA-F]{40}$/i.test(address)) {
359
+ throw new Error(`Invalid Ethereum address format: ${address}`);
423
360
  }
424
361
  }
425
362
  /**
426
363
  * Internal method called after publishing
427
364
  * @internal
428
365
  */
429
- _setPublishedData(root, uri, size, censusId) {
366
+ _setPublishedData(root, uri, censusId) {
430
367
  this._censusRoot = root;
431
368
  this._censusURI = uri;
432
- this._size = size;
433
369
  if (censusId) this._censusId = censusId;
434
370
  }
435
371
  }
436
372
 
373
+ class OffchainCensus extends MerkleCensus {
374
+ constructor() {
375
+ super(CensusOrigin.OffchainStatic);
376
+ }
377
+ }
378
+
379
+ class OffchainDynamicCensus extends MerkleCensus {
380
+ constructor() {
381
+ super(CensusOrigin.OffchainDynamic);
382
+ }
383
+ }
384
+
385
+ class OnchainCensus extends Census {
386
+ /**
387
+ * Creates an OnchainCensus
388
+ * @param contractAddress - The address of the smart contract (e.g., ERC20, ERC721)
389
+ * @param uri - Optional URI with census information
390
+ */
391
+ constructor(contractAddress, uri) {
392
+ super(CensusOrigin.Onchain);
393
+ if (!/^(0x)?[0-9a-fA-F]{40}$/i.test(contractAddress)) {
394
+ throw new Error("Contract address is missing or invalid");
395
+ }
396
+ this._contractAddress = contractAddress;
397
+ this._censusRoot = contractAddress;
398
+ this._censusURI = uri || `contract://${contractAddress}`;
399
+ }
400
+ get contractAddress() {
401
+ return this._contractAddress;
402
+ }
403
+ }
404
+
437
405
  class CspCensus extends Census {
438
- constructor(publicKey, cspURI, size) {
439
- super(CensusType.CSP, CensusOrigin.CSP);
406
+ constructor(publicKey, cspURI) {
407
+ super(CensusOrigin.CSP);
440
408
  if (!/^(0x)?[0-9a-fA-F]+$/.test(publicKey)) {
441
409
  throw new Error("Public key is missing or invalid");
442
410
  }
@@ -449,7 +417,6 @@ class CspCensus extends Census {
449
417
  this._cspURI = cspURI;
450
418
  this._censusRoot = publicKey;
451
419
  this._censusURI = cspURI;
452
- this._size = size;
453
420
  }
454
421
  get publicKey() {
455
422
  return this._publicKey;
@@ -462,17 +429,14 @@ class CspCensus extends Census {
462
429
  class PublishedCensus extends Census {
463
430
  /**
464
431
  * Creates a PublishedCensus from existing census data
465
- * @param type - The census type (PLAIN, WEIGHTED, or CSP)
432
+ * @param censusOrigin - The census origin (OffchainStatic, OffchainDynamic, Onchain, or CSP)
466
433
  * @param root - The census root
467
434
  * @param uri - The census URI
468
- * @param size - The census size (number of participants)
469
- * @param censusOrigin - The census origin (optional - defaults based on type if not provided)
470
435
  */
471
- constructor(type, root, uri, size, censusOrigin) {
472
- super(type, censusOrigin);
436
+ constructor(censusOrigin, root, uri) {
437
+ super(censusOrigin);
473
438
  this._censusRoot = root;
474
439
  this._censusURI = uri;
475
- this._size = size;
476
440
  }
477
441
  }
478
442
 
@@ -481,7 +445,7 @@ class CensusOrchestrator {
481
445
  this.censusService = censusService;
482
446
  }
483
447
  /**
484
- * Publishes a PlainCensus or WeightedCensus
448
+ * Publishes a MerkleCensus (OffchainCensus, OffchainDynamicCensus, or OnchainCensus)
485
449
  * Creates a working census, adds participants, and publishes it
486
450
  */
487
451
  async publish(census) {
@@ -497,23 +461,24 @@ class CensusOrchestrator {
497
461
  census._setPublishedData(
498
462
  publishResponse.root,
499
463
  publishResponse.uri,
500
- publishResponse.participantCount,
501
464
  censusId
502
465
  );
503
466
  }
504
467
  /**
505
468
  * Gets census data for process creation
506
- * Throws if census is not published
469
+ * Throws if census is not ready (published for Merkle/CSP, or constructed for Onchain)
507
470
  */
508
471
  getCensusData(census) {
509
- if (!census.isPublished) {
510
- throw new Error("Census must be published before creating a process");
472
+ if (census.requiresPublishing && !census.isPublished) {
473
+ throw new Error("Merkle census must be published before creating a process");
474
+ }
475
+ if (!census.censusRoot || !census.censusURI) {
476
+ throw new Error("Census data is incomplete");
511
477
  }
512
478
  return {
513
479
  type: census.censusOrigin,
514
480
  root: census.censusRoot,
515
- uri: census.censusURI,
516
- size: census.size
481
+ uri: census.censusURI
517
482
  };
518
483
  }
519
484
  }
@@ -1191,26 +1156,24 @@ class ProcessOrchestrationService {
1191
1156
  */
1192
1157
  async handleCensus(census) {
1193
1158
  if ("isPublished" in census) {
1194
- if (census instanceof PlainCensus || census instanceof WeightedCensus) {
1195
- if (!census.isPublished) {
1196
- const censusBaseURL = this.apiService.census?.["axios"]?.defaults?.baseURL;
1197
- if (!censusBaseURL || censusBaseURL === "" || censusBaseURL === "undefined") {
1198
- throw new Error(
1199
- 'Census API URL is required to publish PlainCensus or WeightedCensus. Please provide "censusUrl" when initializing DavinciSDK, or use a pre-published census.'
1200
- );
1201
- }
1202
- await this.censusOrchestrator.publish(census);
1159
+ if (census.requiresPublishing && !census.isPublished) {
1160
+ const censusBaseURL = this.apiService.census?.["axios"]?.defaults?.baseURL;
1161
+ if (!censusBaseURL || censusBaseURL === "" || censusBaseURL === "undefined") {
1162
+ throw new Error(
1163
+ 'Census API URL is required to publish Merkle censuses (OffchainCensus, OffchainDynamicCensus). Please provide "censusUrl" when initializing DavinciSDK, or use a pre-published census.'
1164
+ );
1203
1165
  }
1166
+ await this.censusOrchestrator.publish(census);
1204
1167
  }
1205
1168
  const censusData = this.censusOrchestrator.getCensusData(census);
1206
1169
  return {
1207
1170
  type: censusData.type,
1208
1171
  root: censusData.root,
1209
- size: censusData.size,
1210
1172
  uri: censusData.uri
1211
1173
  };
1212
1174
  }
1213
- return census;
1175
+ const { size, ...censusWithoutSize } = census;
1176
+ return censusWithoutSize;
1214
1177
  }
1215
1178
  /**
1216
1179
  * Gets user-friendly process information by transforming raw contract data
@@ -1418,7 +1381,22 @@ class ProcessOrchestrationService {
1418
1381
  ballotMode,
1419
1382
  signature
1420
1383
  });
1421
- const maxVoters = config.maxVoters ?? censusConfig.size;
1384
+ let maxVoters;
1385
+ if (config.maxVoters !== void 0) {
1386
+ maxVoters = config.maxVoters;
1387
+ } else if ("isPublished" in config.census && config.census.isPublished) {
1388
+ if ("participants" in config.census) {
1389
+ maxVoters = config.census.participants.length;
1390
+ } else {
1391
+ throw new Error(
1392
+ "maxVoters is required when using OnchainCensus, CspCensus, or PublishedCensus. It can only be auto-calculated for published MerkleCensus (OffchainCensus/OffchainDynamicCensus)."
1393
+ );
1394
+ }
1395
+ } else {
1396
+ throw new Error(
1397
+ "maxVoters is required. It can only be omitted when using a published MerkleCensus (OffchainCensus/OffchainDynamicCensus), in which case it defaults to the participant count."
1398
+ );
1399
+ }
1422
1400
  const census = {
1423
1401
  censusOrigin: censusConfig.type,
1424
1402
  censusRoot,
@@ -3547,7 +3525,6 @@ exports.Census = Census;
3547
3525
  exports.CensusNotUpdatable = CensusNotUpdatable;
3548
3526
  exports.CensusOrchestrator = CensusOrchestrator;
3549
3527
  exports.CensusOrigin = CensusOrigin;
3550
- exports.CensusType = CensusType;
3551
3528
  exports.CircomProof = CircomProof;
3552
3529
  exports.ContractServiceError = ContractServiceError;
3553
3530
  exports.CspCensus = CspCensus;
@@ -3555,12 +3532,15 @@ exports.DavinciCrypto = DavinciCrypto;
3555
3532
  exports.DavinciSDK = DavinciSDK;
3556
3533
  exports.ElectionMetadataTemplate = ElectionMetadataTemplate;
3557
3534
  exports.ElectionResultsTypeNames = ElectionResultsTypeNames;
3535
+ exports.MerkleCensus = MerkleCensus;
3536
+ exports.OffchainCensus = OffchainCensus;
3537
+ exports.OffchainDynamicCensus = OffchainDynamicCensus;
3538
+ exports.OnchainCensus = OnchainCensus;
3558
3539
  exports.OrganizationAdministratorError = OrganizationAdministratorError;
3559
3540
  exports.OrganizationCreateError = OrganizationCreateError;
3560
3541
  exports.OrganizationDeleteError = OrganizationDeleteError;
3561
3542
  exports.OrganizationRegistryService = OrganizationRegistryService;
3562
3543
  exports.OrganizationUpdateError = OrganizationUpdateError;
3563
- exports.PlainCensus = PlainCensus;
3564
3544
  exports.ProcessCensusError = ProcessCensusError;
3565
3545
  exports.ProcessCreateError = ProcessCreateError;
3566
3546
  exports.ProcessDurationError = ProcessDurationError;
@@ -3578,7 +3558,6 @@ exports.VocdoniCensusService = VocdoniCensusService;
3578
3558
  exports.VocdoniSequencerService = VocdoniSequencerService;
3579
3559
  exports.VoteOrchestrationService = VoteOrchestrationService;
3580
3560
  exports.VoteStatus = VoteStatus;
3581
- exports.WeightedCensus = WeightedCensus;
3582
3561
  exports.assertCSPCensusProof = assertCSPCensusProof;
3583
3562
  exports.assertMerkleCensusProof = assertMerkleCensusProof;
3584
3563
  exports.createProcessSignatureMessage = createProcessSignatureMessage;