Skip to content

Commit 4142795

Browse files
committed
refactor: remove ConfigureVoteScreen and integrate election configuration into CreateElectionScreen
fix: update CreateElectionScreen to accept PlainCensus instead of censusId refactor: enhance EndProcessScreen to use getContractAddresses utility fix: ShowResultsScreen to handle optional title and description for questions refactor: streamline VotingScreen by implementing watchVoteStatus for better vote monitoring refactor: WelcomeScreen to simplify contract address verification and improve network info display refactor: update Home component to manage census state and improve step handling refactor: optimize ProcessesPage to utilize DavinciSDK for process management refactor: consolidate contract address retrieval logic in contractAddresses utility
1 parent 496044a commit 4142795

15 files changed

+219
-849
lines changed

examples/ui/.env.example

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
1-
# Vocdoni Sequencer API endpoint
1+
# Vocdoni Sequencer API endpoint (required)
22
SEQUENCER_API_URL=
33

4-
# Vocdoni Census API endpoint
4+
# Vocdoni Census API endpoint (required)
55
CENSUS_API_URL=
66

77
# Ethereum RPC endpoint (for blockchain interactions)
88
# You can get this from providers like Infura, Alchemy, or use public endpoints
99
RPC_URL=
1010

11-
# Explorer URL for viewing addreses, transactions and contracts
12-
EXPLORER_URL=https://sepolia.etherscan.io
13-
14-
# Optional: Custom contract addresses (if not provided, will use default deployed addresses)
15-
ORGANIZATION_REGISTRY_ADDRESS=
16-
PROCESS_REGISTRY_ADDRESS=
17-
18-
# Force using contract addresses from sequencer info endpoint (default: false)
19-
FORCE_SEQUENCER_ADDRESSES=false
11+
# Explorer URL for viewing addresses, transactions and contracts
12+
EXPLORER_URL=

examples/ui/README.md

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,6 @@ RPC_URL=
6464
6565
# Explorer URL for viewing addresses, transactions and contracts
6666
EXPLORER_URL=https://sepolia.etherscan.io
67-
68-
# Optional: Custom contract addresses (if not provided, will use default deployed addresses)
69-
ORGANIZATION_REGISTRY_ADDRESS=
70-
PROCESS_REGISTRY_ADDRESS=
71-
72-
# Force using contract addresses from sequencer info endpoint (default: false)
73-
FORCE_SEQUENCER_ADDRESSES=false
7467
```
7568

7669
#### Environment Variables
@@ -83,9 +76,6 @@ FORCE_SEQUENCER_ADDRESSES=false
8376
- [Alchemy](https://www.alchemy.com/)
8477
- [QuickNode](https://www.quicknode.com/)
8578
- **EXPLORER_URL**: Block explorer URL for viewing transactions
86-
- **ORGANIZATION_REGISTRY_ADDRESS** (Optional): Custom organization registry contract address
87-
- **PROCESS_REGISTRY_ADDRESS** (Optional): Custom process registry contract address
88-
- **FORCE_SEQUENCER_ADDRESSES** (Optional): Use contract addresses from sequencer info endpoint
8979

9080
## Running the Application
9181

@@ -131,7 +121,7 @@ The application follows a step-by-step workflow:
131121
- 10 random wallets are created by default
132122
- Add additional random wallets as needed
133123
- Each wallet gets a private key for voting later
134-
- Publish the census to make it ready for elections
124+
- Create a PlainCensus object that will be used when creating the election
135125

136126
### 4. Create Election
137127
- Configure election details (title, description, end date)
@@ -216,10 +206,12 @@ src/
216206
### DavinciSDK Integration
217207
All screens now exclusively use the DavinciSDK for operations:
218208

219-
- **Census Operations**: `sdk.api.census.createCensus()`, `sdk.api.census.addParticipants()`
220-
- **Process Creation**: `sdk.createProcess()`
221-
- **Voting**: `sdk.submitVote()`, `sdk.getVoteStatus()`
222-
- **Results**: `sdk.getProcess()` (includes results)
209+
- **SDK Initialization**: `new DavinciSDK({ signer, sequencerUrl, censusUrl })` followed by `await sdk.init()`
210+
- **Census Creation**: Census objects (`PlainCensus`, `WeightedCensus`) are created locally and passed to `createProcess()`
211+
- **Process Creation**: `sdk.createProcess({ title, description, census, ballot, timing, questions })`
212+
- **Voting**: `sdk.submitVote({ processId, choices })` - single method handles all proof generation
213+
- **Vote Monitoring**: `sdk.watchVoteStatus()` - async iterator for real-time status updates
214+
- **Results**: `sdk.getProcess(processId)` - includes process details and results
223215

224216
### Wallet Provider Handling
225217
The application properly handles different wallet scenarios:

examples/ui/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"version": "0.0.0",
55
"type": "module",
66
"scripts": {
7-
"preinstall": "cd ../.. && yarn build && yarn pack && mv vocdoni-davinci-sdk-v*.tgz examples/ui/davinci-sdk.tgz",
87
"dev": "vite",
98
"build": "vite build",
109
"lint": "eslint .",
@@ -17,7 +16,7 @@
1716
"@mui/material": "^7.2.0",
1817
"@mui/x-date-pickers": "^8.6.0",
1918
"@tailwindcss/vite": "^4.1.11",
20-
"@vocdoni/davinci-sdk": "file:davinci-sdk.tgz",
19+
"@vocdoni/davinci-sdk": "0.0.3",
2120
"date-fns": "^4.1.0",
2221
"react": "^19.1.0",
2322
"react-dom": "^19.1.0",

examples/ui/src/components/CensusCreationScreen.tsx

Lines changed: 55 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,22 @@ import {
1616
Tooltip,
1717
Typography,
1818
} from '@mui/material'
19-
import { DavinciSDK } from '@vocdoni/davinci-sdk'
19+
import { PlainCensus } from '@vocdoni/davinci-sdk'
2020
import { Wallet, JsonRpcProvider } from 'ethers'
2121
import { useEffect, useState } from 'react'
2222

2323
interface CensusCreationScreenProps {
2424
onBack: () => void
25-
onNext: (censusId: string) => void
25+
onNext: (census: PlainCensus) => void
2626
}
2727

2828
export default function CensusCreationScreen({ onBack, onNext }: CensusCreationScreenProps) {
2929
const [addresses, setAddresses] = useState<string[]>([])
3030
const { walletMap, setWalletMap } = useWallets()
3131
const [newAddress, setNewAddress] = useState('')
3232
const [error, setError] = useState<string | null>(null)
33-
const [isLoading, setIsLoading] = useState(false)
3433
const [censusCreated, setCensusCreated] = useState(false)
35-
const [progress, setProgress] = useState(0)
36-
const [censusId, setCensusId] = useState<string | null>(null)
34+
const [census, setCensus] = useState<PlainCensus | null>(null)
3735

3836
useEffect(() => {
3937
// Generate initial 10 random wallets on component mount
@@ -82,86 +80,28 @@ export default function CensusCreationScreen({ onBack, onNext }: CensusCreationS
8280
}
8381
}
8482

85-
const handleCreateCensus = async () => {
83+
const handleCreateCensus = () => {
8684
if (addresses.length === 0) {
8785
setError('Add at least one address to create a census')
8886
return
8987
}
9088

9189
try {
92-
setIsLoading(true)
9390
setError(null)
94-
setProgress(0)
9591

96-
// Initialize DavinciSDK with first available wallet
97-
const firstWallet = Object.values(walletMap)[0]
98-
if (!firstWallet) {
99-
throw new Error('No wallets available for SDK initialization')
100-
}
92+
// Create a PlainCensus object with all addresses
93+
const newCensus = new PlainCensus()
94+
newCensus.add(addresses)
10195

102-
// Check if wallet already has a provider (e.g., MetaMask)
103-
// If not, connect it to the RPC provider from env
104-
const wallet = firstWallet as Wallet
105-
let signerWithProvider = wallet
106-
if (!wallet.provider) {
107-
if (!import.meta.env.RPC_URL) {
108-
throw new Error('RPC_URL environment variable is required')
109-
}
110-
const provider = new JsonRpcProvider(import.meta.env.RPC_URL)
111-
signerWithProvider = wallet.connect(provider)
112-
}
113-
114-
const sdk = new DavinciSDK({
115-
signer: signerWithProvider,
116-
environment: 'dev',
117-
sequencerUrl: import.meta.env.SEQUENCER_API_URL,
118-
censusUrl: import.meta.env.CENSUS_API_URL,
119-
chain: 'sepolia',
120-
useSequencerAddresses: true
121-
})
122-
await sdk.init()
123-
124-
// Create census
125-
setProgress(20)
126-
const newCensusId = await sdk.api.census.createCensus()
127-
setCensusId(newCensusId)
128-
129-
// Add voters in batches
130-
const batchSize = Math.ceil(addresses.length / 4) // Split into 4 parts for progress
131-
for (let i = 0; i < addresses.length; i += batchSize) {
132-
const batch = addresses.slice(i, i + batchSize)
133-
134-
// Add participants using addresses
135-
await sdk.api.census.addParticipants(
136-
newCensusId,
137-
batch.map((address) => ({
138-
key: address,
139-
weight: '1', // All voters have equal weight
140-
}))
141-
)
142-
setProgress(40 + Math.floor((i / addresses.length) * 40))
143-
}
144-
145-
// Publish the census and store root & size locally
146-
setProgress(90)
147-
const publishResult = await sdk.api.census.publishCensus(newCensusId)
148-
const censusRoot = publishResult.root
149-
const censusSize = await sdk.api.census.getCensusSize(censusRoot)
150-
151-
// Store census details locally for the next screen
152-
localStorage.setItem('censusDetails', JSON.stringify({
153-
censusId: newCensusId,
154-
censusRoot,
155-
censusSize
156-
}))
157-
158-
setProgress(100)
96+
// Store census object for the next screen
97+
setCensus(newCensus)
15998
setCensusCreated(true)
99+
100+
console.log('Census object created with', addresses.length, 'addresses')
101+
console.log('Census will be automatically published when creating the process')
160102
} catch (err) {
161103
setError(err instanceof Error ? err.message : 'Failed to create census')
162104
console.error('Error creating census:', err)
163-
} finally {
164-
setIsLoading(false)
165105
}
166106
}
167107

@@ -179,13 +119,13 @@ export default function CensusCreationScreen({ onBack, onNext }: CensusCreationS
179119
<Card sx={{ mb: 4 }}>
180120
<CardContent>
181121
<Box sx={{ mb: 3, display: 'flex', gap: 2, justifyContent: 'center' }}>
182-
<Button variant='contained' onClick={handleAddRandomWallet} startIcon={<AddIcon />} disabled={isLoading}>
122+
<Button variant='contained' onClick={handleAddRandomWallet} startIcon={<AddIcon />} disabled={censusCreated}>
183123
Add Random Wallet
184124
</Button>
185125
<Button
186126
variant='contained'
187127
onClick={handleCreateCensus}
188-
disabled={addresses.length === 0 || isLoading || censusCreated}
128+
disabled={addresses.length === 0 || censusCreated}
189129
>
190130
Create Census
191131
</Button>
@@ -197,18 +137,17 @@ export default function CensusCreationScreen({ onBack, onNext }: CensusCreationS
197137
</Alert>
198138
)}
199139

200-
{isLoading && (
201-
<Box sx={{ mb: 2, textAlign: 'center' }}>
202-
<CircularProgress variant='determinate' value={progress} sx={{ mb: 1 }} />
203-
<Typography variant='body2' color='text.secondary'>
204-
Creating census... {progress}%
205-
</Typography>
206-
</Box>
207-
)}
208-
209140
{censusCreated && (
210-
<Alert severity='success' sx={{ mb: 2 }}>
211-
Census created successfully!
141+
<Alert severity='success' sx={{ mb: 3, py: 2 }}>
142+
<Typography variant='h6' gutterBottom>
143+
✓ Census Created Successfully!
144+
</Typography>
145+
<Typography variant='body2' gutterBottom>
146+
Census ready with {addresses.length} addresses. It will be automatically published when you create the process.
147+
</Typography>
148+
<Typography variant='body1' sx={{ mt: 2, fontWeight: 'bold' }}>
149+
👉 Click "Next" below to continue to the next step
150+
</Typography>
212151
</Alert>
213152
)}
214153

@@ -217,11 +156,11 @@ export default function CensusCreationScreen({ onBack, onNext }: CensusCreationS
217156
<ListItem
218157
key={address}
219158
secondaryAction={
220-
!censusCreated && (
159+
!censusCreated ? (
221160
<IconButton edge='end' aria-label='delete' onClick={() => handleRemoveAddress(address)}>
222161
<DeleteIcon />
223162
</IconButton>
224-
)
163+
) : null
225164
}
226165
>
227166
<ListItemText
@@ -261,11 +200,37 @@ export default function CensusCreationScreen({ onBack, onNext }: CensusCreationS
261200
</Card>
262201

263202
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
264-
<Button variant='outlined' onClick={onBack} disabled={isLoading}>
203+
<Button variant='outlined' onClick={onBack} disabled={censusCreated}>
265204
Back
266205
</Button>
267-
<Button variant='contained' onClick={() => censusId && onNext(censusId)} disabled={!censusCreated || !censusId}>
268-
Next
206+
<Button
207+
variant='contained'
208+
size={censusCreated ? 'large' : 'medium'}
209+
onClick={() => {
210+
if (census) {
211+
onNext(census)
212+
}
213+
}}
214+
disabled={!censusCreated || !census}
215+
sx={censusCreated ? {
216+
fontSize: '1.1rem',
217+
py: 1.5,
218+
px: 4,
219+
animation: 'pulse 2s infinite',
220+
'@keyframes pulse': {
221+
'0%': {
222+
boxShadow: '0 0 0 0 rgba(43, 108, 176, 0.7)',
223+
},
224+
'70%': {
225+
boxShadow: '0 0 0 10px rgba(43, 108, 176, 0)',
226+
},
227+
'100%': {
228+
boxShadow: '0 0 0 0 rgba(43, 108, 176, 0)',
229+
},
230+
},
231+
} : {}}
232+
>
233+
{censusCreated ? '→ Next: Create Election' : 'Next'}
269234
</Button>
270235
</Box>
271236
</Box>

examples/ui/src/components/CheckElectionScreen.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ import {
1313
ListItemText,
1414
Typography,
1515
} from '@mui/material'
16-
import { VocdoniApiService } from '@vocdoni/davinci-sdk'
16+
import { DavinciSDK } from '@vocdoni/davinci-sdk'
17+
import { JsonRpcSigner, Wallet } from 'ethers'
1718
import { useEffect, useState } from 'react'
1819

1920
interface CheckElectionScreenProps {
2021
onBack: () => void
2122
onNext: () => void
23+
wallet: Wallet | JsonRpcSigner
2224
}
2325

2426
interface ElectionDetails {
@@ -30,7 +32,7 @@ interface ElectionDetails {
3032
censusSize: number
3133
}
3234

33-
export default function CheckElectionScreen({ onBack, onNext }: CheckElectionScreenProps) {
35+
export default function CheckElectionScreen({ onBack, onNext, wallet }: CheckElectionScreenProps) {
3436
const [isLoading, setIsLoading] = useState(true)
3537
const [error, setError] = useState<string | null>(null)
3638
const [electionReady, setElectionReady] = useState(false)
@@ -52,17 +54,18 @@ export default function CheckElectionScreen({ onBack, onNext }: CheckElectionScr
5254
const details: ElectionDetails = JSON.parse(detailsStr)
5355
setCheckStatus((prev) => ({ ...prev, detailsLoaded: true }))
5456

55-
const api = new VocdoniApiService({
56-
sequencerURL: import.meta.env.SEQUENCER_API_URL,
57-
censusURL: import.meta.env.CENSUS_API_URL
57+
const sdk = new DavinciSDK({
58+
signer: wallet,
59+
sequencerUrl: import.meta.env.SEQUENCER_API_URL
5860
})
61+
await sdk.init()
5962

6063
// Start polling for process status and update wait time
6164
const startTime = Date.now()
6265
const pollInterval = setInterval(async () => {
6366
setWaitTime(Math.floor((Date.now() - startTime) / 1000))
6467
try {
65-
const process = await api.sequencer.getProcess(details.processId)
68+
const process = await sdk.api.sequencer.getProcess(details.processId)
6669
setCheckStatus((prev) => ({ ...prev, processExists: true }))
6770

6871
if (process.isAcceptingVotes) {
@@ -124,7 +127,7 @@ export default function CheckElectionScreen({ onBack, onNext }: CheckElectionScr
124127
<List>
125128
{renderCheckItem('Election details loaded', checkStatus.detailsLoaded, false)}
126129
{renderCheckItem(
127-
'Election process created',
130+
'Election process found',
128131
checkStatus.processExists,
129132
!checkStatus.processExists && checkStatus.detailsLoaded
130133
)}

0 commit comments

Comments
 (0)