On February 21, 2025, Bybit’s engineers watched roughly $1.5 billion in ether leave a wallet that three separate people had approved. Every signer used a hardware wallet. Every signer followed the checklist. The multisig contract itself never broke. What broke was the screen those signers were looking at when they clicked “confirm,” according to a statement the Safe Ecosystem Foundation published on February 28, 2025. That single fact reframes what “secure multisig” actually means, and it’s the reason this tutorial exists.
By September 2026, Safe (the wallet standard formerly branded Gnosis Safe) secures more assets than any other non-liquidity protocol in DeFi. Safe Foundation’s Q1 2026 report put total assets secured at roughly $35.25 billion, spread across more than 61 million deployed accounts, and Safe’s own materials cite a broader lifetime figure north of $60 billion moving through the ecosystem. Uniswap, ENS, Optimism, and Lido all park treasury funds in Safe accounts. If you run a DAO, a protocol treasury, a company crypto account, or even a family fund big enough to worry about, you’re going to end up on Safe or something like it. This guide walks through building that setup correctly, using the Bybit incident, plus 2026 follow-on failures at Drift Protocol and Humanity Protocol, as the negative example to design against.
We’ll deploy a real Safe on a public testnet, verify it independently of the web app, wire up transaction simulation, add a timelock guard for large transfers, and build a small Python monitor that watches your Safe’s pending transactions from the command line. None of it requires trusting a single browser tab.
What Actually Happened to Bybit’s Safe Multisig
Before writing a single line of setup instructions, it’s worth being precise about the Bybit case, because most write-ups after the fact blur the details in ways that lead people to fix the wrong thing.
Bybit used a Safe-based multisig cold wallet to move roughly 401,000 ETH (plus staked derivatives) from cold storage to a warm wallet, a routine operation the exchange had performed many times before. According to forensic reconstructions from Checkpoint Research and incident responder Sygnia, attackers, later attributed to North Korea’s Lazarus Group (specifically a subcluster known as TraderTraitor), had compromised a developer machine inside the Safe{Wallet} organization. From there they hijacked AWS session tokens and swapped a JavaScript bundle served from Safe’s own frontend (app.safe.global) hosted on S3/CloudFront.
That malicious script didn’t touch the Safe smart contract at all. Instead, when it detected it was loaded on a page tied to Bybit’s cold wallet address, it silently rewrote the pending transaction. What the signers saw on their screens was a routine ETH transfer. What they were actually approving was a delegatecall that upgraded the Safe’s implementation contract to one containing backdoor functions, described in incident reports as sweepETH and sweepERC20. The signers used Ledger hardware wallets, which is exactly the control this tutorial will tell you to use. It didn’t matter, because a hardware wallet can only show you what the software feeding it chooses to display. Ledger’s own postmortem calls this “blind signing,” and it’s the crux of the whole problem: the interface, not the cryptography, was the attack surface.
Once the malicious upgrade was approved, attackers drained close to 401,000 ETH equivalent to 51 separate addresses in minutes. Safe’s own statement was blunt about where the fault sat: the incident stemmed from a compromised developer machine and a disguised transaction, not a flaw in the Safe multisig contract logic itself.
Bybit wasn’t an isolated case. CleanSky’s 2026 security analysis tracked roughly $1.8 billion in combined losses across Bybit, Drift Protocol, and Humanity Protocol between February 2025 and June 2026, every one of them running multisig. Drift lost an estimated $285 million in April 2026 after attackers abused pre-signed durable-nonce transactions with no expiration policy, effectively replaying stale signed approvals. Humanity Protocol lost $36.4 million in June 2026 when the keys controlling its multisig were concentrated on a single compromised laptop. None of these were smart-contract bugs. All of them were operational security failures that a properly configured multisig, with the right guardrails, would have caught.
| Incident | Date | Loss | Root cause |
|---|---|---|---|
| Bybit cold wallet | Feb 21, 2025 | ~$1.4B-$1.5B | Compromised Safe{Wallet} frontend, blind-signed contract upgrade |
| Drift Protocol | April 2026 | ~$285M | Replayed pre-signed durable-nonce transaction, no expiration policy |
| Humanity Protocol | June 2026 | $36.4M | Multisig keys concentrated on one compromised laptop |
| Step Finance | February 2026 | ~$27M-$30M | Treasury key compromise |
| Individual Trezor user | January 2026 | ~$282M | Social engineering, seed phrase shared with fake support |
CertiK’s Hack3D report tallied $1.315 billion stolen across 344 on-chain incidents in the first half of 2026 alone, with wallet compromise the single costliest category at over $444 million, averaging more than $13 million per incident. That’s the highest average loss of any attack category CertiK tracks. A CoinDesk analysis of the first half of 2025 reached a similar conclusion from a different data set: roughly $3.1 billion was lost to Web3 hacks in that window, and the largest of those incidents specifically involved Safe multisig wallets where operational security and frontend integrity failed rather than the underlying contract code. The pattern across all of it: the multisig math was sound, the human and interface layer around it wasn’t.
That’s worth sitting with for a moment, because it cuts against the intuition most teams have when they adopt multisig in the first place. The whole pitch of an M-of-N wallet is that no single point of failure can drain the treasury. That’s true at the smart-contract layer. It stops being true the moment every signer is looking at the same compromised rendering of a transaction, or the moment those signers’ keys all trace back to one physical laptop. A multisig is only as distributed as its weakest shared dependency, and for most teams that weak dependency turns out to be the software stack everyone uses to interact with the contract, not the contract itself.
Prerequisites: What You Need Before Starting
This build uses free and open-source tooling throughout. Here’s the exact stack, with versions current as of September 2026:
- A modern browser (Chrome 128+, Firefox 130+, or Brave) with no unnecessary extensions installed — extension-based wallet phishing is a real vector, so run a clean profile for treasury work
- Node.js v20 LTS or newer, and npm 10+
- 2-3 hardware wallets from at least two different vendors (mixing brands means a single vendor-side firmware bug doesn’t take down every signer at once) — Ledger Nano X/S Plus or Trezor Safe 5/7 both work
- Safe’s Protocol Kit SDK, published as @safe-global/protocol-kit (latest major is 4.x as of 2026)
- The ethers library, v6.x
- Python 3.11+ with the requests library, for the monitoring script in Step 10
- Testnet ETH on Sepolia — get it from a public faucet before starting, you’ll need it for two or three test transactions
- An RPC endpoint (Infura, Alchemy, or a public one) for Sepolia and, later, mainnet
- Safe’s core smart contracts, currently tagged v1.5.0 in the safe-global/safe-smart-account GitHub repository (v1.4.1 remains in wide production use and is still fully supported)
Budget 90-120 minutes for the full walkthrough if you’re doing it for the first time, including waiting on testnet confirmations. Do not skip the testnet phase to save time — that’s precisely the corner-cutting that turns a treasury setup into next year’s incident report.
Step 1: Decide Your Owner Set and Signature Threshold
Before touching any software, decide who the signers are and how many signatures you’ll require out of the total (an “M-of-N” configuration). This decision drives everything downstream, and it’s harder to change later than people expect — changing owners or threshold on a live Safe is itself a transaction that needs to go through the same signing process.
A few concrete guidelines drawn from how large DAOs structure their treasuries:
- 3-of-5 is the most common configuration for active operating treasuries — it survives one signer being unavailable or compromised without blocking operations
- 2-of-3 is the floor for anything holding real value; 1-of-N setups are not multisig in any meaningful sense
- For very large treasuries, some protocols split funds across multiple Safes with different thresholds — a “hot” operating Safe at 2-of-4 for day-to-day spend, and a “cold” Safe at 4-of-7 with a mandatory timelock for anything above a set dollar amount
- Never let one person control two of the signing keys. Humanity Protocol’s $36.4 million loss happened specifically because keys ended up concentrated on a single machine — the threshold number meant nothing once the effective control was one point of failure
Write this decision down before you deploy anything. Owner addresses and threshold are set at deployment and changing them is a governance action in itself.
Step 2: Set Up Independent Hardware Wallets for Every Signer
Each signer needs their own hardware wallet, initialized independently, with a seed phrase that never touches a computer, phone camera, or password manager. This part is standard hardware wallet hygiene — if you haven’t done it before, our hardware wallet security walkthrough covers device initialization and seed backup in detail, so this section focuses only on what’s specific to multisig signing.
The specific requirement for Safe signing: use a hardware wallet and firmware version that supports “clear signing” — meaning the device itself decodes and displays the actual contract call being approved, not just a truncated hex blob. Ledger’s post-Bybit guidance is explicit that this is the baseline expectation now, not a nice-to-have. If your device shows you raw calldata instead of a decoded function name and parameters, treat every transaction as unverifiable until you cross-check it through a second channel (Step 6 covers exactly that).
Mix vendors across your signer set. If three of five signers use the same hardware wallet brand and that vendor ships a firmware bug or suffers a supply-chain compromise, you’ve effectively reduced your threshold. Two vendors, ideally three, spreads that risk.
Step 3: Deploy Your Safe on a Testnet First
Go to app.safe.global, connect one signer’s wallet, and switch the network to Sepolia before creating anything. Click “Create new Safe Account,” enter your owner addresses (one per signer, from Step 1), and set your threshold. Review the deployment transaction carefully — this is good practice for the muscle memory you’ll need on mainnet.
You can also deploy programmatically with the Protocol Kit, which is worth doing at least once so you understand what the UI is doing under the hood:
// deploy-safe.js
import Safe, { SafeFactory } from '@safe-global/protocol-kit'
import { ethers } from 'ethers'
const RPC_URL = process.env.SEPOLIA_RPC_URL
const DEPLOYER_KEY = process.env.DEPLOYER_PRIVATE_KEY // testnet key only
async function deploySafe() {
const provider = new ethers.JsonRpcProvider(RPC_URL)
const safeFactory = await SafeFactory.init({
provider: RPC_URL,
signer: DEPLOYER_KEY,
})
const owners = [
'0xOwnerAddress1...',
'0xOwnerAddress2...',
'0xOwnerAddress3...',
]
const threshold = 2 // 2-of-3 for this example
const safeAccountConfig = { owners, threshold }
const protocolKit = await safeFactory.deploySafe({ safeAccountConfig })
const safeAddress = await protocolKit.getAddress()
console.log('Safe deployed at:', safeAddress)
return safeAddress
}
deploySafe().catch(console.error)
Run it with node deploy-safe.js after setting SEPOLIA_RPC_URL and a funded testnet DEPLOYER_PRIVATE_KEY in your environment. Expected output:
Safe deployed at: 0x8f3B2a1C4d5E6f7890AbCdEf1234567890AbCdEf
Save that address. It’s deterministic based on your owners, threshold, and a salt nonce, which means you can predict a Safe’s address before deploying it — useful for pre-announcing a treasury address without funding it yet.
Step 4: Verify the Deployment Independently of the Web App
This is the step Bybit’s team effectively skipped, not out of carelessness but because nobody expected the web app itself to lie. Never trust a single frontend’s rendering of your Safe’s state for anything consequential. Query the contract directly.
// verify-safe.js
import { ethers } from 'ethers'
const SAFE_ABI = [
'function getOwners() view returns (address[])',
'function getThreshold() view returns (uint256)',
'function VERSION() view returns (string)',
]
async function verifySafe(safeAddress, rpcUrl) {
const provider = new ethers.JsonRpcProvider(rpcUrl)
const safe = new ethers.Contract(safeAddress, SAFE_ABI, provider)
const owners = await safe.getOwners()
const threshold = await safe.getThreshold()
const version = await safe.VERSION()
console.log('Owners:', owners)
console.log('Threshold:', threshold.toString())
console.log('Contract version:', version)
}
verifySafe('0x8f3B2a1C4d5E6f7890AbCdEf1234567890AbCdEf', process.env.SEPOLIA_RPC_URL)
.catch(console.error)
Expected output:
Owners: [
'0xOwnerAddress1...',
'0xOwnerAddress2...',
'0xOwnerAddress3...'
]
Threshold: 2
Contract version: 1.4.1
Compare this output against what the web UI shows you. If they ever disagree, stop everything and treat the browser session as compromised until proven otherwise. This two-second script is the single highest-leverage habit in this entire guide.
Step 5: Fund the Safe and Run a Small Test Transaction
Send a small amount of Sepolia ETH to your Safe address, then propose a transaction sending a fraction of it back out. Have each required signer approve it through their own hardware wallet, on their own machine, ideally on different networks (not all signers on the same office Wi-Fi). This tests the full signing flow end to end before you ever touch mainnet funds.
Watch what your hardware wallet screen actually displays during this step. If it shows a decoded “Send 0.01 ETH to 0x…”, that’s clear signing working as intended. If it shows an opaque data blob, note that now — you’ll want a mitigation for that signer before moving real funds.
Step 6: Add Transaction Simulation Before Every Signature
This is the control that would have caught Bybit’s malicious contract upgrade. Blockaid’s post-incident analysis argues the entire failure mode comes down to blind signing, and the fix is simulation: run the proposed transaction against a forked copy of mainnet state before anyone signs, and check what it actually does, not what the UI says it does.
Safe{Wallet}’s app has built-in transaction checks for known risk patterns, but for treasury-grade operations, add an independent simulation step outside the Safe UI itself. A simple version using a local Anvil fork (from Foundry) lets you replay a pending transaction and inspect its actual state changes:
# Fork mainnet locally, then replay the pending Safe transaction against it
anvil --fork-url $MAINNET_RPC_URL --fork-block-number latest &
cast call $SAFE_ADDRESS \
"execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes)" \
$TO $VALUE $DATA $OPERATION $SAFE_TX_GAS $BASE_GAS $GAS_PRICE \
$GAS_TOKEN $REFUND_RECEIVER $SIGNATURES \
--rpc-url http://localhost:8545 --trace
The –trace flag shows every internal call the transaction makes. If a “routine ETH transfer” actually triggers a delegatecall to change the Safe’s implementation contract, this is where you’d see it, in plain text, before anyone signs anything. This is exactly the check that would have surfaced Bybit’s malicious upgrade before signatures were collected.
Step 7: Verify Transaction Hashes Out of Band
Ledger’s guidance after Bybit specifically recommends out-of-band verification: compare the transaction hash your signing device computes against a hash computed independently, through a completely different tool or channel than the one proposing the transaction.
Safe transactions have a well-defined EIP-712 typed hash. Compute it yourself with the SDK, separately from the web app session that proposed the transaction:
// compute-safe-tx-hash.js
import Safe from '@safe-global/protocol-kit'
async function getIndependentHash(safeAddress, txData, rpcUrl, signerKey) {
const protocolKit = await Safe.init({
provider: rpcUrl,
signer: signerKey,
safeAddress,
})
const safeTransaction = await protocolKit.createTransaction({ transactions: [txData] })
const txHash = await protocolKit.getTransactionHash(safeTransaction)
console.log('Independently computed Safe tx hash:', txHash)
return txHash
}
Have one signer run this script on a laptop that never opens the Safe web app, then read the resulting hash out loud on a call while another signer confirms it matches what their hardware wallet displays before signing. It’s low-tech, it takes ninety seconds, and it would have stopped every UI-based spoofing attack in this article.
Step 8: Add a Timelock Guard for High-Value Transactions
Safe supports “Guard” contracts that run additional checks on every transaction before it executes, and “Modules” that extend what the Safe can do (the Zodiac framework, maintained by Gnosis Guild, is the standard toolkit for both). For treasury-grade setups, add a timelock guard that forces a mandatory delay, say 24 to 48 hours, on any transaction above a threshold dollar amount, and blocks contract-upgrade or delegatecall operations from ever executing without that delay.
2026 security commentary on the Drift and Humanity incidents converges on the same recommendation: mandatory timelocks and multi-step governance for large transfers and contract upgrades buy you the window needed for monitoring and simulation to catch an anomaly before funds actually move. Drift’s exploit specifically succeeded because it had a zero-timelock migration path — there was no delay window in which anyone could react.
// Simplified timelock guard check (Solidity, illustrative)
function checkTransaction(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
...
) external override {
if (operation == Enum.Operation.DelegateCall) {
revert("Guard: delegatecall blocked, use timelock module");
}
if (value > highValueThreshold) {
bytes32 txId = keccak256(abi.encode(to, value, data));
require(
queuedAt[txId] != 0 &&
block.timestamp >= queuedAt[txId] + 24 hours,
"Guard: high-value transfer must be queued 24h in advance"
);
}
}
You don’t need to write this from scratch. Zodiac’s audited module library includes a delay module that implements this pattern; wiring it into your Safe is a governance transaction like any other, executed through the same signing flow you already tested in Step 5.
Step 9: Separate Proposer Roles From Signer Roles
Safe supports delegate addresses that can propose transactions without holding signing authority. Use this to separate “who can suggest a payment” from “who can approve it.” A finance team member can propose a vendor payment through the Safe Transaction Service API without ever holding one of the owner keys, and your actual signers only ever see and approve proposals, never draft them from scratch under time pressure.
curl -X POST "https://safe-transaction-sepolia.safe.global/api/v1/safes/$SAFE_ADDRESS/multisig-transactions/" \
-H "Content-Type: application/json" \
-d '{
"to": "0xVendorAddress...",
"value": "1000000000000000",
"data": "0x",
"operation": 0,
"safeTxGas": "0",
"baseGas": "0",
"gasPrice": "0",
"gasToken": "0x0000000000000000000000000000000000000000",
"refundReceiver": "0x0000000000000000000000000000000000000000",
"nonce": "5",
"contractTransactionHash": "0x...",
"sender": "0xDelegateAddress...",
"signature": "0x..."
}'
This narrows the blast radius of a single compromised laptop from “attacker can move funds” to “attacker can suggest a transaction that still needs threshold signatures from hardware devices the attacker doesn’t control.”
Step 10: Build a Pending-Transaction Monitor
Safe’s Transaction Service exposes a public API for every proposed and executed transaction on any Safe. A short Python script polling this endpoint gives you an independent alert channel that doesn’t depend on anyone remembering to check the web app:
# safe_monitor.py
import time
import requests
SAFE_ADDRESS = "0x8f3B2a1C4d5E6f7890AbCdEf1234567890AbCdEf"
API_BASE = "https://safe-transaction-sepolia.safe.global/api/v1"
POLL_SECONDS = 60
seen_hashes = set()
def check_pending():
url = f"{API_BASE}/safes/{SAFE_ADDRESS}/multisig-transactions/?executed=false"
resp = requests.get(url, timeout=10)
resp.raise_for_status()
results = resp.json().get("results", [])
for tx in results:
tx_hash = tx["safeTxHash"]
if tx_hash not in seen_hashes:
seen_hashes.add(tx_hash)
print(f"[ALERT] New pending transaction: {tx_hash}")
print(f" To: {tx['to']} Value: {tx['value']} Operation: {tx['operation']}")
if tx["operation"] == 1:
print(" WARNING: this is a delegatecall - verify manually before signing")
if __name__ == "__main__":
while True:
check_pending()
time.sleep(POLL_SECONDS)
Run it with python3 safe_monitor.py. Expected output when a new proposal lands:
[ALERT] New pending transaction: 0x4a2f...e91c
To: 0x1234...5678 Value: 500000000000000000 Operation: 0
Wire this into a Slack or Telegram webhook for a real deployment, and flag Operation: 1 (delegatecall) transactions specifically, since that’s the exact operation type the Bybit exploit used to swap the Safe’s implementation contract.
Step 11: Write an Operational Security Policy
Tooling doesn’t substitute for a written policy that survives staff turnover. At minimum, document:
- Which hardware wallet brand and model each signer uses, and confirmation that no two signers share a device or backup location
- Geographic distribution requirements for key custody — no two signing devices stored in the same building
- A hard rule against pre-signing transactions with no expiration, the exact failure mode that cost Drift Protocol $285 million
- A mandatory two-channel verification step (Step 7) for any transaction above a set dollar threshold
- An escalation path if any signer’s hardware wallet shows undecoded calldata instead of a clear function summary
Review this policy every time you onboard or offboard a signer, not just annually.
Step 12: Run an Incident-Response Tabletop Drill
Before going live on mainnet, run a dry-run drill: simulate a signer reporting a suspicious transaction and walk through your actual response, including who has authority to pause operations, how you’d verify whether the Safe app itself has been compromised (repeat Step 4’s independent verification script), and how you’d communicate with the rest of the team without using potentially compromised channels. Bybit’s response was fast and well-organized once the theft was detected, and postmortems from Sygnia credit that speed with limiting follow-on damage, but the drill should happen before an incident, not during your first real one.
Step 13: Migrate to Mainnet
Once every step above has been tested on Sepolia and every signer is comfortable with the flow, repeat Step 3 on mainnet with real owner addresses. Fund the Safe with a small amount first, run one full sign-and-execute cycle, verify independently (Step 4), and only then move the bulk of your treasury. Never fund a freshly deployed mainnet Safe with your full treasury balance in the same transaction you use to test the flow.
Common Pitfalls
- Trusting a single browser session for verification. Bybit’s signers had no reason to distrust app.safe.global — it was the legitimate, official frontend, just compromised at the JavaScript delivery layer. Always verify state independently, per Step 4.
- Signing transactions with undecoded calldata. If your hardware wallet shows a hex blob instead of a decoded function call, that’s not a minor inconvenience — it’s the exact condition that let the Bybit attack through.
- Leaving pre-signed transactions with no expiration. Drift Protocol’s $285 million loss traces directly to durable-nonce transactions that remained valid indefinitely. Set expiration windows on anything pre-signed.
- Concentrating keys for convenience. Humanity Protocol’s multisig had the right threshold number on paper, but effective control sat on one laptop. A threshold is meaningless if the keys aren’t genuinely independent.
- Skipping the testnet phase. Every mistake in owner configuration, threshold logic, or guard wiring is far cheaper to make on Sepolia than on mainnet with real funds already deposited.
- Same hardware wallet vendor for every signer. A single vendor-side firmware flaw or supply-chain compromise then threatens your entire signer set at once.
- No timelock on contract upgrades. Contract implementation changes should never execute instantly, regardless of how many signatures approve them.
Troubleshooting
- “Transaction hash mismatch” between your script and the Safe UI. Recompute using Step 7’s script with the exact same chain ID, nonce, and calldata. A mismatch here means stop immediately and don’t sign — this is precisely the anomaly detection this setup exists to catch.
- Safe deployment transaction reverts. Usually an RPC issue or insufficient testnet ETH for gas. Confirm your deployer address has at least 0.05 Sepolia ETH before retrying.
- Hardware wallet shows a “blind signing” warning and asks you to enable it. Do not enable blind signing as a workaround. Update your device’s firmware and the companion app first; most modern Ledger and Trezor firmware supports clear signing for standard Safe operations without needing that override.
- Protocol Kit throws “invalid owner address” during deployment. Check for checksummed vs. lowercase address mismatches — ethers.getAddress() will normalize this for you before passing addresses into safeAccountConfig.
- Safe Transaction Service API returns 404 for your Safe. The indexer needs a minute or two after deployment to pick up a new Safe. Wait and retry, or query the contract directly via Step 4’s script in the meantime.
- Signers see different threshold or owner counts when checking independently. Treat this as an active compromise indicator. Freeze all pending transactions and investigate before any further signing.
- Anvil fork in Step 6 fails with “block not found.” Your RPC provider may be pruning old state; use –fork-block-number latest rather than pinning to a specific historical block unless you have an archive node endpoint.
- Zodiac delay module blocks a transaction you expected to execute immediately. That’s the module working as designed. If the transaction is genuinely urgent and legitimate, your policy needs an emergency override path defined in advance (Step 11), not an ad hoc bypass in the moment.
- Python monitor script misses a transaction. Check your polling interval against the Transaction Service’s rate limits; back off to 60-second polling if you’re hitting 429 responses on the public endpoint.
Safe vs. Other Treasury Security Models
Safe isn’t the only way to secure a crypto treasury, and it’s worth understanding where it sits relative to alternatives before committing to it.
| Model | How it works | Strength | Weakness |
|---|---|---|---|
| Safe smart-contract multisig | On-chain contract requires M-of-N signatures for any transaction | Auditable on-chain, composable with guards/modules, chain-native | Frontend/UI is a separate trust layer from the contract itself |
| Bitcoin script multisig (e.g. via Sparrow) | Native Bitcoin script requires M-of-N key signatures | No smart-contract attack surface, mature and simple | Bitcoin-only, less programmable for treasury automation |
| MPC custodial wallet | Key shares split across parties, threshold cryptography reconstructs signature off-chain | No single on-chain multisig contract to target | Trust concentrated in the MPC provider’s infrastructure and key-share security |
| Single EOA (one private key) | One key signs everything | Simple, fast | Single point of failure; not appropriate for any treasury above trivial amounts |
If you’re securing Bitcoin specifically, our Sparrow multisig wallet guide covers that native-script approach in depth. Safe is the dominant standard for EVM chains, supporting deployment across 300+ networks according to Safe’s smart-contract deployment documentation, which is why it’s the default choice for DAO and protocol treasuries specifically.
Advanced Tips
Once the base setup is running, a few refinements are worth the extra effort for larger treasuries:
- Split treasury tiers. Run a low-threshold “operating” Safe for routine spend and a high-threshold “reserve” Safe with a mandatory timelock for anything above a defined percentage of total holdings, mirroring how large protocol treasuries structure their funds.
- Multi-chain Safe deployment. Safe supports deterministic deployment across 28+ networks in its consumer product, so the same owner set and threshold can control funds on multiple chains with a predictable, pre-computed address on each.
- Session-based delegate keys for automation. If you need automated payouts (contributor payroll, recurring grants), use time-boxed delegate keys with hard spending caps rather than giving any automated system owner-level signing power.
- Independent monitoring redundancy. Run the Step 10 monitor script from at least two separate machines on separate networks, so a single compromised endpoint can’t suppress your alerting.
- Formal Zodiac module audits. Any custom Guard or Module you add expands your attack surface. Use only audited modules from Zodiac’s library, or commission an independent audit before deploying custom guard logic to a mainnet Safe holding real funds.
Your Complete Working Project
By the end of this tutorial you should have a small project directory that looks like this:
safe-treasury-setup/
├── deploy-safe.js # Step 3: programmatic Safe deployment
├── verify-safe.js # Step 4: independent on-chain verification
├── compute-safe-tx-hash.js # Step 7: out-of-band hash verification
├── safe_monitor.py # Step 10: pending-transaction alerting
├── policy.md # Step 11: written operational security policy
├── .env.example
└── package.json
Together these give you a Safe that’s been tested on a public testnet, verified independently of any single web frontend, protected by a timelock guard against instant high-value transfers or contract upgrades, and monitored continuously outside the official web app. That’s a materially different security posture from what Bybit had in February 2025, not because the underlying Safe contracts changed, but because the workflow around them now assumes the interface can lie.
For broader context on how attackers approach crypto wallets generally, our wallet security fundamentals guide and wallet monitoring walkthrough pair well with this setup, and if you’re deploying custom Guard contracts, run them through the same process in our smart contract audit tutorial before they touch mainnet funds.
Frequently Asked Questions
Is Safe{Wallet} the same thing as Gnosis Safe?
Yes. Gnosis Safe rebranded to Safe (marketed as Safe{Wallet} for the consumer app and Safe{Core} for the SDK) as the project spun out from Gnosis into the independent Safe Ecosystem Foundation. The underlying smart-contract standard is the same lineage; v1.4.1 remains in wide production use while v1.5.0 is the current tagged release on GitHub.
Did the Bybit hack mean Safe’s smart contracts are unsafe?
No. Safe’s own statement, and independent forensic analysis from firms including Sygnia and Checkpoint Research, concluded the exploit came from a compromised developer machine and a manipulated frontend, not a bug in the Safe multisig contract logic. That distinction matters because it means the fix is procedural (independent verification, simulation, clear signing) rather than a contract patch.
How much does it cost to deploy a Safe?
Deployment itself only costs network gas — there’s no protocol fee from Safe to create an account. On Ethereum mainnet, expect deployment gas costs comparable to any moderately complex contract deployment; on lower-fee EVM chains it’s typically a few dollars or less.
What’s the minimum threshold I should use for a real treasury?
2-of-3 is a reasonable floor for anything holding meaningful value. Most active DAO and protocol treasuries settle on 3-of-5, which tolerates one unavailable or compromised signer without halting operations while still requiring a majority to act.
Can I use the same hardware wallet for multiple Safe accounts?
Yes, a single hardware wallet can hold multiple addresses and sign for multiple Safes. Just make sure you’re not using the same address as a signer across Safes in a way that undermines the independence assumption behind your threshold — a compromised device would then affect every Safe it signs for.
What is “blind signing” and why does it matter here?
Blind signing means approving a transaction without your device decoding and displaying what it actually does, typically because the calldata is complex or the wallet firmware can’t parse it. Bybit’s signers approved a disguised contract upgrade because the interface presented it as a routine transfer. Clear signing, where the device itself decodes the call, plus independent verification per Step 4 and Step 7 of this guide, is the direct countermeasure.
Do I need a timelock guard for a small operating treasury?
For genuinely small, low-value operating funds, a full timelock module may be overkill and could slow down legitimate operations more than it’s worth. The tradeoff shifts quickly once a treasury holds enough value that a 24-hour delay is a small cost against the downside of an instant, irreversible large transfer.
How do I know if my current multisig setup is vulnerable to a Bybit-style attack?
Run the independent verification script from Step 4 right now against your existing Safe and compare the output to what your web app shows. Then check whether any signer has ever approved a transaction where their hardware wallet displayed undecoded calldata instead of a clear function summary. If the answer is yes, treat that as an open finding and work through Steps 6 through 8 of this guide before your next high-value transaction.




