@vocdoni/davinci-sdk 0.0.7 → 0.1.1

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,66 @@ 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 - The URI pointing to census data source (e.g., subgraph endpoint)
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
+ if (!uri || uri.trim() === "") {
395
+ throw new Error("URI is required for onchain census");
396
+ }
397
+ this._contractAddress = contractAddress;
398
+ this._censusRoot = "0x0000000000000000000000000000000000000000000000000000000000000000";
399
+ this._censusURI = uri;
400
+ }
401
+ get contractAddress() {
402
+ return this._contractAddress;
403
+ }
404
+ }
405
+
435
406
  class CspCensus extends Census {
436
- constructor(publicKey, cspURI, size) {
437
- super(CensusType.CSP, CensusOrigin.CSP);
407
+ constructor(publicKey, cspURI) {
408
+ super(CensusOrigin.CSP);
438
409
  if (!/^(0x)?[0-9a-fA-F]+$/.test(publicKey)) {
439
410
  throw new Error("Public key is missing or invalid");
440
411
  }
@@ -447,7 +418,6 @@ class CspCensus extends Census {
447
418
  this._cspURI = cspURI;
448
419
  this._censusRoot = publicKey;
449
420
  this._censusURI = cspURI;
450
- this._size = size;
451
421
  }
452
422
  get publicKey() {
453
423
  return this._publicKey;
@@ -460,17 +430,14 @@ class CspCensus extends Census {
460
430
  class PublishedCensus extends Census {
461
431
  /**
462
432
  * Creates a PublishedCensus from existing census data
463
- * @param type - The census type (PLAIN, WEIGHTED, or CSP)
433
+ * @param censusOrigin - The census origin (OffchainStatic, OffchainDynamic, Onchain, or CSP)
464
434
  * @param root - The census root
465
435
  * @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
436
  */
469
- constructor(type, root, uri, size, censusOrigin) {
470
- super(type, censusOrigin);
437
+ constructor(censusOrigin, root, uri) {
438
+ super(censusOrigin);
471
439
  this._censusRoot = root;
472
440
  this._censusURI = uri;
473
- this._size = size;
474
441
  }
475
442
  }
476
443
 
@@ -479,7 +446,7 @@ class CensusOrchestrator {
479
446
  this.censusService = censusService;
480
447
  }
481
448
  /**
482
- * Publishes a PlainCensus or WeightedCensus
449
+ * Publishes a MerkleCensus (OffchainCensus, OffchainDynamicCensus, or OnchainCensus)
483
450
  * Creates a working census, adds participants, and publishes it
484
451
  */
485
452
  async publish(census) {
@@ -495,23 +462,26 @@ class CensusOrchestrator {
495
462
  census._setPublishedData(
496
463
  publishResponse.root,
497
464
  publishResponse.uri,
498
- publishResponse.participantCount,
499
465
  censusId
500
466
  );
501
467
  }
502
468
  /**
503
469
  * Gets census data for process creation
504
- * Throws if census is not published
470
+ * Throws if census is not ready (published for Merkle/CSP, or constructed for Onchain)
505
471
  */
506
472
  getCensusData(census) {
507
- if (!census.isPublished) {
508
- throw new Error("Census must be published before creating a process");
473
+ if (census.requiresPublishing && !census.isPublished) {
474
+ throw new Error("Merkle census must be published before creating a process");
509
475
  }
476
+ if (!census.censusRoot || !census.censusURI) {
477
+ throw new Error("Census data is incomplete");
478
+ }
479
+ const contractAddress = "contractAddress" in census ? census.contractAddress : void 0;
510
480
  return {
511
481
  type: census.censusOrigin,
512
482
  root: census.censusRoot,
513
483
  uri: census.censusURI,
514
- size: census.size
484
+ contractAddress
515
485
  };
516
486
  }
517
487
  }
@@ -1035,7 +1005,9 @@ class ProcessRegistryService extends SmartContractService {
1035
1005
  const contractCensus = {
1036
1006
  censusOrigin: BigInt(census.censusOrigin),
1037
1007
  censusRoot: census.censusRoot,
1038
- censusURI: census.censusURI
1008
+ contractAddress: census.contractAddress ?? "0x0000000000000000000000000000000000000000",
1009
+ censusURI: census.censusURI,
1010
+ onchainAllowAnyValidRoot: census.onchainAllowAnyValidRoot ?? false
1039
1011
  };
1040
1012
  return this.sendTx(
1041
1013
  this.contract.newProcess(
@@ -1066,7 +1038,9 @@ class ProcessRegistryService extends SmartContractService {
1066
1038
  const contractCensus = {
1067
1039
  censusOrigin: BigInt(census.censusOrigin),
1068
1040
  censusRoot: census.censusRoot,
1069
- censusURI: census.censusURI
1041
+ contractAddress: census.contractAddress ?? "0x0000000000000000000000000000000000000000",
1042
+ censusURI: census.censusURI,
1043
+ onchainAllowAnyValidRoot: census.onchainAllowAnyValidRoot ?? false
1070
1044
  };
1071
1045
  return this.sendTx(
1072
1046
  this.contract.setProcessCensus(processID, contractCensus).catch((e) => {
@@ -1189,26 +1163,19 @@ class ProcessOrchestrationService {
1189
1163
  */
1190
1164
  async handleCensus(census) {
1191
1165
  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);
1166
+ if (census.requiresPublishing && !census.isPublished) {
1167
+ const censusBaseURL = this.apiService.census?.["axios"]?.defaults?.baseURL;
1168
+ if (!censusBaseURL || censusBaseURL === "" || censusBaseURL === "undefined") {
1169
+ throw new Error(
1170
+ 'Census API URL is required to publish Merkle censuses (OffchainCensus, OffchainDynamicCensus). Please provide "censusUrl" when initializing DavinciSDK, or use a pre-published census.'
1171
+ );
1201
1172
  }
1173
+ await this.censusOrchestrator.publish(census);
1202
1174
  }
1203
- const censusData = this.censusOrchestrator.getCensusData(census);
1204
- return {
1205
- type: censusData.type,
1206
- root: censusData.root,
1207
- size: censusData.size,
1208
- uri: censusData.uri
1209
- };
1175
+ return this.censusOrchestrator.getCensusData(census);
1210
1176
  }
1211
- return census;
1177
+ const { size, ...censusWithoutSize } = census;
1178
+ return censusWithoutSize;
1212
1179
  }
1213
1180
  /**
1214
1181
  * Gets user-friendly process information by transforming raw contract data
@@ -1416,11 +1383,31 @@ class ProcessOrchestrationService {
1416
1383
  ballotMode,
1417
1384
  signature
1418
1385
  });
1419
- const maxVoters = config.maxVoters ?? censusConfig.size;
1386
+ let maxVoters;
1387
+ if (config.maxVoters !== void 0) {
1388
+ maxVoters = config.maxVoters;
1389
+ } else if ("isPublished" in config.census && config.census.isPublished) {
1390
+ if ("participants" in config.census) {
1391
+ maxVoters = config.census.participants.length;
1392
+ } else {
1393
+ throw new Error(
1394
+ "maxVoters is required when using OnchainCensus, CspCensus, or PublishedCensus. It can only be auto-calculated for published MerkleCensus (OffchainCensus/OffchainDynamicCensus)."
1395
+ );
1396
+ }
1397
+ } else {
1398
+ throw new Error(
1399
+ "maxVoters is required. It can only be omitted when using a published MerkleCensus (OffchainCensus/OffchainDynamicCensus), in which case it defaults to the participant count."
1400
+ );
1401
+ }
1420
1402
  const census = {
1421
1403
  censusOrigin: censusConfig.type,
1422
1404
  censusRoot,
1423
- censusURI: censusConfig.uri
1405
+ contractAddress: censusConfig.contractAddress,
1406
+ // Only set for onchain censuses
1407
+ censusURI: censusConfig.uri,
1408
+ // For onchain censuses (ERC20 token snapshots), allow any valid merkle root
1409
+ // For other census types, require the specific censusRoot
1410
+ onchainAllowAnyValidRoot: censusConfig.type === CensusOrigin.Onchain
1424
1411
  };
1425
1412
  return {
1426
1413
  processId,
@@ -3540,5 +3527,5 @@ class DavinciSDK {
3540
3527
  }
3541
3528
  }
3542
3529
 
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 };
3530
+ 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
3531
  //# sourceMappingURL=index.mjs.map