Skip to main content
A Chipi wallet starts with one owner: whatever passkey or signer the user registered at sign-up. This page covers the two ways to add more signers to that wallet:
  1. Additional devices that each act on their own. Add the user’s laptop passkey alongside their phone passkey. Either device can sign a transfer. This is what most consumer apps want.
  2. Co-signing where multiple signers must approve. Require two signatures (or three of four, etc.) before any transfer goes through. This is what agent treasuries, shared wallets, and high-value accounts want.
Both routes share the same on-chain account. You’re not creating a second wallet — you’re widening who controls the existing one.
Co-signing needs a SHHH V8.5 wallet (class 0x067828c8…bd96a, the default for wallets created with 14.17.0+). On V8.4 and earlier the contract checks the threshold only on threshold envelopes: one owner signing alone with a single-owner envelope still executes, so a “2-of-3” V8.4 wallet is really 1-of-3. Chipi’s relay refuses to raise the threshold above 1 on those wallets (422 SHHH_THRESHOLD_UNSUPPORTED), and V8.x wallets can’t upgrade: a V8.4 wallet that needs N-of-M moves to a new V8.5 wallet. Independent owners (threshold 1) work the same on both.
Available without advertisement until external audit closes.

When to use which

Concrete examples in production today:
  1. Agent treasuries — an automated agent spends up to $X/day on its own, anything bigger requires a co-sign from the user’s passkey. 2-of-2 above the threshold, 1-of-2 below.
  2. AI-API service accounts — a service account shares custody with a human operator. Routine API spend doesn’t prompt; settlement transfers do.
  3. Co-signed org treasuries — Chipi credits balance is N-of-M with key roles distributed across finance + ops + the founder.

Compared to single-owner

Single-owner SHHH wallets sign via one V2_SNIP12 envelope:
A threshold wallet signs via a threshold envelope wrapping several inner frames, each prefixed by its length. A frame is one owner’s single-owner envelope without its V2_SNIP12 tag, which the outer tag replaces:
buildThresholdEnvelope takes the per-kind builders’ envelopes as they are and drops the tags (14.17.0+; earlier versions kept them, and the contract rejected those envelopes with THRESH: bad owner_id). Each inner envelope can be a different signer kind. The wallet validates each inner envelope against its registered verifier class, rejects a repeated owner, sums the verified owners’ weights, and admits the call only when that sum reaches the threshold. The threshold is not in the envelope: the set of owners, their weights and the threshold live on-chain. On V8.5 the single-owner envelope is held to the same rule: an owner whose weight is below the threshold gets SHHH: below threshold, with one exception, a lone cancel_recovery naming that owner (the recovery veto). An owner whose weight alone meets the threshold can still sign alone. One consequence for multisig coordinators: on V8.4 each collected approval is by itself a valid single-owner signature for the same transaction, so treat stored approvals for V8.4 wallets as executable.

Owner kinds within a threshold

Anything that works as a single-owner signer kind works as a threshold owner. You can mix:
  • An EOA on MetaMask (EIP191_SECP256K1) co-signs with a passkey (WEBAUTHN_P256)
  • A Phantom / Solana wallet (ED25519) co-signs with a STARK key held server-side
  • A JWT_ES256 service account co-signs with a STARK key held server-side
  • A Starknet wallet the user already has, such as Bramble (STARK_SNIP12), co-signs with a passkey. Bramble signs with wallet_signTypedData and buildStarkSnip12Envelope wraps its signature into an inner envelope. See Trusted wallets
  • A guardian (role: "GUARDIAN") does NOT count toward the threshold for normal transactions — guardians only initiate recovery, never co-sign
Each kind needs its verifier class registered on the wallet. A wallet starts with its first owner’s kind only; register another with propose_add_verifier_class (48h) and execute it before execute_add_owner. See New signer kinds.

Configuring N and M

The wallet is created with N initial owners (M=N at start). Add or remove owners post-creation via the recovery flows in recovery:
  • propose_add_owner → execute_add_owner after 48h
  • propose_remove_owner → execute_remove_owner after 24h
  • propose_set_threshold → execute_set_threshold after 48h (above 1 only on V8.5+)
  • propose_add_verifier_class → execute_add_verifier_class after 48h, for a signer kind the wallet doesn’t verify yet
Threshold changes are timelocked because they’re governance-grade — a malicious add-owner that immediately set threshold=1 would be a wallet takeover. The 48h window gives the owners a chance to cancel_pending_op. On a V8.5 wallet with threshold 2+, every one of these calls, cancels included, needs the threshold too: one owner alone can only cancel a recovery.

SDK status

External wallets that don’t expose a key (MetaMask, Phantom, Bramble)

MetaMask, Phantom and Bramble never hand you a private key — the user signs in their wallet and you get a signature back. Each has a builder that wraps that signature into an inner envelope: Bramble signs typed data, never a raw hash: have it sign starkSnip12ApprovalTypedData({ walletAddress, messageHash: oeHash }) with wallet_signTypedData, then pass the result as returned to buildStarkSnip12Envelope({ signature, ownerIndex }). For Phantom, the wallet must sign the exact bytes returned by ed25519SignedBytes(messageHash) — the 64-byte hex-ASCII encoding of the OE hash — not the raw 32-byte hash. The on-chain verifier reconstructs those same bytes, so signing anything else fails verification.

Gas

The paymaster sums the per-kind gas overhead across each inner envelope. A 2-of-2 STARK + EIP-191 OE budgets 2M + 10M = 12M l2_gas for verification on top of the call’s own cost. See the passkey API reference for the kinds available client-side; gas tables live with the backend builders. This composes — you don’t budget gas yourself. The paymaster reserves it automatically based on the envelope shape.

Threshold + recovery together

The headline product story for SHHH (V8.5+) is threshold combined with guardian recovery:
  • Day-to-day operations require N-of-M signatures
  • Lost-key recovery still works because a guardian can initiate initiate_recovery even if N-1 owners have lost their keys
  • Any one owner can still stop a recovery with cancel_recovery naming itself, even below the threshold
Take a 2-of-3 wallet (three owners of weight 1, threshold 2) with one guardian. If owners are lost, the guardian starts a recovery. Recovery adds one owner and leaves the threshold unchanged. After 7 days, remove the lost owners (propose_remove_owner, 24h each).