How to Verify a Safe Transaction Before Signing: 5 Steps
Quick answer. Recompute the domain, message and Safe transaction hashes on a separate machine. Decode the calldata, refuse unexplained delegatecalls (operation 1), check the nonce and gas fields, simulate, then match the hashes on your hardware wallet before signing.
Video transcript
The Safe interface is a web page. At Bybit, it showed signers a routine transfer while they signed away a cold wallet.
Your hardware wallet signs whatever the browser sends. A compromised machine can show one transaction and sign another. So verify outside the Safe interface, on a dedicated signing machine.
There are five checks. Compute the hashes, decode the calldata, check every field, simulate the outcome, then match on your device.
Recompute the domain hash and message hash on a separate machine. Change any field, the nonce included, and the hash changes. SEAL suggests cross-checking two independent tools.
Hashes do not tell you what a transaction does, so decode the calldata yourself. At Bybit, the danger was a delegate call to an unknown contract, while the calldata decoded as a zero token transfer.
Refuse operation one, a delegatecall, unless the target is known and audited. Expect zero value and gas fields, the nonce you expect, and treat owner changes as high risk.
Simulation is a check, not proof. Radiant simulated each transaction in Tenderly and still lost about 50 million dollars.
Finally, match the full hashes on your hardware wallet. A mismatch means stop. As Safe's help center says, if you can't verify it, don't sign it.
Guardian's Managed Security team reviews every multisig transaction before signing, and an OpSec audit tests your signing process. Read the full guide, or talk to Guardian.
The Safe{Wallet} screen is a web page. At Bybit, that page showed signers a routine transfer while they signed away a cold wallet.
Your device signs a hash of ten transaction fields, the chain and the Safe's address. You can check all of them yourself, away from the interface.
This guide walks through five checks. For why devices often show only hashes, see what blind signing is.
Why the Interface Is Not Enough
Your device signs whatever the browser sends it. If the page or your computer is compromised, the page can show one transaction while the device signs another.
At Radiant, compromised machines showed legitimate data in Safe{Wallet} and sent malicious transactions to the hardware wallets, its post-mortem says.
So every check below uses data from outside the Safe interface: your own tools, your own decoding and your device's screen.
Run them on a dedicated machine used only for signing. The safe-tx-hashes-util README suggests an operating system such as Tails or Qubes OS.
Step 1: Compute the Three Hashes
A Safe transaction hash is built in two halves under EIP-712. Safe's own contract code defines both.
| Hash | Built from | Proves |
|---|---|---|
| Domain hash | The chain ID (Safe 1.3.0 and later) and the Safe's address | Which Safe, on which chain |
| Message hash | The ten fields, to through nonce | The exact transaction |
| Safe transaction hash | Both hashes behind a 0x1901 prefix, hashed again | The value owners approve |
Change one field, the nonce included, and the message hash changes. Point at another Safe or chain, and the domain hash changes.
With safe-tx-hashes-util
The open-source script by pcaversaccio computes all three hashes for a queued transaction:
./safe_hashes.sh --network ethereum --address <your Safe> --nonce <nonce>
- Default: it pulls the transaction from Safe's Transaction Service API.
- Interactive: with
--interactiveyou type each field; a blank field takes the API's value. - Nested Safes: add
--nested-safe-addressand--nested-safe-noncefor theapproveHashhashes too. - Needs: Foundry and Bash 4 or later.
Its README says interactive mode, plus your own calldata decoding, removes the need to trust Safe's API. It also warns that the decoded method and parameters "are not verified by this script."
With OpenZeppelin Safe Utils
Safe Utils does the same calculation in the browser, with a manual entry mode. OpenZeppelin calls it a proof of concept with no security assessment, and recommends running it locally.
For critical operations, SEAL suggests cross-checking with both a command-line and a web tool. Two independent tools should give the same result.
Step 2: Decode the Calldata
Computed hashes pin down the transaction you will sign. They do not tell you what it does.
SEAL warns that a compromised Safe API could produce a matching hash for the wrong transaction. So decode the calldata independently:
- Copy the raw data from your wallet extension, not the Safe interface's copy button.
- Decode it with Etherscan's Input Data Decoder or another decoder Safe lists.
- Confirm the function, the target and every parameter.
- Check each address in full against a trusted source.
Batches
A MultiSend batch packs several transactions into one payload. Decode and review each one.
SEAL lists "MultiSend when a single action was expected" as a red flag.
What Bybit's calldata looked like
Decoded alone, Bybit's payload looked harmless: a token transfer of zero to an unfamiliar address.
The danger was in the next field. It was sent as a delegatecall to an unknown contract.
Step 3: Check Every Field
Safe's help center sets out what each field should hold in a normal transaction:
| Field | Expect | Stop if |
|---|---|---|
| operation | 0, a call (batches: 1 to MultiSend) | 1 to any target you cannot verify |
| to | The contract or recipient you intend | An address you cannot verify |
| value | 0 for contract calls | ETH you did not mean to send |
| nonce | The next transaction index | Not the one you expect |
| safeTxGas, baseGas | 0 on Safe 1.3.0 and later | Nonzero, on 1.3.0 or later |
| gasPrice | 0 | Nonzero |
| gasToken, refundReceiver | The zero address | Any other address |
Why operation 1 matters
Operation 1 is a delegatecall. Per SEAL, it gives the target contract full control over the Safe's storage.
That storage includes the implementation address in slot 0. At Bybit, a delegatecall overwrote it.
SEAL says it should appear only when the target is a known, audited module. Safe's help center says to check with a technical person.
Batches normally delegatecall Safe's MultiSend contract. safe-tx-hashes-util keeps a list of trusted targets and warns on any other.
Gas fields, nonce and owners
Gas settings can move value quietly. The script warns that a custom gas token with a refund receiver "can be used to hide a rerouting of funds through gas refunds."
Each nonce executes once. If two queued transactions share a nonce, only one can run, so confirm which one you are signing.
Treat owner and threshold changes as high risk. The script flags them with "Proceed with caution!"
Step 4: Simulate the Outcome
A simulation shows what the transaction will actually do: transfers, events and calls.
- Simulate in Tenderly from a separate device, not only through the Safe interface.
- Read the trace, not just the summary. Check the Contracts and Events tabs.
- Or add
--simulateand an RPC URL to the script, which runscast call --trace.
The script's simulation trusts your RPC provider. Running your own node removes that trust, its README notes.
Simulation is a check, not proof. SEAL warns that "Simulations can be spoofed."
Radiant simulated each transaction in Tenderly and still lost about $50M. Malware on its developers' devices bypassed those checks, Radiant says.
So reconcile the simulated effect with your own decoded calldata, as SEAL advises.
Step 5: Match on the Device
Last, compare what your hardware wallet shows with what you computed in Step 1:
- If it shows hashes: match the domain and message hashes to your tool's output.
- If it shows fields: check each one against the values you verified in Steps 2 and 3.
- Either way: compare the entire hash, not just the first and last few characters.
Sign only when everything matches. Safe's help center puts the rule simply: "If you can't verify it, don't sign it."
A mismatch means stop. Do not sign, and re-check the proposal with the other signers.
How Guardian Helps
With Managed Security, Guardian's security operations team reviews every multisig transaction before it is signed, and every configuration change as it lands.
An OpSec audit tests how your team proposes, checks and signs transactions, and what it takes to slip a malicious payload past them.
For thresholds and signer roles, see multisig best practices. To compare interfaces, see the best multisig wallets for Ethereum.
Frequently Asked Questions
What is a Safe transaction hash?
The value Safe owners approve. Under EIP-712 it combines the domain hash, which binds the chain and the Safe's address, with the message hash of the ten transaction fields. Change any field, the nonce included, and the hash changes.
What does operation 1 mean in a Safe transaction?
A delegatecall. The target contract's code runs on the Safe's own storage, so it can rewrite owners or the implementation address. Safe's help center calls it dangerous. Refuse it unless the target is a known, audited contract.
Can I verify a Safe transaction without the Safe interface?
Yes. The interactive mode of safe-tx-hashes-util, or the manual entry mode of OpenZeppelin Safe Utils, computes the hashes from fields you enter. Decode the calldata separately and compare everything on your hardware wallet.
Does simulation prove a transaction is safe?
No. SEAL warns that simulations can be spoofed, and Radiant simulated its transactions in Tenderly before losing about $50M. Reconcile the simulated effect with your own decoded calldata, on a machine the proposer does not control.
Sources
- Safe Help Center: How to verify Safe{Wallet} transactions on a hardware wallet (read November 2026)
- Safe Help Center: How to perform basic transaction checks on Safe{Wallet} (read November 2026)
- Safe on GitHub: Safe.sol, v1.4.1
- Safe on GitHub: SafeProxy.sol, v1.4.1
- Safe on GitHub: GnosisSafe.sol, v1.2.0 (domain hash without chain ID)
- pcaversaccio on GitHub: safe-tx-hashes-util README (read November 2026)
- OpenZeppelin on GitHub: safe-utils README (read November 2026)
- SEAL Frameworks: Safe Multisig, Step-by-Step Verification (v0.0.2)
- Ethereum: EIP-712, Typed structured data hashing and signing (Final)
- Radiant Capital: Radiant Post-Mortem (October 2024)
- Etherscan: Bybit cold wallet execTransaction, block 21895238 (February 21, 2025)
Put a second check on every signature
Guardian's security operations team reviews every multisig transaction before your signers approve it.