6. Multi Signature Flow
Walk through the full lifecycle of a multi-owner Safe: add an owner, change the threshold, propose with one signer, confirm with another, and execute once the threshold is met.
What you'll learn
Add and remove owners from a Safe programmatically
Change the signing threshold
Propose a transaction signed by one owner, then confirm with a second
Execute a transaction that requires multiple signatures
Handle Transaction Service indexing lag in multi-step flows
Prerequisites
Node.js 18+
A Safe on Sepolia where you control an owner key (starting as 1-of-1)
A private key for the existing Safe owner: testnet only
Sepolia ETH in the Safe for gas (multiple on-chain transactions)
viem: used for address derivation and transaction receipts
Install dependencies:
npm install @safe-global/api-kit @safe-global/protocol-kit @safe-global/types-kit viemConfiguration
The TX_SERVICE_URL points at the Ledger-hosted Transaction Service. Transactions proposed here appear in the Ledger Multisig UI.
Supported chains: Ethereum (1), Optimism (10), BSC (56), Polygon (137), Base (8453), Arbitrum (42161), Sepolia (11155111).
Helper: wait for receipt
Throughout this tutorial, we verify every on-chain transaction. This helper waits for the receipt and throws if the transaction reverted:
Helper: reinitialize Protocol Kit
After any on-chain state change (new owner, new threshold), reinitialize the Protocol Kit to pick up the latest Safe state:
Helper: normalize private keys
Use one key format consistently. This helper accepts either "abc..." or "0xabc...":
Step-by-step
Add a second owner
Starting from a 1-of-1 Safe, generate an ephemeral Owner B key, then add Owner B while keeping the threshold at 1 (so Owner A can still execute alone for now).
What happens on-chain: The Safe calls its own addOwnerWithThreshold(owner, threshold) method, adding Owner B to the owner list.
Verify the new owner was added:
Change threshold to 2
Now require both owners to sign. This is an on-chain Safe transaction:
Critical: After changing the threshold on-chain, you must wait for the Transaction Service to index the new state before proposing new transactions. Otherwise, confirmationsRequired in the API response will still show the old threshold.
Key concepts
How threshold signing works:
A Safe with N owners and threshold T requires at least T unique owner signatures before a transaction can execute.
Propose
Any owner or delegate
Transaction data + first signature submitted to TX Service
No
Confirm
Other owners
Additional signatures submitted to TX Service
No
Execute
Anyone (usually an owner)
All signatures bundled and submitted on-chain
Yes
Signatures are collected off-chain (free) and only the final execution costs gas. This is the core efficiency of the Safe signature model.
For the full guide, see Transactions with Off-chain Signatures.
Tips and pitfalls
Transaction Service indexing lag is critical here. After any on-chain state change (adding an owner, changing threshold), the Transaction Service needs time (10–60 seconds) to index the new state. If you immediately propose a new transaction, the API may report the old
confirmationsRequiredvalue. Always pollgetSafeInfountil it reflects the expected threshold before proceeding.Always verify receipt status.
executeTransactionreturns a hash even if the on-chain transaction reverts. UsewaitForTransactionReceiptand checkreceipt.status:
Reinitialize the Protocol Kit after state changes. The Protocol Kit caches the Safe's owner list and threshold at initialization. After adding/removing owners or changing the threshold, call
Safe.init(...)again to pick up the new state.Private key format must be consistent. Avoid mixing prefixed/non-prefixed keys manually (for example
0x+0x...). Use a key normalizer helper and keep the format uniform.Signature ordering. The Safe contract expects signatures sorted by signer address (ascending, lowercase). The Protocol Kit and Transaction Service handle this automatically when you use
executeTransactionwith a transaction object fromgetTransaction. Don't manually reorder signatures.createRemoveOwnerTxwith threshold. When removing an owner, you can simultaneously lower the threshold. If you set the threshold higher than the remaining owner count, the transaction will revert.Don't lose keys mid-flow. If you change the threshold to 2-of-2 and then lose access to one key, you'll be locked out of the Safe permanently. In production, always test threshold changes carefully and keep backup access plans.
Last updated