Skip to content

Implement ballot creation, eligibility upload and token issuance flow #10

@grantfox-oss

Description

@grantfox-oss

Problem
Ballot creation is the admin entry point for the entire AnonVote
lifecycle and it is broken across three layers. The ballot creation
form on the frontend has no deadline picker, no option management
UI, and no CSV upload capability. The backend has no CSV parsing
or eligibility ingestion route, and raw identifiers are currently
being written directly to the database without being hashed first
— a critical privacy violation. Token issuance runs after
eligibility upload but has no error handling, no retry mechanism,
and no audit trail, meaning failed token deliveries are silent
and unrecoverable.

These three problems together mean an admin cannot successfully
set up a ballot from start to finish without manual database
intervention.

Solution
Implement the full ballot creation, eligibility upload, and token
issuance flow so an admin can create a ballot, upload a voter
list, and have tokens issued and delivered without any manual steps.

Backend

  • Add POST /admin/ballots route that accepts title, options array,
    and deadline — validate all fields and return structured errors
    for missing or invalid inputs
  • Call create_ballot on the Soroban contract via Stellar SDK
    immediately after database write — store the transaction ID
    against the ballot record
  • Add POST /admin/ballots/:id/eligibility route that accepts
    a CSV file upload via multipart form
  • Parse and validate the CSV — reject malformed files early with
    row-level error reporting
  • Pass every identifier through hashIdentifier from @anonvote/crypto
    before any database write — raw identifiers must never be persisted
  • Handle duplicate identifiers gracefully — skip and include in
    the import summary response
  • After eligibility upload, trigger token issuance — generate a
    token per voter via generateToken, store only the hash via
    hashToken, and deliver the raw token via Resend email
  • Log each token issuance attempt to the audit log — success or
    failure — without logging the token value itself
  • Implement a retry queue for failed token deliveries — failed
    sends must not be silently dropped

Frontend

  • Build the ballot creation form — title input, dynamic option
    adder, deadline date/time picker with timezone display
  • Add CSV upload component with drag and drop support, file
    validation feedback, and import summary display after upload
  • Show token issuance progress after eligibility upload — how
    many tokens sent, how many pending, how many failed
  • Allow admin to manually trigger a resend for failed token
    deliveries from the dashboard

Bug Fixes

  • Raw voter identifiers are currently written to the eligibility
    table without hashing — this is a critical privacy bug that
    must be fixed as part of this issue before any other eligibility
    logic is touched
  • The ballot creation route returns 500 when the Soroban contract
    call fails instead of gracefully handling the failure and
    queuing a retry
  • The admin dashboard shows all ballots in a flat list with no
    status filtering — fix to show draft, active, and closed
    ballots in separate tabs

Acceptance Criteria

  • Ballot creation writes to database and anchors to Stellar
    in a single flow
  • Raw voter identifiers never appear in the database, logs,
    or API responses at any point
  • CSV upload returns a row-level import summary with counts
    and rejection reasons
  • Every eligible voter receives a token delivery email
  • Failed token deliveries are logged and retryable from
    the admin dashboard
  • Soroban contract failure does not crash ballot creation —
    it is logged and queued for retry
  • Admin dashboard separates ballots by status
  • Unit tests cover: ballot creation, CSV parsing, duplicate
    handling, token generation, failed delivery retry
  • Integration test covers full flow from ballot creation
    to token delivery confirmation

Note for contributors
The raw identifier hashing bug must be the first thing fixed
before any other work in this issue is started. Do not add
new eligibility logic on top of the existing broken flow —
fix the foundation first. All token values are single-use
secrets. They must never appear in logs, database records,
or API responses — only their SHA-256 hashes are stored.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignbackendBackend-related issuesbugSomething isn't workingenhancementNew feature or requestfrontendFrontend-related issues

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions