Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Building the Multisig Program

Pre-Alpha Disclaimer: This is an early pre-alpha release for exploring the SDK and starting development only. There is no real MPC signing — all signatures are generated by a single mock signer, not a distributed network. Do not submit any real transactions for signing or rely on any security guarantees. The dWallet keys, trust model, and signing protocol are not final; do not rely on any key material until mainnet. All interfaces, APIs, and data formats are subject to change without notice. The Solana program and all on-chain data will be wiped periodically and everything will be deleted when we transition to Ika Alpha 1. This software is provided “as is” without warranty of any kind; use is entirely at your own risk and dWallet Labs assumes no liability for any damages arising from its use.

What You’ll Learn

  • How to design a multisig with fixed members and threshold approval
  • How to store transaction data on-chain for other signers to inspect
  • How to implement both approval and rejection flows
  • How to use transfer_future_sign for partial signature management

Architecture

Creator ──► CreateMultisig (members, threshold, dWallet)
                │
Member 1 ──► CreateTransaction (message data stored on-chain)
                │
Member 1 ──► Approve ──┐
Member 2 ──► Approve ──┼──► threshold reached? ──► approve_message CPI
Member 3 ──► Reject  ──┘                                    │
                                                   transfer_future_sign CPI
                                                            │
                                                   Transaction = Approved

1. Account Layouts

Multisig PDA (["multisig", create_key]) — 395 bytes

FieldOffsetSizeType
disc01always 1
version11always 1
create_key232unique key
threshold342u16 LE
member_count362u16 LE
tx_index384u32 LE (auto-increment)
dwallet4232pubkey
bump741PDA bump
members7532010 × 32-byte pubkeys

Transaction PDA (["transaction", multisig, tx_index_le]) — 432 bytes

FieldOffsetSizeType
disc01always 2
multisig232pubkey
tx_index344u32 LE
proposer3832pubkey
message_hash7032keccak256
approval_count1352u16 LE
rejection_count1372u16 LE
status13910=Active, 1=Approved, 2=Rejected
message_data_len1742u16 LE
message_data176256raw bytes

ApprovalRecord PDA (["approval", transaction, member]) — 68 bytes

Prevents double voting. One per member per transaction.

2. Instructions

DiscNameDescription
0CreateMultisigSet members (up to 10), threshold, dWallet reference
1CreateTransactionPropose with message data stored on-chain
2ApproveVote yes; triggers CPI at threshold
3RejectVote no; marks rejected when impossible to approve

3. Rejection Threshold

A transaction is rejected when enough members reject that approval becomes impossible:

rejection_threshold = member_count - threshold + 1

Example: 2-of-3 multisig → 3 - 2 + 1 = 2 rejections needed.

4. CPI Flow on Approval

When approval_count >= threshold:

#![allow(unused)]
fn main() {
// 1. Approve the message (creates MessageApproval PDA)
ctx.approve_message(
    message_approval, dwallet, payer, system_program,
    message_hash, user_pubkey, signature_scheme,
    message_approval_bump,
)?;

// 2. Optionally transfer future sign authority
if partial_user_sig != [0u8; 32] {
    ctx.transfer_future_sign(partial_user_sig_account, proposer_key)?;
}

// 3. Mark transaction as approved
tx_data[TX_STATUS] = STATUS_APPROVED;
}

Source Code

FrameworkPath
Pinocchiochains/solana/examples/multisig/pinocchio/src/lib.rs
Nativechains/solana/examples/multisig/native/src/lib.rs
Anchorchains/solana/examples/multisig/anchor/src/lib.rs