Bittensor · Multisig Guide
worked examples

Bittensor Key Security · Worked Examples

Multisig on the chain: four real lifecycles

Every multisig on Bittensor leaves the same event trail — NewMultisig → approvals → MultisigExecuted (or MultisigCancelled). Here are four real operations pulled from Finney, each with its live extrinsic data on Taostats, so you can see exactly how a signed transaction moves from proposal to execution — and how it gets abandoned.

How to read these

Pick an example

A 3-of-3 multisig sends a transfer — the full, textbook flow

3-of-3 opened with as_multi call: Balances.transfer_keep_alive 0.01 TAO ✅ Executed

Three signers, all three required. The proposer opened the operation with as_multi, so the entire call — a 0.01 TAO transfer — was written on-chain in the opening extrinsic. Every co-signer could read the exact transaction straight from the chain before approving. This is the complete three-signature chain, start to finish.

multisig 5C9v3RAMbo…52RaLe4Z (threshold 3, three signers) inner call: Balances.transfer_keep_alive → 0.01 TAO
1
Signer A opens the op Multisig.as_multi

Signer A composes the transfer and opens the multisig with the full call on-chain. This emits NewMultisig. Approvals so far: 1 of 3 — nothing moves yet.

signer: 5DXoaFJyg…MPWcSMwc

2
Signer B approves Multisig.approve_as_multi

Signer B reads the call from the chain, confirms it, and approves against the same call_hash. This emits MultisigApproval. Approvals: 2 of 3 — still one short, still nothing executes.

signer: 5EcWJipQv…kaAwibV

Signer C approves — threshold met, it executes Multisig.as_multi

The third signature reaches the threshold. On the same extrinsic, the runtime dispatches the inner transfer from the multisig account. This emits MultisigExecuted with result: Ok. The 0.01 TAO moves. Done.

signer: 5FPCUBpEB…hmXZ29s

The takeaway

Three separate blocks, three separate signers, one transaction. The funds moved only when the third approval landed — no single signer could have moved them alone. Because it was opened with as_multi, signers B and C read the exact call from the chain; no off-chain trust required.

A 2-of-2 multisig — the shortest possible successful path

2-of-2 opened with as_multi call: Balances.transfer_keep_alive 0.005 TAO ✅ Executed

The simplest real case: two signers, both required. Only two signatures exist in the whole lifecycle — the open and the execute. There's no middle "approval" step at all, because the second signature is the threshold. Notice how much shorter the event trail is than the 3-of-3.

multisig 5GG4UvADv…kAnVTfsb (threshold 2, two signers) inner call: Balances.transfer_keep_alive → 0.005 TAO
1
Signer A opens the op Multisig.as_multi

Signer A opens with the full call on-chain. NewMultisig emitted. Approvals: 1 of 2.

signer: 5Gn81EQTZ…GwaxZvCd

Signer B approves — threshold met, it executes Multisig.as_multi

The second and final signature reaches the 2-of-2 threshold and dispatches the transfer in the same extrinsic. MultisigExecuted with result: Ok. Two signatures, two blocks, done — no separate approval round.

signer: 5CgQDQRQ8…TWYmCj6

The takeaway

A 2-of-N multisig has no intermediate MultisigApproval events — the second signature is the threshold, so it goes straight from NewMultisig to MultisigExecuted. Fewer signers = a shorter ceremony, but also less redundancy: in a 2-of-2, losing either key locks the account forever.

The multisig executed — but the transaction it ran failed

2-of-3 opened with approve_as_multi hash-only open ⚠️ Executed → result: Err

This is the case that trips people up. The multisig reached its threshold and dispatched — the outer extrinsic succeeded. But the inner call it dispatched returned an error, so MultisigExecuted carries result: Err. The multisig machinery worked perfectly; the thing it tried to do did not. These are two different outcomes, and reading them as one is a classic mistake.

It was opened with approve_as_multi, so only the hash of the call went on-chain at open time — the signers verified the call off-chain against that hash before the final signer supplied the full call at execution.

multisig 5DcSqBNqC…gbmF5E8s (threshold 2, three signers) opened hash-only — call revealed only at the final signature
1
Signer A opens with the hash only Multisig.approve_as_multi

Signer A opens the op supplying just the call_hash — the call body is not on-chain yet. NewMultisig emitted. Approvals: 1 of 2.

signer: 5GRCukV2r…ZdaQXDXo

!
Signer B supplies the full call — threshold met, it dispatches… and the inner call errors Multisig.as_multi

The second signer reaches the 2-of-3 threshold and provides the full call, which the runtime dispatches. The outer extrinsic succeeds (the multisig did its job) but the dispatched call returns an error — so the event is MultisigExecuted with result: Err. Nothing the inner call intended actually took effect.

signer: 5E7RCRrPV…y3L7AgirGJ

The takeaway — read the two results separately

An extrinsic that shows success on Taostats only means the multisig call was accepted and dispatched. Whether the inner transaction succeeded is a separate signal — the result field inside MultisigExecuted (Ok vs Err). A tool built for signers should surface both: "threshold reached ✅" is not the same as "your transaction did what you wanted ✅."

A pending op that never executed — the depositor cancelled it

2-of-2 opened with approve_as_multi 🚫 Cancelled

Not every multisig op completes. Here the proposer opened an operation, it sat pending without reaching threshold, and the original depositor cancelled it with cancel_as_multi — clearing the pending entry and reclaiming the reserved deposit. Only the person who opened the op (the depositor) can cancel it.

multisig 5FTw4dySS…UTdoCMRc (threshold 2) opened hash-only, sat pending, then withdrawn by the depositor
1
Depositor opens the op Multisig.approve_as_multi

The depositor opens a pending op (hash-only) and reserves the multisig deposit. NewMultisig emitted. Approvals: 1 of 2 — it now waits for a co-signer.

signer: 5FYk9u3v8…BTNht1m

Depositor cancels it Multisig.cancel_as_multi

Roughly 34 blocks later, instead of a second approval, the depositor withdrew the operation entirely. cancel_as_multi references the original op's timepoint and call_hash, removes the pending entry, and returns the reserved deposit. This emits MultisigCancelled. The transaction never executed and never will — it's gone.

signer: 5FYk9u3v8…BTNht1m (same depositor)

The takeaway

A pending multisig op isn't permanent. The depositor can abandon it with cancel_as_multi — useful if a co-signer disappears, the details were wrong, or the transaction is no longer wanted. Only the depositor can cancel, and doing so reclaims the reserved deposit. A signer tool should offer this as a clear "abandon" action, not leave stale ops hanging.

The event trail is always the same

Whatever the threshold or the call, every multisig op walks the same state machine — and every step is a real event you can query on Taostats:

NewMultisig ─ op opened (1st signer). Carries the multisig + call_hash. │ ▼ MultisigApproval ─ each middle signer approves (0 of these in a 2-of-N). │ ▼ ┌─────────────┴─────────────┐ ▼ ▼ MultisigExecuted MultisigCancelled threshold met → dispatched depositor withdrew it result: Ok or Err (deposit reclaimed)
ExampleThresholdOpened withOutcomeSignatures
3-of-3 transfer3-of-3as_multi (call on-chain)Executed · Ok3
2-of-2 transfer2-of-2as_multi (call on-chain)Executed · Ok2
Inner failed2-of-3approve_as_multi (hash only)Executed · Err2
Cancelled2-of-2approve_as_multi (hash only)Cancelled1 + cancel
Provenance: All four examples are real operations on Bittensor Finney, pulled from the Taostats API (/api/event/v1 and /api/extrinsic/v1) and linked to their live extrinsic pages. Addresses are SS58-encoded (network prefix 42) from the on-chain public keys. Amounts converted from RAO (1 TAO = 10⁹ RAO). Every extrinsic link resolves to its Taostats page — verify the raw call data there.