Skip to content

Voting

How a proposal is voted and resolved on the two OhShii governance families:

  • ONS (OhShii Network System) — ohshii_governance, the single OhShii ecosystem DAO. It governs the entire OhShii ecosystem — the launcher, the locker, and the OHSHII token itself (ledger and index) — plus any external dapp that adds it as a controller, which it can then upgrade (backend code and frontend assets) by proposal.
  • SONS — sons_governance, one DAO per LGE campaign. It governs that campaign’s treasury, token ledger suite, asset canister, and pool — and, like ONS, any external dapp added as one of its controllers (same code + asset upgrade powers).

ONS does not govern SONS. While an LGE is in progress the launcher backend and ONS are temporary controllers of the new campaign’s canisters (so OhShii can monitor and finalize it). At completion (finalization) those controllers are removed and the campaign DAO becomes fully autonomous — from then on only its own SONS DAO can act on it; ONS has no power over a completed campaign. See Creating a Liquidity Generation Event for the controller lifecycle.

This page is the voting mechanics reference. For the full proposal catalog (every category and allowlisted method) see Governance: ONS vs SONS; for vote delegation see Liquid Democracy; for the identity layer see Google Verification, Telegram Verification, World ID Verification and DecideID Verification; for how these mechanics add up to capture resistance (threat model, honest limits, and the SNS comparison) see Governance & Capture Resistance.

ONS and SONS share the same two-axis voting engine and (by default) the same threshold constants; they differ mainly in where Voting Power comes from and how identity verification is gated. Those differences are summarised at the end.

  • A vote is a single boolean: decision = true → Adopt (yes), false → Reject (no). There is no Abstain — abstaining means not voting, which counts against quorum.
  • Voting is free. It costs no tokens; you only need eligible lock-based Voting Power. (Only creating a proposal costs a fee — see Proposing.)
  • The anonymous principal is always rejected (guard = "reject_anonymous"), and each call passes through the OhShii Shield (PoW / rate-limit / Defcon) with the PoW solution as the trailing optional argument.
  • Rate limit: at most 3 vote attempts per hour per (caller, proposal). The veto-override round uses a separate counter, so spending your round-1 attempts never locks you out of an override vote.
  • One recorded vote per principal per round; a second attempt is rejected (Already voted on this proposal).

Wire signatures:

ONS : vote(proposal_id: nat64, decision: bool, voting_token: principal, pow_solution: opt blob) -> (variant { Ok; Err: text })
SONS : vote(proposal_id: nat64, approve: bool, pow_solution: opt blob) -> (variant { Ok; Err: GovernanceError })

ONS takes a voting_token (which token’s locks supply your VP — OHSHII for ecosystem proposals, or a completed campaign’s token). SONS always uses its own campaign token.

Voting Power is quadratic and time-weighted:

weight(lock) = token_amount × lock_months²
VP = min( sqrt( Σ weight(lock) ) , 36,000 )
  • Weights are summed first; the square root is applied once, to the principal. For a single lock this reduces to sqrt(token_amount) × lock_months, the familiar form — which is why one lock’s number is unchanged by the aggregation.
  • Splitting a stake across several locks gains nothing, at any level. k locks of A/k tokens for the same duration produce exactly the same VP as one lock of A. The cap (max_voting_power_per_user, default 36,000) then applies to that single aggregate number.
  • Where an interface shows a per-lock figure, it is a share of the aggregate, not a standalone amount: the shares sum to the total and cannot be added to anything else.
  • A lock is eligible only if its duration ≥ min_lock_duration_days (default 45 days = 1.5 months) and its status is Active. A freshly-created Inactive vesting lock grants zero VP until the beneficiary activates it.
  • VP is read live at vote time (recomputed from your current locks on every vote). What is snapshotted at proposal creation is the set of decision thresholds (ProposalVotingSnapshot), so a proposal is judged by the rules in force when it was created (grandfathering) — not your VP balance.
ONSSONS
VP tokenOHSHIIthe campaign’s own token
How VP is readinter-canister call to lock_storage (15s bounded wait; never silently defaults to 0 on failure)computed canister-locally from the DAO’s own locks (get_locks_by_creator, zero ICC)
Quadratic detailfloat sqrt over the summed weightthe same float sqrt over the summed weight — ONS and SONS produce identical numbers for the same locks

A short duration grace (ten minutes) exists so that a lock created a few seconds under a round duration still counts as meeting the minimum. It affects eligibility only; it does not add to the month count, so a lock earns weight for exactly the time it commits.

The quadratic formula is hardcoded (changes only by a canister upgrade); the cap value, the 45-day minimum, and days_in_month are governance-votable.

Every proposal is either Normal (standard) or Critical, which sets its voting window, quorum, and approval rule.

A proposal is Critical when its category is one of: UpgradeCanister, UpgradeGovernanceCanister, UpgradeAssetCanister, CriticalGovernanceOperation, UpgradeCanisterBatch, UpgradeDappBatch. Adding/replacing a guardian also clears Critical thresholds. In addition, a subset of ExecuteAdminMethod proposals run the Critical tier even though the category is Normal — the high-blast-radius AdminMethod calls: on ONS (admin_method_call_is_critical) treasury / balance withdrawals, liquidity removal, ICPSwap position transfers, the treasury-funded liquidity operations (create / add / retry / remove-to-treasury / forward-retry), OhShii Locker lock mutations (update-status / delete / transfer-recipient) and campaign deletion, plus token-metadata edits of the OHSHII ledger and LGE-info edits of the root campaign dao-0000000001; on SONS (is_critical_admin_method) the equivalent AdminMethods — TreasuryWithdraw, RemoveLiquidity, TransferLiquidityPosition, CreatePoolAndAddLiquidity, AddLiquidityToPool, ModifyLock, DeleteLock, TransferLockBeneficiary, TransferLockCreator, BatchSetFollow, UpgradeIndexCanister. Everything else (Motion, config/fee changes, most AdminMethods) is Normal. Only Critical proposals can be guardian-vetoed and can close early.

ParameterDefaultFloor / boundVotable?
Normal voting window3 daysguardian_min ≤ normal ≤ criticalyes
Critical voting window7 days> 0yes
Guardian-created Normal min window1 day> 0yes
Guardian-removal window≥ 7 dayshardcoded floor (never early-close; vetoable-but-overridable)no
Standard quorum (VP)250,000≠ 0yes
Standard min voters15≠ 0yes
Standard approvalsimple VP majority — adopt VP > reject VP (a tie rejects)hardcoded rule—
Critical quorum (VP)350,000≠ 0yes
Critical min voters20≠ 0yes
Critical approval≥ 65%1–99yes
Critical immediate-close VP450,000> 0yes
Critical immediate-close voters30≠ 0yes
Critical immediate-close approval≥ 75%1–99yes
Veto-override approval≥ 80%1–99yes
Veto-override quorum (VP)700,000≠ 0yes
Veto-override min voters50≠ 0yes
Veto-override window24 h≥ 1 day hard flooryes
VP cap per user36,000> 0yes
Min lock duration45 days—yes
Min VP to propose10,000≥ 1yes
Proposal creation fee30,000 tokens±50% per change; ≥ rejection_cost + ~3× ledger_feeyes
Rejection cost25,000 tokens±50% per change; ≤ proposal feeyes
Vote rate limit3 / hour / proposalhardcoded—

The “normal immediate-approval” constants (90% / 20 voters / 500,000 VP) are vestigial — early close is disabled for Normal proposals in the two-axis model. They are retained only for stable-storage / snapshot compatibility.

  • Normal, at the deadline: quorum met iff voters ≥ min_quorum_votes (15) and total_VP ≥ standard_quorum_vp (250,000). If met → Adopt iff adopt_VP > reject_VP (strict; tie → Rejected). If quorum not met → Expired. Normal proposals never close early.
  • Critical, at the deadline: quorum met iff total_VP ≥ critical_quorum_vp (350,000) and voters ≥ critical_min_quorum_votes (20). If met and any votes cast → Adopt iff adopt% ≥ critical_approval_pct (65%), else Rejected. Quorum met but zero votes, or quorum not met → Expired.
  • Critical early close (inside vote(), before the deadline): voters ≥ 30 and total_VP ≥ 450,000 and adopt% ≥ 75% → Approved immediately. The symmetric early reject fires at adopt% ≤ 25%. Guardian gate: if the proposal is vetoable and a guardian is set, an early approval additionally requires the guardian to have voted yes (this preserves a real veto window; the early-reject side has no such gate). A guardian-removal proposal can never close early.

code / asset proposal (awaiting chunk upload)

all other proposals

all WASM/asset chunks uploaded

upload session expires (fee forfeited)

passes (at deadline, or early for Critical)

quorum met but vote fails

quorum not met

guardian veto (Critical/vetoable only)

override round succeeds (>=80% / 700k VP / 50 voters)

veto upheld

execution begins

success

execution error

PendingUpload

Open

Expired

Approved

Rejected

Vetoed

Executing

Executed

ExecutionFailed

The stored status enum is PendingUpload, Open, Approved, Executing, Executed, Rejected, Expired, ExecutionFailed, Vetoed, VetoOverrideExpired. Vetoed is non-terminal (the override round is open); VetoOverrideExpired is a display-only status — once an override window has lapsed the UI shows it, but the stored status stays Vetoed until the timer finalises it to Approved/Rejected. Resolution runs on a periodic timer (ONS ≈ every 5 minutes): expire/close → execute approved → process refunds.

The guardian is a single trusted principal (set via SetGuardian) who can cast a blocking but overridable veto on any Critical/vetoable Open proposal. guardian_veto is deliberately the least-gated update — it is gated by the guardian check alone, so an emergency veto cannot be held back by rate limits or the Shield. It moves Open → Vetoed and opens a separate override round with its own tally and its own vote map (a round-1 voter may vote again; the override round is direct-only — no delegation cascade — and has no early close).

At the override deadline the timer finalises: the override succeeds (Vetoed → Approved) iff override_voters ≥ 50 and override_VP ≥ 700,000 and adopt% ≥ 80%; otherwise the veto is upheld (Vetoed → Rejected). A guardian-removal proposal is itself vetoable-but-overridable: the guardian may veto its own removal, but the community can still remove it by clearing that override supermajority (with a fresh personhood re-attestation per voter) — so removing a guardian that fights its removal takes the 80% / 700,000 VP / 50-voter override, not a bare majority. The guardian can never vote on its own removal in either round. (The 700,000 VP override quorum is intentionally kept reachable — an unreachable bar would turn every veto into a permanent kill.)

An upheld veto blocks the proposer for 30 days. When the override round closes against the proposer (Vetoed → Rejected), that principal cannot create another proposal on that DAO for 30 days; the refusal names the unban date, and the block is checked before the creation rate limit so a blocked proposer never spends their window — nor, on a SONS, their fee approval. An overridden veto (Vetoed → Approved) carries no block, and neither does an ordinary rejection: the penalty is attached to the veto being upheld, not to the proposal failing. Each DAO keeps its own register, so a block on OHSHII governance does not reach any campaign DAO and a block on one campaign does not reach another. It can be lifted early — on OHSHII governance by the guardian, on a campaign DAO by a passed proposal.

Creating a proposal (unlike voting) costs tokens and needs stake:

  • No block from an upheld veto: a proposer whose veto was upheld in the last 30 days is refused up front, before the VP check and before any fee moves (see above). Guardians are exempt.
  • Min VP to propose: live VP ≥ min_vp_to_propose (default 10,000), checked before the fee is charged. SONS additionally requires a raw token balance ≥ 100 tokens (hardcoded floor). Guardians are exempt from the VP/fee checks at proposal creation (and, on SONS, from the 100-token balance floor). ONS proposal creation is currently paused for the transition phase. A fee a guardian has already paid to stage an upload (prepare_wasm_upload / prepare_asset_upload) is attached to its proposal and settled by outcome exactly as for any other proposer.
  • Creation fee: default 30,000 tokens (OHSHII for ONS, the campaign token for SONS), collected via ICRC-2 and frozen on the proposal. The deposit is held by the governance canister itself.
  • Refund (single source of truth refund_for(status)):
OutcomeRefund
Approved / Executed / ExecutionFailedFull (fee − ledger fee)
Rejected / Expired (no quorum)Partial (fee − rejection_cost − ledger fee); the withheld rejection_cost (default 25,000) goes to the platform treasury (ONS) / stays on-canister (SONS)
Vetoed / still Open / ExecutingNone yet
Abandoned paid PendingUploadForfeited

OhShii supports four independent verification methods on two strength levels; a principal is verified if any of them has verified them, and one verification works on every OhShii DAO (verification state lives on ohshii_governance).

  • World ID — Strong, and legacy (closed to new proofs since 2026-09-05; existing verifications keep counting). Scan a World ID document (@worldcoin/idkit); the canister signs the Relying-Party request with IC threshold ECDSA (no HTTPS outcall; the key never materializes). Anti-replay via spent nullifier.
  • DecideID — Strong, and legacy (closed to new proofs since 2026-09-05; existing verifications keep counting). An Internet-Identity-issued JWT Verifiable Presentation validated by pure on-canister crypto (no inter-canister calls, no HTTPS outcalls). Anti-replay via a presentation digest.
  • Google — Standard, live. An attribute bundle signed by Internet Identity asserting a verified Google address; the canister never contacts Google. One Google account per wallet, anti-replay via a salted address digest. Available to Internet Identity sessions only.
  • Telegram — Strong. Control of a phone-verified Telegram account, proved by the signed launch data of a Telegram Mini App session and checked as a signature against Telegram’s published key; the canister makes no outbound call. Anti-replay via a salted per-account fingerprint. Like every provider it ships closed and is opened by an ONS proposal — and, during the transition phase, also by the deploying principals; opened on mainnet on 2026-09-05.

What strength changes. Any method opens standard proposals, delegation, hotkeys, the creation flows and sponsor attribution. A Strong method is additionally required for critical proposals (voting and creating), for a delegated ballot that lands on one, for the LGE purchase-tier ladder, and for the veto-override attestation. Strong is a superset of Standard, so holding one Strong method satisfies everything.

Each provider carries two independent flags: enabled (does it make the gate mandatory?) and accepts_new_proofs (will it still register a new proof?). Closing a provider to new proofs retires it without revoking anyone.

A verification, once granted, persists even if the DAO later disables that method or closes it to new proofs (the check reads the verified-user sets, not the flags). There is no “set verified” backdoor — only the real proof flow adds; the verification state can only be cleared (never forged) by the authorized caller, ohshii_governance executing an approved proposal (and, during the transition phase, also by the deploying principals).

Is verification required to vote? It depends on the DAO family:

Governance actionONS (ecosystem)SONS (per-campaign)
Cast a voteConditional — required only if at least one provider is enabled on ONS. All default OFF, so by default no verification is required. On a critical proposal the requirement rises to a Strong method whenever any Strong provider is enabled.Conditional on requires_verification, which DEFAULTS TO TRUE. An LGE campaign DAO is always created with it ON; an imported DAO may be created with it OFF by the importer (then participation is gated by holding vesting-locked tokens instead). Once created, only a Critical vote can flip it. The Strong requirement on critical proposals applies here too.
Create a proposalSame conditional gate (guardian exempt)Same gate (guardian exempt; plus an emergency exemption — see below)
Delegate / followSame conditional gate (caller + delegate)follow primitives carry the same gate; vote delegation is an ONS feature (see Liquid Democracy)
Guardian vetoNever gatedNever gated

SONS holds no verification store of its own — it calls ohshii_governance.is_verified (10s bounded wait, behind a circuit breaker), then caches a success in a local “Forever Verified” set so the DAO keeps working even during an ONS outage (eviction is restricted to the authorized caller). To prevent a DAO soft-bricking itself (no verified holder left to flip the gate off), an emergency exemption lets any staked holder submit the one narrow rescue proposal — a CriticalGovernanceOperation whose only effect is turning requires_verification off — without being verified.

When the gate is off — either method disabled on ONS, or requires_verification = false on a SONS (e.g. an imported DAO created verification-free) — the system is not ungated:

  • Direct voting / proposing still require VP > 0 (and, on SONS, stake ≥ min_vp_to_propose plus the proposal fee).
  • Delegation substitutes an active-lock anti-Sybil check: follow requires the followee to own at least one Active lock, and set_accepts_followers(true) requires the caller to own at least one Active lock. Because Voting Power derives only from Active, creator-owned locks, participation costs real locked tokens whether or not identity verification is on.
  • Cycle-drain / grinding-DoS defense is independent of the identity gate. vote, create_proposal, follow, and set_accepts_followers are each wrapped by the OhShii Shield (layered rate limits + Argon2id proof-of-work under Defcon + a cycle-reserve floor) at verification level None — the Shield’s cost-of-attack defenses run identically whether requires_verification is true or false. Turning identity verification off does not weaken the canister’s protection against cycle-drain attacks; it only swaps the personhood check for the locked-token check above.

Separate, do not conflate: verification also gates LGE participation economics (purchase tier above Guest, the OHSHII guest fee — both of which need a Strong method, not merely any verification), and sponsor rewards require active-voter status — but those are LGE/economic gates, not governance-voting gates. “Active voter” (recent participation) is a different concept from “verified”, which is itself different from “strongly verified”. See Participating in an LGE.

AspectONS (ohshii_governance)SONS (sons_governance)
Instancesone (ecosystem)one per LGE campaign
Scope / can controllauncher, locker, OHSHII ecosystem + any dapp that adds ONS as a controllerits campaign’s canisters + any dapp that adds that SONS as a controller; ONS can act on the campaign only before completion, autonomous after
VP tokenOHSHIIthe campaign token
VP sourceICC to lock_storagecanister-local
Verification gateconditional on a method being enabled (default off)per-DAO requires_verification (default on)
Verification storesource of truthcalls ONS + “Forever Verified” cache
Vote delegationyes (Liquid Democracy)follow primitives exist; delegation product is ONS
Param routingall voting params via one CriticalGovernanceOperation (set_voting_parameters)split: fees via ConfigChange (Normal); all thresholds/durations/VP-cap/min-lock/requires_verification via CriticalGovernanceOperation
Threshold constants—identical defaults to ONS

Both families snapshot per-proposal thresholds at creation (grandfathering); both reject zero quorum; both forbid changing the formula/critical-category classification without a canister upgrade.