Skip to main content

Prerequisites

  • Node.js 18+ (web SDK)
  • Go 1.22+ (runtime / ABCI node)
  • Docker and Docker Compose (local node)

1. Start a local node

cd POP-U-LIST/shyconfig/node/cometbft
docker compose up
This starts a single-validator development node. The ABCI app and REST API listen on :8080.

2. Initialize a client from a shyconfig

Every embodiment client follows the same pattern: load a shyconfig, initialize, read, build, submit.
import { initializeFromShyConfig } from 'shyware/sdk/web/votingClient.js'

// Load your deployment manifest
const shyconfig = await fetch('/shyconfig.json').then(r => r.json())

const client = await initializeFromShyConfig(shyconfig)

const summary = await client.initialize()
// → { contractVersion: "shyvoting-v1", appId: "pop-u-list", posture: "recoverable", ... }

3. Read a poll

const polls = await client.getAllPolls()
const poll = await client.getPoll('proposal-42')
// → { id, question, options, votingMethod, startTime, endTime, status }

4. Cast a ballot

// castBallot = buildBallot + submitBallot + receipt persistence
await client.castBallot({
  pollId: 'proposal-42',
  choice: 'yes',
  personId: 'didit-journey-id'   // returned by Didit IDV session
})
// → { ballotId }   (direction-free — publicly visible on-chain)
The client:
  1. Derives identity_hash = H("stable_identity:didit:personId") and proofHash
  2. Generates a random ballot_nonce and ballot_id = H(ballot_nonce)
  3. Builds a TxTypeBallotCast payload
  4. POSTs to {api.base_url}/ballots
  5. Persists { ballotNonce, choice, pollId } to the configured receipt store
The canonical runtime verifies the IDV attestation, performs the atomic two-list write, and enforces the count-match invariant.

5. Verify the tally

After the poll closes:
const tally = await client.getTally('proposal-42')
// → {
//     pollId, options, counts, totalVotes, confirmedCount,
//     merkleRoot1, merkleRoot2,
//     publicKey, signature    // HSM-signed — verifiable by anyone
//   }
Verify the HSM signature independently:
go run shyware/verify/cmd/verify \
  --api http://localhost:8080 \
  --poll proposal-42
# → Tally signature: VALID
# → Total votes: 42
# → Confirmed: 39 / 42

6. Verify your own receipt

// Offline — no server contact required
const votes = await client.getVotes('proposal-42')
const result = await client.verifyReceipt(ballotNonce, 'yes', votes)
// → { verified: true, ballotId, matchedChoice: 'yes' }

Other embodiments

The same pattern applies to all five clients. Swap the import and shyconfig:
// Weighted governance (shyshares)
import { initializeFromShyConfig } from 'shyware/sdk/web/sharesClient.js'
const client = await initializeFromShyConfig(shyconfig)
await client.submitWeightedBallot({ proposalId, choice: 'yes', accountCommitment })

// Value transfer (shywire)
import { initializeFromShyConfig } from 'shyware/sdk/web/wireClient.js'
const client = await initializeFromShyConfig(shyconfig)
await client.transfer({ assetId, amount: 100, senderCommitment, recipientCommitment })

// Commodity custody (shycustody)
import { initializeFromShyConfig } from 'shyware/sdk/web/custodyClient.js'
const client = await initializeFromShyConfig(shyconfig)
await client.buildRequestRedemption({ assetId, amount: 5.0, redeemerCommitment })

// Revenue financing (shycontracts)
import { initializeFromShyConfig } from 'shyware/sdk/web/contractsClient.js'
const client = await initializeFromShyConfig(shyconfig)
await client.buildFinancingRemittance({ contractId, payerCommitment, matchedAmount: 12500 })

Next steps

  • shyconfig manifest — full manifest reference
  • Architecture — two-list state machine, identity tiers, count-match invariant
  • votingClient — full voting API reference
  • ZK tier — high-assurance nullifier setup and proof generation
  • Go SDK — embed the ABCI state machine in your own runtime