Multisig wallets are the governance primitive of DeFi. They protect treasuries worth hundreds of millions of dollars, gate protocol upgrade paths, and act as the final authority on emergency pauses. Yet the historical record of production multisig failures reads less like a catalogue of smart contract bugs and more like a catalogue of human failures: compromised developer machines, blind signing, keys collocated on the same infrastructure, and threshold configurations that were never re-evaluated after the initial deployment.
This article dissects every layer of that risk surface—starting with the architecture of Safe (formerly Gnosis Safe), moving through signature mechanics, module attack surface, key management, and operational security, and ending with a production checklist.
Gnosis Safe Architecture and Its Security Model
Safe began as the Gnosis Multi-signature Wallet in 2017 and evolved into Gnosis Safe in 2018, introducing a modular architecture and enhanced security features. Compared to its predecessor, Gnosis Safe introduced a proxy-singleton architecture that massively optimised gas efficiency. Today the protocol holds over $100 billion in assets and is deployed on more than 100 EVM networks.
Safe is a non-custodial smart contract wallet platform that secures digital assets through multi-signature control and modular transaction execution, primarily used for DAO treasuries and collective asset management. Unlike an EOA, a Safe is an account on the blockchain controlled not by a private key directly, but by smart contract code—an architectural difference that unlocks enhanced security features.
The Proxy-Singleton Model
The core deployment pattern uses a minimal proxy (EIP-1167 clone) that DELEGATECALLs into a shared singleton implementation. This means every Safe deployment shares logic but maintains independent storage. The tradeoffs are:
- Gas efficiency: Proxy deployment costs far less than deploying a full implementation.
- Upgrade path: The singleton can be upgraded, but only by the owners of each individual Safe—a key distinction from purely upgradeable proxies where an admin can silently change logic.
- Attack surface: Because the proxy stores the
masterCopyaddress in slot 0, any operation that writes to slot 0 can replace the implementation entirely.
// Simplified Safe proxy pattern
contract SafeProxy {
// Slot 0: masterCopy address (the singleton implementation)
address internal masterCopy;
constructor(address _masterCopy) {
require(_masterCopy != address(0), "Invalid master copy");
masterCopy = _masterCopy;
}
fallback() external payable {
// All calls are delegated to the singleton
assembly {
let _masterCopy := and(
sload(0),
0xffffffffffffffffffffffffffffffffffffffff
)
calldatacopy(0, 0, calldatasize())
let success := delegatecall(
gas(), _masterCopy, 0, calldatasize(), 0, 0
)
returndatacopy(0, 0, returndatasize())
switch success
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
Transaction Execution Flow
The core workflow involves three key steps: proposal, confirmation, and execution. Safe allows for multiple owners, with the ability to establish an N-out-of-M scheme where transactions are approved with the consent of N owners. The execTransaction function on the Safe singleton performs the following checks before execution:
- Verifies that the packed signature array contains at least
thresholdvalid signatures. - Checks that each signer is a registered owner and that no duplicate signers exist.
- Verifies the nonce matches the Safe’s current nonce, incrementing it on success.
- Executes the call,
DELEGATECALL, orCREATEoperation according to theoperationparameter.
// Core Safe execution (simplified from Safe v1.4.x)
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation, // 0 = CALL, 1 = DELEGATECALL
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures
) public payable virtual returns (bool success) {
bytes32 txHash = getTransactionHash(
to, value, data, operation, safeTxGas,
baseGas, gasPrice, gasToken, refundReceiver,
nonce
);
checkSignatures(txHash, data, signatures);
nonce++;
// ... execute and handle gas refunds
}
The nonce prevents replay attacks. Each transaction should have a different nonce, and once a transaction with a specific nonce has been executed it should not be possible to execute that transaction again.
Signature Replay in Multisig Transactions
Replay attacks are among the most consistently exploited vulnerability classes in multisig systems. The attack surface splits into two categories: cross-chain replay and same-chain replay.
Cross-Chain Replay
Each EIP-712 signature includes a domain containing: name, version, chainId, verifyingContract, and optionally a salt. Without a chainId binding, a signature valid on Ethereum mainnet can be replayed on any EVM-compatible fork or L2 where the same contract address exists—a real risk for protocols that deploy deterministically via CREATE2.
EIP-712 solved the core problem by introducing typed structured data (human-readable fields), domain separation (preventing cross-application replay), and standardized encoding (ensuring consistent cryptographic properties).
Safe’s domainSeparator is computed as:
// EIP-712 domain separator for Safe
bytes32 public constant DOMAIN_SEPARATOR_TYPEHASH =
keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
function domainSeparator() public view returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_SEPARATOR_TYPEHASH,
block.chainid,
address(this)
)
);
}
Note that Safe intentionally excludes name and version from the domain separator but binds to chainId and verifyingContract. This means:
- Signatures are always Safe-specific (no cross-contract replay).
- Signatures cannot be replayed cross-chain.
- However, if you sign a transaction with a stale nonce and it has not yet been executed, it remains valid until consumed.
Signature Malleability
The Elliptic Curve Digital Signature Algorithm (ECDSA) employed by Ethereum is vulnerable to signature malleability attacks. An attacker can modify specific parts of a signature without access to the private key, creating a new valid signature corresponding to the original message.
Due to ECDSA signature malleability, an attacker can produce a second valid signature from any existing one by flipping the s value. Track by message hash instead, or better yet, use nonces which inherently prevent replay regardless of malleability. Safe addresses this by consuming the nonce on each execution, making malleability a non-issue for executed transactions—but custom modules that do not implement replay protection are a separate risk.
Module-Level Replay
Modules need to implement their own replay protection. This is a critical but frequently overlooked requirement. Any module that accepts off-chain signatures to authorize transactions without an internal nonce or deadline can be drained by replaying any previously collected valid signature set. The standard fix is a full EIP-712 + nonce + deadline stack:
// Minimal replay-safe module signature verification
contract SafeModule {
bytes32 public constant MODULE_TYPEHASH = keccak256(
"ModuleExec(address to,uint256 value,bytes data,uint256 nonce,uint256 deadline)"
);
mapping(address => uint256) public nonces;
function executeWithSig(
address safe,
address to,
uint256 value,
bytes calldata data,
uint256 deadline,
bytes calldata signature
) external {
require(block.timestamp <= deadline, "Signature expired");
bytes32 structHash = keccak256(
abi.encode(
MODULE_TYPEHASH,
to, value, keccak256(data),
nonces[safe]++,
deadline
)
);
bytes32 digest = _hashTypedData(structHash);
address recovered = ECDSA.recover(digest, signature);
require(isSafeOwner(safe, recovered), "Unauthorized");
ISafe(safe).execTransactionFromModule(to, value, data, 0);
}
}
DELEGATECALL Risks in Safe Modules
DELEGATECALL is Safe’s most powerful—and most dangerous—execution mode. It executes external code inside the Safe’s own storage context.
While most wallet contracts only support CALL operations, adding DELEGATECALL operations allows enhancement of the wallet’s functionality without updating its code. As a DELEGATECALL is executed in the context of the wallet contract, it can potentially mutate the state of the wallet (like changing owners) and therefore should only be used with known, trusted contracts.
The Bybit hack of February 2025 is the definitive production demonstration of what happens when this invariant is violated. On 21st February 2025, Bybit suffered the largest cryptocurrency theft ever recorded, with more than $1.4 billion in assets drained from its cold wallet. The attack compromised the transaction approval process by altering what Bybit’s signers saw when approving a cold wallet transaction, causing them to unknowingly authorize a transaction that resulted in a loss of funds.
The key vulnerability was a delegatecall exploit—a function in Ethereum that allows a smart contract to execute another contract’s code within its own storage context, modifying the critical storage variables of the original contract. The attackers tricked Bybit’s signers into changing their wallet’s contract logic to a malicious version, effectively granting full control.
The successful manipulation of the transaction enabled the attackers to alter its payload, replacing it with a delegation call to a pre-deployed malicious contract. The pre-deployed malicious contract introduced sweepETH and sweepERC20 functions to the smart contract, allowing the attackers to transfer funds without requiring multisig approval.
Concrete Storage Collision
When a DELEGATECALL target writes to slot 0, it overwrites the Safe proxy’s masterCopy pointer. A malicious contract needs only three lines of logic to take over a Safe:
// Malicious delegatecall target (simplified Bybit attack pattern)
contract MaliciousImpl {
// Slot 0 in the Safe proxy context = masterCopy
address private _masterCopy; // slot 0
function sweep(address payable recipient) external {
// Overwrites masterCopy to point to attacker-controlled implementation
_masterCopy = recipient; // writes to slot 0 of the *proxy*
// Or more directly: drain all ETH
recipient.call{value: address(this).balance}("");
}
}
Mitigating DELEGATECALL Risk
The recommendation from Trail of Bits is to disable delegatecall functionality entirely, or alternately, design the on-chain components so a signature is only valid for a specific implementation being called into by delegatecall.
Teams that cannot give up flexibility entirely should start using a guard that prevents delegate calls. Make sure the guard is audited. A minimal delegatecall-blocking guard looks like this:
// Guard that blocks all DELEGATECALL operations
contract NoDelegatecallGuard is BaseGuard {
function checkTransaction(
address,
uint256,
bytes memory,
Enum.Operation operation,
uint256, uint256, uint256,
address, address payable,
bytes memory,
address
) external pure override {
require(
operation != Enum.Operation.DelegateCall,
"Guard: delegatecall not allowed"
);
}
function checkAfterExecution(bytes32, bool) external override {}
}
Safe Guards are meant for adding extra layers of security on top of the N-out-of-M scheme. When a Guard is implemented, every outgoing transaction from the wallet must pass the Guard’s checks.
The Risk of Single-EOA Admin vs. Multisig
The contrast between a single-EOA admin and a multisig is not subtle.
The single-EOA design presents a critical vulnerability: a single point of failure. If this private key is lost, stolen, or otherwise compromised, the funds associated with that EOA are irretrievably gone.
The fundamental architecture of a multisig transforms asset custody from an individual responsibility into a secure, collaborative process, mitigating single points of failure like lost keys or malicious actors.
In practice, a surprising number of “multisigs” still have EOA escape hatches: an owner function callable directly by a single deployer key, a pause() function gated only to a team Gnosis Safe but with a single owner at threshold 1/1, or an admin key that was never rotated from the deployer wallet.
// DANGEROUS: Single EOA admin on a protocol
contract UnsafeVault {
address public admin; // Single point of failure
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
// One compromised key = total loss
function emergencyWithdraw(address token, uint256 amount)
external onlyAdmin
{
IERC20(token).transfer(admin, amount);
}
}
// SAFER: Multisig-gated with timelock
contract SaferVault {
address public immutable multisig;
ITimelock public immutable timelock;
modifier onlyTimelock() {
require(msg.sender == address(timelock), "Not timelock");
_;
}
// Requires multisig approval + 48h timelock delay
function setEmergencyWithdrawTarget(address newTarget)
external onlyTimelock
{
_emergencyTarget = newTarget;
}
}
The pattern of unilateral admin risk extends to governance. A single EOA that can call upgradeTo on a proxy, change a critical oracle address, or modify fee recipients without multisig approval represents a full protocol compromise from a single phishing attack.
Threshold Configuration and Key Management
Choosing Threshold and Signer Count
Avoid N-of-N schemes, as the loss of a single key would result in a permanent loss of access to all funds. The standard recommendation is an M-of-N configuration where M is strictly less than N, providing both security and redundancy. In practice:
| Configuration | Risk Profile |
|---|---|
| 1-of-3 | Any one signer can unilaterally drain the treasury |
| 2-of-3 | Minimal viable threshold; one compromised key + one colluding signer = total loss |
| 3-of-5 | Reasonable for small teams; tolerates one lost key |
| 4-of-7 | Common for protocol governance with distributed signer set |
| 5-of-9 | Enterprise/bridge scale; still failed Ronin due to key collocation |
Select the number of parties commensurate with the value of the assets. Use a threshold that is less than the total (M < N) so that one lost key won’t cause a disaster.
Key Distribution
The security of a multisig depends entirely on the operational security (OpSec) of its individual signer keys. Storing multiple signer keys on the same device or in the same physical location negates the security benefits.
The Ronin bridge collapse is the canonical example. The Ronin bridge operated a lock-and-mint model, with withdrawals guarded by a 5-of-9 multisignature scheme, but the validator set was small and centralised, with Sky Mavis operating four validators itself. A compromise of just one external validator key was enough to meet the threshold.
The Axie DAO Validator lent out their multisig to Sky Mavis so that Sky Mavis could authorise transactions quickly due to a high number of users. The Axie DAO Validator received back its control later, but the key was never deleted from the Sky Mavis servers. When the attacker got into the Sky Mavis systems, they found all five keys needed to sign transactions.
The lesson: delegation of signing authority must be accompanied by explicit revocation. Stale keys on shared infrastructure are as dangerous as compromised keys.
Key Rotation
Key rotation is often neglected after initial setup. A signer departing the team should trigger an immediate rotation transaction. In Safe, removing a signer and adjusting threshold is a standard execTransaction call to the Safe itself:
// Encoding a Safe owner removal via Safe's own interface
// This transaction must be signed by the current threshold of signers
bytes memory data = abi.encodeWithSignature(
"removeOwner(address,address,uint256)",
prevOwner, // Address of previous owner in the linked list
ownerToRemove, // Address of the owner to remove
newThreshold // New threshold after removal
);
// This is queued and signed by remaining owners,
// then executed via execTransaction on the Safe itself
The Guardian Pattern and Its Tradeoffs
The guardian pattern introduces a secondary multisig (or a smart contract with limited authority) whose sole power is to pause or veto transactions. It is distinct from the primary operational multisig.
// Simple guardian pattern implementation
contract ProtocolWithGuardian {
address public multisig; // Full control after timelock
address public guardian; // Emergency pause only
bool public paused;
modifier notPaused() {
require(!paused, "Protocol paused");
_;
}
modifier onlyMultisig() {
require(msg.sender == multisig, "Not multisig");
_;
}
modifier onlyGuardian() {
require(
msg.sender == guardian || msg.sender == multisig,
"Not guardian"
);
_;
}
// Guardian can pause instantly with no timelock
function pause() external onlyGuardian {
paused = true;
emit Paused(msg.sender);
}
// Unpausing requires multisig and timelock
function unpause() external onlyMultisig {
paused = false;
emit Unpaused(msg.sender);
}
// Guardian can be replaced only by multisig + timelock
function setGuardian(address newGuardian) external onlyMultisig {
guardian = newGuardian;
}
}
The tradeoffs are real. A guardian that can pause arbitrarily is itself a governance attack surface: a compromised guardian key can halt a live protocol indefinitely, causing significant user harm and reputational damage even without draining funds. Some protocols mitigate this by having the guardian’s pause authority expire after a fixed window, requiring an active multisig vote to extend the pause:
// Expiring pause with guardian
uint256 public pauseExpiry;
uint256 constant MAX_PAUSE_DURATION = 3 days;
function pause() external onlyGuardian {
paused = true;
pauseExpiry = block.timestamp + MAX_PAUSE_DURATION;
}
function isPaused() public view returns (bool) {
return paused && block.timestamp < pauseExpiry;
}
Timelocks enforce a mandatory delay between the approval of a transaction and its execution. This provides a critical window for the community or team to detect and react to malicious proposals. Combining a guardian with a timelock-gated primary multisig gives you two complementary mechanisms: fast emergency response without requiring the full governance overhead, but no single key that can permanently disable the protocol.
Organizations can establish veto quorums to bypass time-locks in case of emergency. The standard quorum plus two additional signers should be required to bypass the timelock.
Hardware Wallet Requirements for Key Holders
Every signer on a protocol multisig controlling material value must use a hardware wallet. This is not optional.
Strong OpSec means hardware wallets for any privileged role, zero digital seed storage, strict separation between browsing and signing environments, least-privilege permissions for deploy and admin actions, and aggressive access revocation when people or responsibilities change.
The Bybit incident confirmed that hardware wallets alone are not sufficient if signers don’t read what is actually displayed on the device screen. The signers could not see what they were signing due to two critical issues: malware that modified the wallet interface and the limitation of blind signing on hardware wallets, which do not display complete semantic information on what is being signed by the user.
Key hardware wallet requirements for protocol signers:
1. Dedicated devices only. Device security assessment should enforce dedicated cold devices only—no email, no Telegram, no code. A signing device that is used for general internet browsing has an attack surface orders of magnitude larger than a dedicated device.
2. Verify on the device screen, not the UI. When the signers reviewed the transaction but did not verify the calldata on their physical hardware devices, everything appeared correct. This underscores the critical need for thorough transaction verification beyond what’s displayed on the screen. The computers showed a spoofed transaction, but the hardware wallets showed the malicious transaction.
3. Independent hash computation. Establish a mandatory verification process for every transaction. Require signers to use at least one additional method—a command-line tool or an alternative web UI—to independently compute the transaction hash. Compare this independently generated hash with the one displayed on the signing device. If any discrepancy is found, do not proceed.
4. Physical security of the device. Use fireproof and waterproof safes (UL-rated) or bank safety deposit boxes for seed phrase backups. Do not store all backups in a single geographic location.
5. Understand the specific transaction fields. When signing a Safe execTransaction call, the hardware wallet EIP-712 display should clearly show:
to: destination address — verify the full address, not just prefix and suffixvalue: ETH value being sentdata: calldata — for simple transfers, this should be empty or a recognizable function selectoroperation: 0 for CALL, 1 for DELEGATECALL — any unexpected1here is a critical red flagnonce: must match the current Safe nonce
Operational Security for Multisig Signers
Technical measures without operational discipline fail in production. The Bybit, Ronin, and Harmony hacks were all primarily operational failures.
Signing Process Discipline
Multisigs and governance reduce single points of failure—until process breakdowns turn them into attack surfaces. Many teams end up with “paper decentralization,” where only a few signers are active, reviews are rushed, and transactions get approved without fully understanding the destination, calldata, or downstream impact.
Every multisig signing event should follow a documented checklist:
- Receive transaction proposal through a verified channel — not a DM, not a link from an unverified source.
- Simulate the transaction on-chain using
eth_callor Tenderly before signing. Simulation reveals what state changes will actually occur. - Verify the
toaddress against published contract addresses. Do not rely on ENS for high-value transactions. - Decode the
datafield manually or with a trusted tool. Do not sign calldata you cannot read. - Check the
operationtype. A1(DELEGATECALL) must be explicitly justified. - Independently compute the Safe transaction hash using
safe-tx-hashes-utilor an equivalent tool. - Sign on the hardware wallet, confirming the displayed hash matches the independently computed hash.
- Do not pre-sign transactions to be executed later by other parties.
Infrastructure Separation
Bybit may have lacked proper separation between corporate infrastructure and critical signing infrastructure, potentially allowing a corporate network compromise to impact signing operations. Signing infrastructure should be on a completely isolated network segment. The machine used to compose a transaction proposal must not be the same machine used to sign it.
Monitoring and Alerting
All signing wallets should be monitored for any activity that is not directly related to multisig operations. Any EOA associated with a multisig signer that initiates a non-multisig transaction is an immediate indicator of potential compromise. Set up on-chain monitoring (OpenZeppelin Defender, Tenderly, or custom indexers) to alert on:
- Any
addOwnerorremoveOwnerevent on the Safe - Any
execTransactionwhereoperation == 1 - Any module addition or guard change
- Unexpected ETH or token outflows
Emergency Rotation Procedures
Every team should have a documented, regularly tested key rotation procedure. For multisigs that perform emergency operations like pausing, teams should have processes to regularly conduct drills to ensure operational readiness and signer availability. Before deploying on mainnet, thoroughly practice wallet creation, transaction signing, and owner management on a test network.
How Multisig Compromise Has Occurred in Production
Ronin Network (March 2022) — $625 Million
In March 2022, attackers compromised the Ronin Network bridge by gaining control of five out of nine validator keys. This allowed them to forge and approve unauthorised withdrawals. The forged transactions were submitted directly to Ethereum mainnet, where the bridge’s smart contract verified only that they bore valid signatures from five validators.
The breach went undetected for over a week, largely due to a lack of real-time monitoring on large bridge withdrawals and validator activity. The attack relied on spear-phishing and exploiting leftover privileged RPC access, enabling the attackers to sign as the Axie DAO validator alongside compromised Sky Mavis-controlled nodes.
Root causes: key collocation on shared infrastructure, stale delegate key not revoked, threshold too low relative to the number of keys controlled by one party, no anomaly detection on withdrawals.
Harmony Horizon Bridge (June 2022) — $100 Million
Attackers gained control of the multi-sig wallets used in the Horizon Bridge. The Horizon Bridge had a two-out-of-five multi-sig setup, meaning two out of five keys were needed to validate transactions. The attackers compromised two of these keys.
Harmony Protocol confirmed that the attack was not due to vulnerabilities in their smart contract codes. The breach occurred because private keys were compromised, leading to the exploitation of the Horizon Bridge on the Ethereum network.
A 2-of-5 threshold is dangerously low. Compromising only 40% of the signer set is sufficient. Any threshold below M=⌈N/2⌉+1 for high-value custody warrants serious reconsideration.
Bybit (February 2025) — $1.5 Billion
The Bybit hack was not caused by a flaw in the smart contract code; instead, the attackers exploited human and operational layers. Lazarus manipulated the signing interface during a routine transfer from cold to hot wallet. Although the signing UI appeared legitimate, it had been covertly modified via malicious JavaScript injected into resources served by Safe{Wallet}‘s AWS S3 bucket two days before the attack.
It is probable the attackers targeted a Safe{Wallet} developer by getting them to download a trojanized file, granting unrestricted access to their system. Once inside, the attackers used stolen AWS credentials to access Safe{Wallet}‘s cloud storage, which hosted the JavaScript for Bybit’s wallet management application. They then injected malicious JavaScript designed to alter transaction requests only when Bybit’s wallet was accessed.
The most critical issue to the hack was blind-signing, a longstanding problem in the ecosystem. Cold wallets often have poor UX for reviewing transactions, making it easy for signers to approve malicious payloads during routine operations without validating what they’re actually signing.
The central lesson: this was not due to smart contract flaws or coding errors but rather a sophisticated operational security failure. This attack follows a disturbing pattern observed across WazirX ($230M) and Radiant Capital ($50M). In each case, attackers targeted human and operational elements rather than exploiting code vulnerabilities.
Multisig Security Checklist
Use this checklist during initial deployment, after any signer set change, and as part of a recurring quarterly security review.
Architecture & On-Chain Configuration
- Threshold is M-of-N where M ≥ ⌈N/2⌉ + 1 for high-value treasuries
- No N-of-N configurations — one lost key must not freeze the Safe permanently
- No single entity controls enough keys to meet threshold alone
- A
NoDelegatecallGuardis deployed and active unless DELEGATECALL usage is explicitly audited and justified - All enabled modules have been audited — enumerate them on-chain and verify each one’s replay protection
- No stale or deprecated modules remain enabled on the Safe
- A timelock contract gates all high-impact governance actions (upgrades, parameter changes, owner changes)
- Timelock delay is commensurate with protocol TVL — minimum 48h, ideally 72h for major upgrades
- Guardian/pause functionality is time-bounded — pauses auto-expire and cannot be extended without multisig approval
- The Safe proxy
masterCopyaddress is pinned to the audited singleton; any deviation from the expected address is an immediate incident