@@ -25,15 +25,24 @@ error ElectionDatabase__CandidateNotRegistered();
2525/// @notice Thrown when a candidate is already registered in the election
2626error ElectionDatabase__CandidateAlreadyEnrolled ();
2727
28- /// @notice Thrown when a restricted action is attempted during an active election
29- error ElectionDatabase__ElectionActive ();
30-
31- /// @notice Thrown when an election is not currently accepting votes
32- error ElectionDatabase__ElectionClosed ();
33-
3428/// @notice Thrown when the requested election does not exist
3529error ElectionDatabase__ElectionNotFound ();
3630
31+ /// @notice Thrown when the election status is not new
32+ error ElectionDatabase__ElectionNotNew ();
33+
34+ /// @notice Thrown when a restricted action is attempted during an active election, like enrolling a candidate
35+ error ElectionDatabase__ElectionNotActive ();
36+
37+ /// @notice Thrown when an election has already been completed
38+ error ElectionDatabase__ElectionCompleted ();
39+
40+ /// @notice Thrown when trying to perform an action on an archived election
41+ error ElectionDatabase__ElectionAlreadyArchived ();
42+
43+ /// @notice Thrown when trying to complete an election without sufficient data
44+ error ElectionDatabase__CannotCompleteElection ();
45+
3746/// @notice Thrown when an election has no contestants/candidates enrolled
3847error ElectionDatabase__ElectionHasNoContestant ();
3948
@@ -51,6 +60,20 @@ error ElectionDatabase__InvalidAddress();
5160 * - Integration with VoterDatabase and CandidateDatabase
5261 */
5362contract ElectionDatabase is AdminManagement {
63+ /**
64+ * @notice Election status enumeration
65+ * @dev NEW: Election created, candidates can enroll
66+ * @dev ACTIVE: Election open for voting
67+ * @dev COMPLETED: Election closed, results calculated
68+ * @dev ARCHIVED: Election archived, no further modifications allowed
69+ */
70+ enum ElectionStatus {
71+ NEW,
72+ ACTIVE,
73+ COMPLETED,
74+ ARCHIVED
75+ }
76+
5477 /**
5578 * @notice Stores details for a single election
5679 * @dev The registrationTimestamp serves as both a timestamp and a registration flag
@@ -67,8 +90,8 @@ contract ElectionDatabase is AdminManagement {
6790 mapping (address => address ) voterToChosenCandidate;
6891 // voter -> timestamp when they voted in this specific election (0 if not voted)
6992 mapping (address => uint256 ) voterToVoteTimestamp;
70- // used to track whether the election is active or not
71- bool isActive ;
93+ // used to track the current status of the election
94+ ElectionStatus status ;
7295 uint256 totalVotes;
7396 // If > 0, election is registered. Acts as creation timestamp
7497 uint256 registrationTimestamp;
@@ -140,8 +163,11 @@ contract ElectionDatabase is AdminManagement {
140163 /// @notice Emitted when an election is opened for voting
141164 event ElectionOpened (uint256 indexed electionId , address indexed admin );
142165
143- /// @notice Emitted when an election is closed
144- event ElectionClosed (uint256 indexed electionId , address indexed admin );
166+ /// @notice Emitted when an election is completed
167+ event ElectionCompleted (uint256 indexed electionId , address indexed admin );
168+
169+ /// @notice Emitted when an election is archived
170+ event ElectionArchived (uint256 indexed electionId , address indexed admin );
145171
146172 /**
147173 * @notice Ensures the election exists
@@ -157,19 +183,29 @@ contract ElectionDatabase is AdminManagement {
157183 * @notice Ensures the election is open for voting
158184 * @param _electionId ID of the election to check
159185 */
160- modifier onlyOpenElection (uint256 _electionId ) {
161- if (! s_elections[_electionId].isActive)
162- revert ElectionDatabase__ElectionClosed ();
186+ modifier onlyActiveElection (uint256 _electionId ) {
187+ if (s_elections[_electionId].status != ElectionStatus.ACTIVE)
188+ revert ElectionDatabase__ElectionNotActive ();
189+ _;
190+ }
191+
192+ /**
193+ * @notice Ensures the election is in new state
194+ * @param _electionId ID of the election to check
195+ */
196+ modifier onlyNewElection (uint256 _electionId ) {
197+ if (s_elections[_electionId].status != ElectionStatus.NEW)
198+ revert ElectionDatabase__ElectionNotNew ();
163199 _;
164200 }
165201
166202 /**
167- * @notice Ensures the election is closed/inactive
203+ * @notice Ensures the election is not archived
168204 * @param _electionId ID of the election to check
169205 */
170- modifier onlyClosedElection (uint256 _electionId ) {
171- if (s_elections[_electionId].isActive )
172- revert ElectionDatabase__ElectionActive ();
206+ modifier onlyNonArchivedElection (uint256 _electionId ) {
207+ if (s_elections[_electionId].status == ElectionStatus.ARCHIVED )
208+ revert ElectionDatabase__ElectionAlreadyArchived ();
173209 _;
174210 }
175211
@@ -241,7 +277,7 @@ contract ElectionDatabase is AdminManagement {
241277 Election storage newElection = s_elections[electionId];
242278 newElection.name = _name;
243279 newElection.description = _description;
244- newElection.isActive = false ;
280+ newElection.status = ElectionStatus.NEW ;
245281 newElection.totalVotes = 0 ;
246282 newElection.registrationTimestamp = block .timestamp ; // Set timestamp to register the election
247283
@@ -262,7 +298,12 @@ contract ElectionDatabase is AdminManagement {
262298 uint256 _electionId ,
263299 string memory _name ,
264300 string memory _description
265- ) external onlyAdmin onlyRegisteredElection (_electionId) {
301+ )
302+ external
303+ onlyAdmin
304+ onlyRegisteredElection (_electionId)
305+ onlyNonArchivedElection (_electionId)
306+ {
266307 Election storage election = s_elections[_electionId];
267308
268309 election.name = _name;
@@ -305,43 +346,80 @@ contract ElectionDatabase is AdminManagement {
305346 */
306347 function adminOpenElection (
307348 uint256 _electionId
308- ) external onlyAdmin onlyRegisteredElection (_electionId) {
349+ )
350+ external
351+ onlyAdmin
352+ onlyRegisteredElection (_electionId)
353+ onlyNewElection (_electionId)
354+ {
309355 Election storage election = s_elections[_electionId];
310356
311357 // Prevent opening elections with no candidates
312358 if (election.candidates.length == 0 ) {
313359 revert ElectionDatabase__ElectionHasNoContestant ();
314360 }
315361
316- election.isActive = true ;
362+ election.status = ElectionStatus.ACTIVE ;
317363 emit ElectionOpened (_electionId, msg .sender );
318364 }
319365
320366 /**
321- * @notice Closes an election from voting
367+ * @notice Completes an election and calculates results
322368 * @dev Only owner/admins can call this function
323- * @param _electionId ID of the election to close
369+ * @dev Election must be active and have candidates enrolled and votes cast
370+ * @param _electionId ID of the election to complete
324371 */
325- function adminCloseElection (
372+ function adminCompleteElection (
373+ uint256 _electionId
374+ )
375+ external
376+ onlyAdmin
377+ onlyRegisteredElection (_electionId)
378+ onlyActiveElection (_electionId)
379+ {
380+ Election storage election = s_elections[_electionId];
381+
382+ // Ensure election has candidates and votes to calculate a winner
383+ if (election.candidates.length == 0 || election.totalVotes == 0 ) {
384+ revert ElectionDatabase__CannotCompleteElection ();
385+ }
386+
387+ election.status = ElectionStatus.COMPLETED;
388+ emit ElectionCompleted (_electionId, msg .sender );
389+ }
390+
391+ /**
392+ * @notice Archives an election
393+ * @dev Only owner/admins can call this function
394+ * @dev Only completed elections can be archived
395+ * @param _electionId ID of the election to archive
396+ */
397+ function adminArchiveElection (
326398 uint256 _electionId
327399 ) external onlyAdmin onlyRegisteredElection (_electionId) {
328400 Election storage election = s_elections[_electionId];
329- election.isActive = false ;
330- emit ElectionClosed (_electionId, msg .sender );
401+ if (election.status == ElectionStatus.ARCHIVED) {
402+ revert ElectionDatabase__ElectionAlreadyArchived ();
403+ }
404+ if (election.status == ElectionStatus.COMPLETED) {
405+ revert ElectionDatabase__ElectionCompleted ();
406+ }
407+ election.status = ElectionStatus.ARCHIVED;
408+ emit ElectionArchived (_electionId, msg .sender );
331409 }
332410
333411 /**
334412 * @notice Allows a candidate to enroll themselves in an election
335413 * @dev Candidate must be registered in CandidateDatabase
336- * @dev Election must be in closed state
414+ * @dev Election must be in pending state
337415 * @param _electionId ID of the election to enroll in
338416 */
339417 function enrollCandidate (
340418 uint256 _electionId
341419 )
342420 external
343421 onlyRegisteredElection (_electionId)
344- onlyClosedElection (_electionId)
422+ onlyNewElection (_electionId)
345423 onlyRegisteredCandidate (msg .sender )
346424 {
347425 Election storage election = s_elections[_electionId];
@@ -360,15 +438,15 @@ contract ElectionDatabase is AdminManagement {
360438
361439 /**
362440 * @notice Allows a candidate to withdraw themselves from an election
363- * @dev Election must be in closed state
441+ * @dev Election must be in pending state
364442 * @param _electionId ID of the election to withdraw from
365443 */
366444 function withdrawCandidate (
367445 uint256 _electionId
368446 )
369447 external
370448 onlyRegisteredElection (_electionId)
371- onlyClosedElection (_electionId)
449+ onlyNewElection (_electionId)
372450 {
373451 Election storage election = s_elections[_electionId];
374452
@@ -404,7 +482,7 @@ contract ElectionDatabase is AdminManagement {
404482 )
405483 external
406484 onlyRegisteredElection (_electionId)
407- onlyOpenElection (_electionId)
485+ onlyActiveElection (_electionId)
408486 onlyEnrolledCandidate (_electionId, _candidate)
409487 onlyRegisteredVoter
410488 {
@@ -439,6 +517,7 @@ contract ElectionDatabase is AdminManagement {
439517 external
440518 onlyAdmin
441519 onlyRegisteredElection (_electionId)
520+ onlyNonArchivedElection (_electionId)
442521 onlyRegisteredCandidate (_candidate)
443522 {
444523 Election storage election = s_elections[_electionId];
@@ -464,7 +543,12 @@ contract ElectionDatabase is AdminManagement {
464543 function adminWithdrawCandidate (
465544 uint256 _electionId ,
466545 address _candidate
467- ) external onlyAdmin onlyRegisteredElection (_electionId) {
546+ )
547+ external
548+ onlyAdmin
549+ onlyRegisteredElection (_electionId)
550+ onlyNonArchivedElection (_electionId)
551+ {
468552 Election storage election = s_elections[_electionId];
469553
470554 // Find and remove the candidate
@@ -502,22 +586,27 @@ contract ElectionDatabase is AdminManagement {
502586 }
503587
504588 /**
505- * @notice Returns whether the election is currently active
589+ * @notice Returns the current status of the election
506590 * @param _electionId ID of the election
507- * @return Status of the election (true if active)
591+ * @return Status of the election
508592 */
509593 function getElectionStatus (
510594 uint256 _electionId
511- ) external view onlyRegisteredElection (_electionId) returns (bool ) {
512- return s_elections[_electionId].isActive;
595+ )
596+ external
597+ view
598+ onlyRegisteredElection (_electionId)
599+ returns (ElectionStatus)
600+ {
601+ return s_elections[_electionId].status;
513602 }
514603
515604 /**
516605 * @notice Returns comprehensive details of an election
517606 * @param _electionId ID of the election
518607 * @return name Name of the election
519608 * @return description Description of the election
520- * @return isActive Whether the election is currently active
609+ * @return status Current status of the election
521610 * @return candidates Array of candidate addresses enrolled in the election
522611 * @return totalVotes Total number of votes cast in the election
523612 * @return registrationTimestamp When the election was created
@@ -531,7 +620,7 @@ contract ElectionDatabase is AdminManagement {
531620 returns (
532621 string memory name ,
533622 string memory description ,
534- bool isActive ,
623+ ElectionStatus status ,
535624 address [] memory candidates ,
536625 uint256 totalVotes ,
537626 uint256 registrationTimestamp
@@ -541,7 +630,7 @@ contract ElectionDatabase is AdminManagement {
541630 return (
542631 election.name,
543632 election.description,
544- election.isActive ,
633+ election.status ,
545634 election.candidates,
546635 election.totalVotes,
547636 election.registrationTimestamp
0 commit comments