Skip to content

OhShii Locker Integration

This document explains how OhShii Locker relates to the launcher, where locks live (SONS governance locks vs the OhShii Locker product), what the launcher can and cannot do, and how token listing and lock workflows work.

Key distinction up front. A campaign’s SONS locks live on that campaign’s sons_governance canister — this covers the automatic vesting locks created at LGE finalization, the manual “additional” locks users create for that DAO’s token (create_additional_lock), and the lock legs of an approved TreasuryTransferBatch. Manual SONS locks can be created from either frontend — the OhShii launcher frontend or the OhShii Locker frontend — but in both cases the lock is created on, and lives in, the sons_governance canister. This is the opposite of OhShii Locker product locks (OHSHII), which live on lock_storage + the locker backend/strongbox. The frontend you use to create a lock does not determine where it lives: a DAO-token lock always lands on sons_governance, never on lock_storage. No LGE vesting lock is ever created on the OhShii Locker.


Where can I lock or vest token X? (decision table)

Section titled “Where can I lock or vest token X? (decision table)”

Two lock systems exist, and which one applies is decided by the token, not by the app you open. Both the launcher and the Locker frontends offer a lock form; for a DAO token (LGE or Imported DAO) the form always calls that DAO’s sons_governance canister, for OHSHII it calls the locker backend.

Token you holdWhere the lock livesWho can create itWho can modify / cancel itWhat it feedsAvailable today
OHSHIIOhShii Locker: lock_storage record + one strongbox subaccount per lockAny user, from the Locker frontend (create_lock_v2 / create_lock_v3); treasury locks by an approved ONS proposalThe creator can change the recipient (change_lock_recipient); status, deletion and recipient override only by approved ONS proposals (admin_update_lock_status, admin_delete_lock, admin_transfer_lock_recipient)ONS (OHSHII) voting power and lock pointsYes — Locker lock creation is enabled
An ICPSwap LP position pairing OHSHII with another tokenOhShii Locker LP lock: lock_storage record of the OHSHII sideAny user, from the Locker frontend (create_lp_lock); OHSHII must be the governance side, the other side may be any IC tokenThe creator cannot change the recipient of an LP lock (change_lock_recipient and set_lock_recipient_permanent refuse it), and an LP lock cannot be offered for sale; status, deletion and recipient override only by approved ONS proposals (admin_update_lock_status, admin_delete_lock, admin_transfer_lock_recipient)ONS (OHSHII) voting power, from the OHSHII side of the positionYes
An LGE token (a campaign’s own DAO token)That campaign’s sons_governance canister, one custody subaccount per lock (the main account is the DAO treasury)Vesting locks: created automatically at LGE finalization. Manual locks: any holder (create_additional_lock, from the launcher or the Locker frontend). Treasury locks: an approved TreasuryTransferBatch proposal of that DAOThat DAO’s own proposals (ModifyLock, DeleteLock, TransferLockBeneficiary); the creator reclaims a cancelled lock (claim_cancelled_lock) and can change the recipientThat DAO’s voting power only (no lock points, no ONS voting power)Yes, for every existing LGE DAO. LGE creation itself is currently disabled, so no new LGE tokens are being created
An Imported DAO tokenThat Imported DAO’s sons_governance canister — same model as an LGE tokenManual locks: any holder (create_additional_lock). Treasury locks: an approved TreasuryTransferBatch. There are no LGE vesting locksThat DAO’s own proposals, as aboveThat DAO’s voting power onlyYes, for existing Imported DAOs. Imported DAO creation is currently disabled, so no token can become an Imported DAO today
A standalone token that has NOT been importedNowhere as a single-token lock — the platform has no lock or vesting facility for it (it can be the non-OHSHII side of an OHSHII LP lock on the Locker)——NothingNo
Any other third-party ICRC tokenNowhere for new single-token locks. The Locker accepts direct locks of OHSHII only, and its token list is frozen (it can be the non-OHSHII side of an OHSHII LP lock on the Locker)— (locks already held on the Locker for previously listed tokens keep running and are managed by ONS proposals)ONS proposals, for those pre-existing locksLock points, for those pre-existing locksNo

The manual-lock rules of the SONS path (schedule shapes, minimum amount, minimum duration, per-creator cap, fee reservation, rate limits) are listed in Manual locks on a DAO token: the rules below.


ContextWhere locks liveHow they are createdWho manages themLauncher backend can modify?
SONS governance locks — a DAO’s own token: (a) LGE vesting locks, (b) manual additional locks, (c) treasury lock legsEach DAO’s sons_governance canister (per-lock custody subaccount)(a) launcher backend at LGE finalization (batch_create_vesting_locks); (b) any user, from either the launcher OR the OhShii Locker frontend (create_additional_lock, ICRC-2); (c) an approved TreasuryTransferBatch of that DAOThe sons_governance canister + the user (activate, create manual locks, reclaim cancelled) + that DAO’s own SONS proposalsNo. The launcher backend never touches SONS locks; only the user (governance methods) and the governance canister (its own proposals) can.
OhShii Locker (product) — OHSHIIlock_storage + locker backend/strongboxUsers via the locker frontend (OHSHII only); treasury locks via ONS proposalsLocker backend; users via locker frontend; ONS proposals for token list and lock opsNo direct path. Token list and certain lock operations (status, delete, transfer) change only through approved ONS proposals.
  • SONS governance locks are, from the launcher’s perspective, entirely user- and governance-driven: users create manual locks and activate vesting from the frontend, but the authoritative state is in the sons_governance canister. The launcher backend cannot modify them (it only creates the vesting batch at finalization). Both frontends (launcher and locker) offer the manual-lock and reclaim UIs for DAO tokens; both call the same sons_governance methods.
  • OhShii Locker is a separate app (own codebase, canisters, frontend). ONS proposals manage its token list and some lock operations. The launcher backend creates no locks on the Locker.

OhShii Locker canisters (separate project)

Section titled “OhShii Locker canisters (separate project)”
CanisterRole
ohshii_locker_backendLock creation, unlock processing, token list (supported tokens). Privileged methods are callable by the authorized canister ohshii_governance (ONS) via approved proposals.
lock_storageLock records and subaccounts. Used for quadratic voting power (OHSHII) and lock points.
proxySole entrypoint for locker fund movement, gated to the locker backend only (ONS cannot call it directly); wraps transfers to strongbox in a circuit breaker and keeps an independent transfer-status ledger.
strongboxTransfer logging / custody; holds the tokens of Locker locks, one subaccount per lock. Transfers into and out of it route through proxy.
ohshii_locker_frontendLocker web UI (separate app). Users create and manage OHSHII locks here, and DAO-token locks (which it creates on that DAO’s sons_governance).

Launcher and Locker are two separate codebases and deploy pipelines. They share Internet Identity (and on mainnet, alternative origins for the same principal). See ARCHITECTURE.md.


These are all locks on a campaign’s own DAO token, and they all live in that campaign’s sons_governance canister. There are two creation paths:

  • Create — automatic vesting (LGE): The launcher backend creates the vesting locks during LGE finalization by calling sons_governance batch_create_vesting_locks (vesting is mandatory for every LGE). No OhShii Locker canister is involved, for any campaign: when a contributor claims, claim_tokens creates nothing for a vesting campaign, because the tokens are already held by the campaign’s governance canister. These start INACTIVE and the user activates them (below).
  • Create — manual “additional” locks: Any user can lock their own DAO tokens by calling sons_governance create_additional_lock (ICRC-2: approve the governance canister, which pulls the tokens into the new lock’s custody subaccount). This is exposed from both frontends — the OhShii launcher frontend (DAO details / voting-power page) and the OhShii Locker frontend (lock form, DAO-token path) — and in both cases the lock is created on, and managed by, the sons_governance canister (never lock_storage). Manual locks are created Active immediately (no activation step). The lock’s creator is the caller.
  • Custody: Every SONS lock (vesting or manual) holds its tokens in its own custody subaccount of the governance canister — mirroring how the locker holds each product lock in its own strongbox subaccount. Vesting locks are funded from the governance main account async (SONS timer) after finalization; manual locks are funded directly by the user’s ICRC-2 pull at creation. Unlocks are paid out of each lock’s subaccount, and the governance main account is treasury-only. See GOVERNANCE.md → “Per-lock custody isolation”.
  • Activate / claim (vesting only): The launcher frontend lets the user call governance activate_vesting and then dao_storage.confirm_vesting_activation. Unlocks and transfers are handled by the sons_governance canister. Manual locks skip this — they are already Active.
  • Modify: The launcher backend cannot modify any of these locks. Only the governance canister (and the user via governance methods) can. ONS proposals do not apply here; SONS governance can have its own proposals for lock operations (ModifyLock, DeleteLock, TransferLockBeneficiary/Creator, etc.) on that governance’s locks. Consistent with the locker product, a lock a SONS proposal moves to Cancelled leaves its remaining balance in the lock’s custody subaccount, reclaimable by the lock creator via governance claim_cancelled_lock (both the launcher and locker frontends surface a Reclaim button for cancelled SONS locks); a forced Completed or DeleteLock instead sweeps the residual back to the DAO treasury.

OhShii Locker token list (supported tokens)

Section titled “OhShii Locker token list (supported tokens)”

Listing new tokens on the OhShii Locker is currently disabled. The supported-token set is frozen to its current members (OHSHII and the previously listed third-party tokens). The locker backend refuses to add a token id that is not already listed (admin_list_token and addSupportedToken share one predicate), and ONS refuses to create an admin_list_token proposal. Re-listing a token that is already on the list (a metadata refresh) is still accepted.

What ONS proposals can still do to the list — ExecuteAdminMethod targeting ohshii_locker_backend:

  • admin_update_token — update min_lock_amount, logo, etc. (name/symbol/decimals/fee always come from the ledger).
  • remove_supported_token — remove token from the list (existing locks become Cancelled; users can reclaim). OHSHII (7rrmx-tyaaa-aaaal-qsraq-cai) can never be removed — it is a permanent supported token (removing it would cancel every active OHSHII lock). The OHSHII ledger is rejected at the frontend, at ONS proposal creation, and authoritatively in the locker backend.

The launcher backend does not list tokens on the Locker.

  • Create: users create locks via the OhShii Locker frontend (separate app). Only OHSHII can be locked directly on the Locker: the locker backend rejects a direct lock of any other token (“Only OHSHII can be locked directly on the locker; other tokens are locked via their DAO governance”). Locks that already exist on previously listed third-party tokens keep running to their schedule. The launcher backend creates no locks on the Locker.
  • Modify / delete / transfer: The launcher does not do these from the backend. They are done via ONS proposals (ExecuteAdminMethod on ohshii_locker_backend): admin_update_lock_status, admin_delete_lock, admin_transfer_lock_recipient.

The OhShii Locker’s token list is frozen: ONS refuses to create an admin_list_token proposal, and the locker backend refuses to add a token id that is not already listed. No other path lists a token.

The locker’s listing method is admin_list_token (token_id, standard, min_lock_amount, optional logo; name, symbol, decimals and fee are re-read from the ledger). See GOVERNANCE.md.

  • Update: ONS proposal, method admin_update_token (min_lock_amount, logo, etc.). Name/symbol/decimals/fee are always re-fetched from the ledger by the locker.
  • Remove: ONS proposal, method remove_supported_token. Active and Frozen locks are set to Cancelled; users reclaim via the locker. OHSHII can never be removed (rejected at the frontend, at ONS proposal creation, and authoritatively in the locker backend); any other token is removable.

Where min_lock_amount lives (source of truth). The minimum lock amount (token quantity) is stored only in the locker backend, per token, in its supported_tokens entry, and is enforced there at lock creation. ohshii_governance has no min_lock_amount parameter — admin_update_token merely writes the value into the locker’s state; the locker is the sole reader/enforcer. (Contrast the minimum lock duration: for OHSHII that is a votable ONS parameter, min_lock_duration_days, which the locker pulls and syncs — see GOVERNANCE.md.)

3. LGE vesting never creates a lock on OhShii Locker

Section titled “3. LGE vesting never creates a lock on OhShii Locker”

There is no launcher-initiated lock on the Locker. Every LGE’s vesting locks — for every campaign — are created at finalization in that campaign’s sons_governance canister (workflow 5). When a contributor claims on a vesting campaign, claim_tokens creates no lock and calls no Locker canister: the tokens are already held by the campaign’s governance canister, and its timer pays the unlocks.

4. Create a lock on OhShii Locker (user-initiated)

Section titled “4. Create a lock on OhShii Locker (user-initiated)”

The user uses the OhShii Locker frontend (separate app): choose OHSHII, amount and schedule, and create the lock. The locker backend and lock_storage handle creation and lifecycle. The launcher is not involved. Only OHSHII is accepted for a new direct lock. Choosing a DAO token (LGE or Imported DAO) in the same form creates the lock on that DAO’s sons_governance instead (workflow 5).

5. SONS governance locks (sons_governance) — vesting and manual, no OhShii Locker

Section titled “5. SONS governance locks (sons_governance) — vesting and manual, no OhShii Locker”

Every lock on a campaign’s own DAO token lives in that campaign’s sons_governance canister — never on lock_storage. Two creation paths, one home:

  • Automatic vesting (LGE): During LGE finalization, the launcher backend calls governance batch_create_vesting_locks. Locks are stored in the governance canister, each with its own custody subaccount (funded from the governance main account by the SONS timer after finalization); unlocks are paid from those subaccounts, keeping the main account treasury-only. Created INACTIVE — the user activates (below).
  • Manual “additional” locks (user-initiated, either frontend): A user locks their own DAO tokens by calling governance create_additional_lock (ICRC-2: approve the governance canister → it pulls the tokens into the new lock’s custody subaccount). This is available from both the OhShii launcher frontend and the OhShii Locker frontend; both call the same sons_governance method, so the lock lands on the governance canister regardless of which app was used. Created Active immediately. This is the parallel to a Locker-product lock, but for a DAO’s own token it stays in SONS governance — it does not go through the locker backend / lock_storage.
  • Activate (vesting only): User uses the launcher frontend to call governance activate_vesting and then storage confirm_vesting_activation. Unlocks are processed by the governance timer.
  • Cancel / reclaim: A SONS ModifyLock → Cancelled proposal leaves the residual in the lock’s subaccount for the creator to reclaim via governance claim_cancelled_lock (Reclaim button on both frontends) — the same cancel-then-reclaim shape as the OhShii Locker product. Completed/DeleteLock instead sweep the residual to the DAO treasury.
  • The launcher backend cannot modify any of these locks. They are not “on OhShii Locker”; they are in the governance canister. Lock points and the locker token list do not apply to them.

6. Changing a lock’s recipient (creator self-service — both lock systems)

Section titled “6. Changing a lock’s recipient (creator self-service — both lock systems)”

Both lock systems now let the lock’s creator change who receives future unlocks, with the same rules on each side. The lock’s home canister is authoritative and the two implementations are parallel, not shared:

OhShii Locker locksSONS governance locks
Endpoint (home canister)ohshii_locker_backend change_lock_recipient / set_lock_recipient_permanentsons_governance change_lock_recipient / set_lock_recipient_permanent
”Creator” meansfrom_principal (the payer who funded the lock)the recorded creator field — legacy locks with no recorded creator cannot use the feature (fail-closed)
Eligible statusesActive, FrozenActive, Frozen, Inactive (redirecting an Inactive vesting lock also moves the activation right)
Cooldownonce per 24h per lockonce per 24h per lock
Permanent latch at creationcreate_lock_v3 options (recipient_permanent) / locker LockForm checkboxCreateLockArgs.recipient_permanent / launcher creation checkboxes
Governance override (CRITICAL tier)ONS ExecuteAdminMethod admin_transfer_lock_recipientSONS AdminMethod TransferLockBeneficiary
Stable-log visibilitybackend text log (System Status page) + lock_storage LockUpdated eventsLockStateChanged / ErrorOccurred events (per-DAO observability panel)

Shared rules:

  • Each lock records only the change count and the last-change timestamp; both are stamped by the creator path AND by the governance override path.
  • The permanent latch is one-way and binds only the creator-facing endpoints; DAO governance (CRITICAL-tier proposals) retains an override for exceptional cases (e.g. lost-key recovery). A governance change re-arms the creator’s 24h cooldown.
  • LP locks are excluded on both sides; the creator can never modify duration, amount, or schedule — only the recipient.
  • Both endpoints on both canisters are Shield-protected (rate limits + PoW under load).
  • Batch-created LGE vesting locks are covered (creator = recipient at creation, so contributors can redirect or latch their own vesting).

Integrator notes:

  • The three per-lock metadata fields (recipient_change_count, last_recipient_change_at, recipient_permanent) are trailing opt fields on both LockRecord (locker lock_storage) and GovernanceLockRecord (SONS); older records decode as “never changed / not permanent”.
  • A SONS canister must run a wasm that includes the feature: a SONS on an older wasm rejects the new methods (the UI shows an “upgrade pending” message) and silently ignores recipient_permanent at creation (the frontends read the created lock back and warn if the latch did not stick).
  • A DAO relaunch recreates the V-family vesting locks from scratch and resets this metadata.

A manual lock is what create_additional_lock creates (a holder locking their own DAO tokens) and what the lock leg of an approved TreasuryTransferBatch creates (treasury tokens locked for a recipient). Both go through the same validation, so the two can never accept different schedules. The same rules apply to every DAO running the SONS governance canister — LGE DAOs and Imported DAOs alike.

RuleValue
CallerAny non-anonymous principal. The tokens are pulled with ICRC-2: approve the DAO’s governance canister first
Startstart_time must be in the future
Endend_time must be after start_time, and at most 800 days from now (plus a 10-minute grace)
Schedule shapenumber_of_unlocks (1 to 255). The unlock instants are start_time + i × (end_time − start_time) / n for i = 0 … n − 1: the first unlock is at start_time. n = 1 → one-time lock (everything released at start_time); n > 1 → linear lock (equal parts). The frequency field is stored with the lock, but the instants are derived from start, end and count
Minimum durationThe average time from the lock’s creation to its unlock instants must be at least the DAO’s min_lock_duration_days — 45 days by default — with a 10-minute grace. Voting power uses the same test, so an accepted lock earns voting power while the DAO’s minimum stays at or below the one it was accepted under
Minimum amountThe DAO’s min_lock_amount — by default 1,000,000,000,000 base units (10,000 tokens at 8 decimals)
Per-creator capAt most 50 locks recorded with the same creator on that DAO — vesting locks (creator = the buyer) and treasury lock legs (creator = the recipient) count too
Fee reservationOne ledger transfer fee per unlock is reserved out of the amount: the lock holds amount − fee × n, paid as (amount − fee × n) / n per unlock. Refused if the amount does not exceed the reserved fees or the per-unlock amount would be zero
Rate limitLock creation is rate-limited per caller and protected by Shield (rate limits, plus a proof of work under load)
State at creationActive immediately (no activation step); it counts toward that DAO’s voting power

Both defaults (minimum duration and minimum amount) are parameters of each DAO’s own configuration and change only through that DAO’s configuration proposal (new_min_lock_duration_days, new_min_lock_amount).

Where the rules live: src/sons_governance/src/manual_lock.rs (validate_manual_lock_terms, reserve_unlock_fees, build_manual_lock_record); src/sons_governance/src/lib.rs (MAX_VESTING_DURATION_NS, MAX_LOCK_DURATION_GRACE_NS, MIN_LOCK_DURATION_GRACE_NS, MIN_LOCK_AMOUNT, MAX_LOCKS_PER_CREATOR, min_duration_eligible_with_grace); src/sons_governance/src/types.rs (MIN_LOCK_DURATION_DAYS).


Locker

SONS

ONS

Launcher

funds + pays unlocks

batch_create_vesting_locks

proposals: update/remove token, lock ops

create_additional_lock, activate_vesting, claim, claim_cancelled_lock

proposals UI

user locks: OHSHII

create_additional_lock, reclaim: DAO tokens

Launcher backend

Launcher frontend

ohshii_governance

sons_governance

per-lock custody subaccounts

ohshii_locker_backend

lock_storage

strongbox

OhShii Locker frontend

Note the two arrows into sons_governance: both the launcher frontend and the locker frontend let a user create/manage manual locks on a DAO token directly on that campaign’s governance canister. The locker frontend routes OHSHII locks to the locker backend, but DAO-token locks to sons_governance — the destination follows the token, not the app.

  • Token list on locker: ONS proposals (update / remove). Listing new tokens is currently disabled.
  • Locker product locks (OHSHII): locker frontend (user locks) or ONS proposals (treasury locks); stored in lock_storage; lock operations via ONS proposals.
  • SONS governance locks (DAO tokens): In sons_governance only — both LGE vesting locks and user-created manual create_additional_lock locks (creatable from either the launcher or the locker frontend). Each is held in its own per-lock custody subaccount (governance main account stays treasury-only); users activate vesting, create manual locks, and reclaim cancelled locks from the frontend; the launcher backend cannot modify them, and ONS proposals do not touch them (only that DAO’s own SONS proposals do).

  • ARCHITECTURE.md — Canister map and governance scope table.
  • WORKFLOWS.md — Create LGE, token, vote, create/execute proposal, backend vs frontend upgrade, self-snapshot flows.
  • GOVERNANCE.md — ONS vs SONS, proposal categories, snapshot/restore.
  • LOGGING_SYSTEM.md — Three-tier logging architecture, stable memory log details, frontend monitoring.
  • WORLD_ID_VERIFICATION.md — World ID, a legacy verification provider (closed to new proofs; existing verifications stay valid): flow, privacy model, three-state config (not_configured / optional / required), threshold-ECDSA RP signatures, rate limits and governance controls.
  • FRONTEND.md — Frontend architecture: React + JSX + JSDoc @ts-check type-checking, the canister-types.js + utils/canister.js bridges, Candid wire conventions, auth providers, and the TypeScript version policy.
  • Deploy/VOTER_VERIFICATION_GUIDE.md — Verifying WASM hashes for DAO proposals.