@vocdoni/davinci-sdk 0.0.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/README.md ADDED
@@ -0,0 +1,635 @@
1
+ # Vocdoni DaVinci SDK
2
+
3
+ [![npm version](https://badge.fury.io/js/%40vocdoni%2Fdavinci-sdk.svg)](https://badge.fury.io/js/%40vocdoni%2Fdavinci-sdk)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](#)
7
+
8
+ A powerful, easy-to-use TypeScript SDK for building decentralized voting applications on the Vocdoni DaVinci protocol. Create secure, private, and verifiable elections with just a few lines of code.
9
+
10
+ ## ๐Ÿš€ Quick Start
11
+
12
+ ### Installation
13
+
14
+ ```bash
15
+ npm install @vocdoni/davinci-sdk
16
+ # or
17
+ yarn add @vocdoni/davinci-sdk
18
+ ```
19
+
20
+ ### Basic Usage
21
+
22
+ ```typescript
23
+ import { DavinciSDK, CensusOrigin } from '@vocdoni/davinci-sdk';
24
+ import { Wallet } from 'ethers';
25
+
26
+ // Initialize the SDK
27
+ const wallet = new Wallet('your-private-key');
28
+ const sdk = new DavinciSDK({
29
+ signer: wallet,
30
+ environment: 'dev' // or 'stg', 'prod'
31
+ });
32
+
33
+ await sdk.init();
34
+
35
+ // 1. Create a census with eligible voters
36
+ const censusId = await sdk.api.census.createCensus();
37
+
38
+ // Add participants to the census
39
+ const participants = [
40
+ { key: "0x1234567890123456789012345678901234567890", weight: "1" },
41
+ { key: "0x2345678901234567890123456789012345678901", weight: "1" },
42
+ { key: "0x3456789012345678901234567890123456789012", weight: "2" } // Higher weight
43
+ ];
44
+
45
+ await sdk.api.census.addParticipants(censusId, participants);
46
+
47
+ // Publish the census to get the root
48
+ const publishResult = await sdk.api.census.publishCensus(censusId);
49
+ const censusSize = await sdk.api.census.getCensusSize(publishResult.root);
50
+
51
+ // 2. Create a voting process
52
+ const process = await sdk.createProcess({
53
+ title: "Community Decision",
54
+ description: "Vote on our next community initiative",
55
+ census: {
56
+ type: CensusOrigin.CensusOriginMerkleTree,
57
+ root: publishResult.root,
58
+ size: censusSize,
59
+ uri: publishResult.uri
60
+ },
61
+ timing: {
62
+ startDate: new Date("2024-12-01T10:00:00Z"),
63
+ duration: 86400 // 24 hours in seconds
64
+ },
65
+ questions: [{
66
+ title: "Which initiative should we prioritize?",
67
+ choices: [
68
+ { title: "Community Garden", value: 0 },
69
+ { title: "Tech Workshop", value: 1 },
70
+ { title: "Art Exhibition", value: 2 }
71
+ ]
72
+ }]
73
+ });
74
+
75
+ // 3. Submit a vote (using one of the census participants)
76
+ const voterWallet = new Wallet('voter-private-key'); // Must be one of the census participants
77
+ const voterSdk = new DavinciSDK({
78
+ signer: voterWallet,
79
+ environment: 'dev'
80
+ });
81
+ await voterSdk.init();
82
+
83
+ const vote = await voterSdk.submitVote({
84
+ processId: process.processId,
85
+ choices: [1] // Vote for "Tech Workshop"
86
+ });
87
+
88
+ // 4. Wait for vote confirmation
89
+ const finalStatus = await voterSdk.waitForVoteStatus(
90
+ vote.processId,
91
+ vote.voteId,
92
+ VoteStatus.Settled
93
+ );
94
+
95
+ console.log('Vote confirmed!', finalStatus);
96
+ ```
97
+
98
+ ## ๐Ÿ“š Table of Contents
99
+
100
+ - [Features](#-features)
101
+ - [Installation](#-installation)
102
+ - [Core Concepts](#-core-concepts)
103
+ - [API Reference](#-api-reference)
104
+ - [SDK Initialization](#sdk-initialization)
105
+ - [Process Management](#process-management)
106
+ - [Voting Operations](#voting-operations)
107
+ - [Examples](#-examples)
108
+ - [Advanced Configuration](#-advanced-configuration)
109
+ - [Error Handling](#-error-handling)
110
+ - [Testing](#-testing)
111
+ - [Contributing](#-contributing)
112
+ - [Support](#-support)
113
+
114
+ ## โœจ Features
115
+
116
+ - **๐Ÿ”’ Privacy-First**: Homomorphic encryption ensures vote privacy
117
+ - **๐Ÿ›ก๏ธ Secure**: Built on battle-tested cryptographic primitives
118
+ - **โšก Easy Integration**: Simple, intuitive API for developers
119
+ - **๐ŸŒ Decentralized**: No central authority controls the voting process
120
+ - **๐Ÿ“ฑ Cross-Platform**: Works in browsers, Node.js, and mobile apps
121
+ - **๐Ÿ”ง TypeScript**: Full type safety and excellent developer experience
122
+ - **๐ŸŽฏ Flexible**: Support for multiple question types and voting modes
123
+
124
+ ## ๐Ÿ›  Installation
125
+
126
+ ### Prerequisites
127
+
128
+ - Node.js 16+ or modern browser environment
129
+ - An Ethereum wallet/signer (MetaMask, WalletConnect, etc.)
130
+
131
+ ### Package Installation
132
+
133
+ ```bash
134
+ # Using npm
135
+ npm install @vocdoni/davinci-sdk ethers
136
+
137
+ # Using yarn
138
+ yarn add @vocdoni/davinci-sdk ethers
139
+
140
+ # Using pnpm
141
+ pnpm add @vocdoni/davinci-sdk ethers
142
+ ```
143
+
144
+ ## ๐Ÿง  Core Concepts
145
+
146
+ ### Voting Process Lifecycle
147
+
148
+ 1. **Process Creation**: Define voting parameters, questions, and census
149
+ 2. **Vote Submission**: Voters submit encrypted, anonymous votes
150
+ 3. **Vote Processing**: Votes are verified and aggregated using zk-SNARKs
151
+ 4. **Results**: Final results are computed and made available
152
+
153
+ ### Key Components
154
+
155
+ - **Census**: List of eligible voters (Merkle tree or CSP-based)
156
+ - **Ballot**: Vote structure defining questions and possible answers
157
+ - **Process**: Container for all voting parameters and metadata
158
+ - **Proof**: Cryptographic evidence that a vote is valid
159
+
160
+ ## ๐Ÿ“– API Reference
161
+
162
+ ### SDK Initialization
163
+
164
+ #### Constructor Options
165
+
166
+ ```typescript
167
+ interface DavinciSDKConfig {
168
+ signer: Signer; // Ethereum signer (required)
169
+ environment?: 'dev' | 'stg' | 'prod'; // Environment (default: 'prod')
170
+ sequencerUrl?: string; // Custom sequencer URL
171
+ censusUrl?: string; // Custom census API URL
172
+ chain?: 'sepolia' | 'mainnet'; // Blockchain network
173
+ contractAddresses?: { // Custom contract addresses
174
+ processRegistry?: string;
175
+ organizationRegistry?: string;
176
+ // ... other contracts
177
+ };
178
+ useSequencerAddresses?: boolean; // Use addresses from sequencer
179
+ }
180
+ ```
181
+
182
+ #### Basic Initialization
183
+
184
+ ```typescript
185
+ import { DavinciSDK } from '@vocdoni/davinci-sdk';
186
+ import { Wallet } from 'ethers';
187
+
188
+ const sdk = new DavinciSDK({
189
+ signer: new Wallet('your-private-key'),
190
+ environment: 'dev'
191
+ });
192
+
193
+ await sdk.init();
194
+ ```
195
+
196
+ ### Process Management
197
+
198
+ #### Creating a Process
199
+
200
+ ```typescript
201
+ const processResult = await sdk.createProcess({
202
+ title: "Election Title",
203
+ description: "Detailed description of the election",
204
+
205
+ // Census configuration
206
+ census: {
207
+ type: CensusOrigin.CensusOriginMerkleTree,
208
+ root: "0x...",
209
+ size: 1000,
210
+ uri: "ipfs://..."
211
+ },
212
+
213
+ // Timing configuration
214
+ timing: {
215
+ startDate: new Date("2024-12-01T10:00:00Z"),
216
+ duration: 86400 // 24 hours
217
+ // Alternative: endDate: new Date("2024-12-02T10:00:00Z")
218
+ },
219
+
220
+ // Ballot configuration
221
+ ballot: {
222
+ numFields: 1,
223
+ maxValue: "2",
224
+ minValue: "0",
225
+ uniqueValues: false,
226
+ costFromWeight: false,
227
+ costExponent: 1,
228
+ maxValueSum: "2",
229
+ minValueSum: "0"
230
+ },
231
+
232
+ // Questions
233
+ questions: [{
234
+ title: "What is your preferred option?",
235
+ description: "Choose the option that best represents your view",
236
+ choices: [
237
+ { title: "Option A", value: 0 },
238
+ { title: "Option B", value: 1 },
239
+ { title: "Option C", value: 2 }
240
+ ]
241
+ }]
242
+ });
243
+
244
+ console.log('Process created:', processResult.processId);
245
+ ```
246
+
247
+ #### Retrieving Process Information
248
+
249
+ ```typescript
250
+ const processInfo = await sdk.getProcess(processId);
251
+
252
+ console.log('Title:', processInfo.title);
253
+ console.log('Status:', processInfo.status);
254
+ console.log('Start date:', processInfo.startDate);
255
+ console.log('End date:', processInfo.endDate);
256
+ console.log('Questions:', processInfo.questions);
257
+ ```
258
+
259
+ ### Voting Operations
260
+
261
+ #### Submitting a Vote
262
+
263
+ ```typescript
264
+ const voteResult = await sdk.submitVote({
265
+ processId: "0x...",
266
+ choices: [1, 0], // Answers for each question
267
+ randomness: "optional-custom-randomness" // Optional
268
+ });
269
+
270
+ console.log('Vote ID:', voteResult.voteId);
271
+ console.log('Status:', voteResult.status);
272
+ ```
273
+
274
+ #### Checking Vote Status
275
+
276
+ ```typescript
277
+ const status = await sdk.getVoteStatus(processId, voteId);
278
+ console.log('Current status:', status.status);
279
+ // Possible statuses: pending, verified, aggregated, processed, settled, error
280
+ ```
281
+
282
+ #### Waiting for Vote Confirmation
283
+
284
+ ```typescript
285
+ import { VoteStatus } from '@vocdoni/davinci-sdk';
286
+
287
+ const finalStatus = await sdk.waitForVoteStatus(
288
+ processId,
289
+ voteId,
290
+ VoteStatus.Settled, // Target status
291
+ 300000, // 5 minute timeout
292
+ 5000 // Check every 5 seconds
293
+ );
294
+ ```
295
+
296
+ #### Checking if Address Has Voted
297
+
298
+ ```typescript
299
+ const hasVoted = await sdk.hasAddressVoted(processId, voterAddress);
300
+ if (hasVoted) {
301
+ console.log('This address has already voted');
302
+ }
303
+ ```
304
+
305
+ ## ๐Ÿ’ก Examples
306
+
307
+ ### Complete Voting Flow
308
+
309
+ ```typescript
310
+ import { DavinciSDK, CensusOrigin, VoteStatus } from '@vocdoni/davinci-sdk';
311
+ import { Wallet } from 'ethers';
312
+
313
+ async function completeVotingExample() {
314
+ // 1. Initialize SDK
315
+ const organizerWallet = new Wallet('organizer-private-key');
316
+ const sdk = new DavinciSDK({
317
+ signer: organizerWallet,
318
+ environment: 'dev'
319
+ });
320
+ await sdk.init();
321
+
322
+ // 2. Create census with eligible voters
323
+ const censusId = await sdk.api.census.createCensus();
324
+
325
+ // Create voter wallets and add them to census
326
+ const voters = [];
327
+ for (let i = 0; i < 5; i++) {
328
+ const voterWallet = Wallet.createRandom();
329
+ voters.push(voterWallet);
330
+ }
331
+
332
+ const participants = voters.map(voter => ({
333
+ key: voter.address,
334
+ weight: "1"
335
+ }));
336
+
337
+ await sdk.api.census.addParticipants(censusId, participants);
338
+
339
+ // Publish the census
340
+ const publishResult = await sdk.api.census.publishCensus(censusId);
341
+ const censusSize = await sdk.api.census.getCensusSize(publishResult.root);
342
+
343
+ // 3. Create voting process
344
+ const process = await sdk.createProcess({
345
+ title: "Community Budget Allocation",
346
+ description: "Decide how to allocate our community budget",
347
+ census: {
348
+ type: CensusOrigin.CensusOriginMerkleTree,
349
+ root: publishResult.root,
350
+ size: censusSize,
351
+ uri: publishResult.uri
352
+ },
353
+ timing: {
354
+ startDate: new Date(Date.now() + 60000), // Start in 1 minute
355
+ duration: 3600 // 1 hour
356
+ },
357
+ questions: [{
358
+ title: "Which project should receive funding?",
359
+ choices: [
360
+ { title: "Community Garden", value: 0 },
361
+ { title: "Tech Education Program", value: 1 },
362
+ { title: "Local Art Initiative", value: 2 }
363
+ ]
364
+ }]
365
+ });
366
+
367
+ console.log(`Process created: ${process.processId}`);
368
+
369
+ // 4. Vote using one of the census participants
370
+ const voterWallet = voters[0]; // Use first voter from census
371
+ const voterSdk = new DavinciSDK({
372
+ signer: voterWallet,
373
+ environment: 'dev'
374
+ });
375
+ await voterSdk.init();
376
+
377
+ // Wait for process to start accepting votes
378
+ await new Promise(resolve => setTimeout(resolve, 65000));
379
+
380
+ const vote = await voterSdk.submitVote({
381
+ processId: process.processId,
382
+ choices: [1] // Vote for Tech Education Program
383
+ });
384
+
385
+ console.log(`Vote submitted: ${vote.voteId}`);
386
+
387
+ // 5. Wait for vote confirmation
388
+ const finalStatus = await voterSdk.waitForVoteStatus(
389
+ vote.processId,
390
+ vote.voteId,
391
+ VoteStatus.Settled
392
+ );
393
+
394
+ console.log('Vote confirmed with status:', finalStatus.status);
395
+ }
396
+ ```
397
+
398
+ ### Browser Integration with MetaMask
399
+
400
+ ```typescript
401
+ import { DavinciSDK } from '@vocdoni/davinci-sdk';
402
+ import { BrowserProvider } from 'ethers';
403
+
404
+ async function browserVotingExample() {
405
+ // Connect to MetaMask
406
+ if (!window.ethereum) {
407
+ throw new Error('MetaMask not found');
408
+ }
409
+
410
+ const provider = new BrowserProvider(window.ethereum);
411
+ await provider.send("eth_requestAccounts", []);
412
+ const signer = await provider.getSigner();
413
+
414
+ // Initialize SDK
415
+ const sdk = new DavinciSDK({
416
+ signer,
417
+ environment: 'prod'
418
+ });
419
+ await sdk.init();
420
+
421
+ // Submit vote
422
+ const vote = await sdk.submitVote({
423
+ processId: "0x...",
424
+ choices: [2]
425
+ });
426
+
427
+ console.log('Vote submitted from browser:', vote.voteId);
428
+ }
429
+ ```
430
+
431
+ ## โš™๏ธ Advanced Configuration
432
+
433
+ <details>
434
+ <summary>Click to expand advanced configuration options</summary>
435
+
436
+ ### Custom Network Configuration
437
+
438
+ ```typescript
439
+ const sdk = new DavinciSDK({
440
+ signer: wallet,
441
+ chain: 'sepolia',
442
+ sequencerUrl: 'https://your-custom-sequencer.com',
443
+ censusUrl: 'https://your-custom-census.com',
444
+ contractAddresses: {
445
+ processRegistry: '0x...',
446
+ organizationRegistry: '0x...',
447
+ stateTransitionVerifier: '0x...',
448
+ resultsVerifier: '0x...'
449
+ }
450
+ });
451
+ ```
452
+
453
+ ### Using Sequencer-Provided Addresses
454
+
455
+ ```typescript
456
+ const sdk = new DavinciSDK({
457
+ signer: wallet,
458
+ environment: 'dev',
459
+ useSequencerAddresses: true // Fetch contract addresses from sequencer
460
+ });
461
+ ```
462
+
463
+ ### Custom Vote Randomness
464
+
465
+ ```typescript
466
+ const vote = await sdk.submitVote({
467
+ processId: "0x...",
468
+ choices: [1],
469
+ randomness: "your-custom-randomness-hex" // For deterministic testing
470
+ });
471
+ ```
472
+
473
+ ### Advanced Process Configuration
474
+
475
+ ```typescript
476
+ const process = await sdk.createProcess({
477
+ title: "Advanced Election",
478
+ // ... basic config
479
+
480
+ ballot: {
481
+ numFields: 3, // Number of questions
482
+ maxValue: "5", // Maximum choice value
483
+ minValue: "0", // Minimum choice value
484
+ uniqueValues: true, // Require unique choices
485
+ costFromWeight: false, // Use weight for vote cost
486
+ costExponent: 1, // Cost calculation exponent
487
+ maxValueSum: "10", // Maximum sum of all choices
488
+ minValueSum: "3" // Minimum sum of all choices
489
+ }
490
+ });
491
+ ```
492
+
493
+ ### Direct Service Access
494
+
495
+ ```typescript
496
+ // Access underlying services for advanced operations
497
+ const processRegistry = sdk.processes;
498
+ const organizationRegistry = sdk.organizations;
499
+ const apiService = sdk.api;
500
+ const crypto = await sdk.getCrypto();
501
+
502
+ // Direct API calls
503
+ const processInfo = await sdk.api.sequencer.getProcess(processId);
504
+ const censusProof = await sdk.api.census.getCensusProof(root, address);
505
+ ```
506
+
507
+ </details>
508
+
509
+ ## ๐Ÿšจ Error Handling
510
+
511
+ The SDK provides detailed error messages for common scenarios:
512
+
513
+ ```typescript
514
+ try {
515
+ const vote = await sdk.submitVote({
516
+ processId: "0x...",
517
+ choices: [1, 2, 3]
518
+ });
519
+ } catch (error) {
520
+ if (error.message.includes('already voted')) {
521
+ console.log('User has already voted in this process');
522
+ } else if (error.message.includes('not accepting votes')) {
523
+ console.log('Voting period has not started or has ended');
524
+ } else if (error.message.includes('out of range')) {
525
+ console.log('Invalid choice values provided');
526
+ } else {
527
+ console.error('Unexpected error:', error.message);
528
+ }
529
+ }
530
+ ```
531
+
532
+ ### Common Error Types
533
+
534
+ - **Process Errors**: Process not found, not accepting votes, invalid configuration
535
+ - **Vote Errors**: Already voted, invalid choices, proof generation failed
536
+ - **Network Errors**: Connection issues, transaction failures
537
+ - **Validation Errors**: Invalid parameters, out-of-range values
538
+
539
+ ## ๐Ÿงช Testing
540
+
541
+ ### Running Tests
542
+
543
+ ```bash
544
+ # Run all tests
545
+ npm test
546
+
547
+ # Run unit tests only
548
+ npm run test:unit
549
+
550
+ # Run integration tests only
551
+ npm run test:integration
552
+
553
+ # Run specific test suites
554
+ npm run test:contracts
555
+ npm run test:sequencer
556
+ npm run test:census
557
+ ```
558
+
559
+ ### Test Environment Setup
560
+
561
+ Create a `.env` file in the test directory:
562
+
563
+ ```env
564
+ SEPOLIA_RPC=https://sepolia.infura.io/v3/your-key
565
+ PRIVATE_KEY=0x...
566
+ TIME_OUT=600000
567
+ ```
568
+
569
+ ## ๐Ÿค Contributing
570
+
571
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
572
+
573
+ ### Development Setup
574
+
575
+ ```bash
576
+ # Clone the repository
577
+ git clone https://github.com/vocdoni/davinci-sdk.git
578
+ cd davinci-sdk
579
+
580
+ # Install dependencies
581
+ yarn install
582
+
583
+ # Run development build
584
+ yarn dev
585
+
586
+ # Run linting
587
+ yarn lint
588
+
589
+ # Format code
590
+ yarn format
591
+ ```
592
+
593
+ ### Code Quality
594
+
595
+ - **TypeScript**: Full type safety
596
+ - **ESLint**: Code linting and style enforcement
597
+ - **Prettier**: Code formatting
598
+ - **Jest**: Comprehensive testing suite
599
+ - **Husky**: Pre-commit hooks
600
+
601
+ ## ๐Ÿ“„ License
602
+
603
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
604
+
605
+ ## ๐Ÿ†˜ Support
606
+
607
+ ### Documentation
608
+
609
+ - [API Documentation](https://github.com/vocdoni/davinci-node/tree/main/api)
610
+ - [Protocol Documentation](https://whitepaper.vocdoni.io)
611
+ - [Examples Repository](https://github.com/vocdoni/davinci-sdk/tree/main/examples)
612
+
613
+ ### Community
614
+
615
+ - [Discord](https://chat.vocdoni.io)
616
+ - [Telegram](https://t.me/vocdoni_community)
617
+ - [Twitter](https://twitter.com/vocdoni)
618
+
619
+ ### Issues and Bugs
620
+
621
+ Please report issues on our [GitHub Issues](https://github.com/vocdoni/davinci-sdk/issues) page.
622
+
623
+ ### Professional Support
624
+
625
+ For enterprise support and custom integrations, contact us at [info@vocdoni.io](mailto:info@vocdoni.io).
626
+
627
+ ---
628
+
629
+ <div align="center">
630
+
631
+ **Built with โค๏ธ by the [Vocdoni](https://vocdoni.io) team**
632
+
633
+ [Website](https://vocdoni.io) โ€ข [Documentation](https://docs.vocdoni.io) โ€ข [GitHub](https://github.com/vocdoni) โ€ข [Twitter](https://twitter.com/vocdoni)
634
+
635
+ </div>