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