Skip to main content

Import

import {
  createVotingClient,
  initializeFromShyConfig,
  assertVotingManifest,
  resolveEffectivePosture,
  VOTING_MANIFEST_CONTRACT_VERSION   // "shyvoting-v1"
} from 'shyware/sdk/web/votingClient.js'

Initialization

const client = await initializeFromShyConfig(shyconfig, options?)
Validates the manifest (assertVotingManifest), resolves runtime signals, and returns a client instance. Throws if contract_version !== "shyvoting-v1" or required fields are missing.
const summary = await client.initialize()
// → { contractVersion, appId, chainId, posture, receipts, identity }

Read methods

await client.getAllPolls()
// → Poll[]

await client.getPoll(pollId)
// → Poll

await client.getTally(pollId)
// → Tally | null — null until poll closes

await client.getVotes(pollId)
// → VoteRecord[]   (List 1 — no identity)

await client.getVoterCount(pollId)
// → number

Ballot build and submit

buildBallot(args)

Constructs a transaction envelope without submitting.
const envelope = await client.buildBallot({
  pollId: 'proposal-42',
  choice: 'yes',           // string for plurality; string[] for approval/ranked
  personId: 'didit-journey-id',    // provider-specific identity input
  identityInput: { ... },          // alternative to personId
  proofHash: '...'                 // optional — pre-computed identity proof hash
})
// → { txJson, ballotId, ballotNonce, encSecret? }
txJson is a JSON-serialized TxTypeBallotCast (type 2) payload ready for submitBallot. ballotNonce is the 32-byte random hex that the caller must retain as the private receipt — it is the only value that enables post-close self-verification. ballotId = H(ballotNonce) is the direction-free public identifier.

submitBallot(txJson)

await client.submitBallot(envelope.txJson)
POSTs to {api.base_url}/ballots. Returns the server response.

castBallot(args) — combined

await client.castBallot({
  pollId: 'proposal-42',
  choice: 'yes',
  personId: 'didit-journey-id'
})
Combines buildBallot + submitBallot + receipt persistence in one call. If a managed receipt store is configured and posture is recoverable, saves the private receipt automatically.

Receipt methods

// Offline verification against the public vote list
await client.verifyReceipt(hexNonce, expectedChoice, votes)
// → { verified: boolean, ballotId, matchedChoice }

// Read receipt from managed store
await client.getPrivateReceipt(pollId)
// → { ballotNonce, choice, pollId } | null

// Save receipt to managed store
await client.savePrivateReceipt(pollId, receipt)

// Post-close Sybil-audit signal — confirm your identity_hash appears in List 2
await client.confirmReceipt(pollId)
verifyReceipt recomputes ballotId = H(hexNonce) and scans the public votes list for a matching record. It does not contact the server — the full vote list is the only dependency.

Posture

const signals = await client.readBrowserRuntimeSignals(manifest, options?)
// → { playIntegrity, deviceAttestation, hostileNetwork, ... }

const posture = client.resolveEffectivePosture(manifest, signals)
// → "recoverable" | "write_only"
When posture resolves to "write_only", castBallot suppresses receipt persistence and returns only { ballotId } — no nonce, no choice, no linkage material retained on device.

Voting methods

Voting methodchoices format
yes_no["yes"] or ["no"]
plurality["option_id"]
approval["opt_a", "opt_b"]
ranked_choice["first", "second", "third"] in preference order

Write-only posture in hostile jurisdictions

For SEDA_HAQQ and other coercion-resistant deployments, set default_posture: "coercion_resistant" and all three write_only_on_* fallbacks to true. After castBallot returns in write-only mode:
  • device retains only ballotId (publicly visible on-chain, direction-free)
  • no ballotNonce stored
  • no encSecret stored
  • a party seizing the device cannot derive vote direction