$1,000,000 in security audit grants are live now, Apply here →

← Resource Hub

How to Verify a Safe Transaction Before Signing: 5 Steps

By the Guardian team Updated 6 min read

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.

Watch: How to Verify a Safe Transaction Before Signing: 5 Steps 1:44
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.

HashBuilt fromProves
Domain hashThe chain ID (Safe 1.3.0 and later) and the Safe's addressWhich Safe, on which chain
Message hashThe ten fields, to through nonceThe exact transaction
Safe transaction hashBoth hashes behind a 0x1901 prefix, hashed againThe 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 --interactive you type each field; a blank field takes the API's value.
  • Nested Safes: add --nested-safe-address and --nested-safe-nonce for the approveHash hashes 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:

  1. Copy the raw data from your wallet extension, not the Safe interface's copy button.
  2. Decode it with Etherscan's Input Data Decoder or another decoder Safe lists.
  3. Confirm the function, the target and every parameter.
  4. 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:

FieldExpectStop if
operation0, a call (batches: 1 to MultiSend)1 to any target you cannot verify
toThe contract or recipient you intendAn address you cannot verify
value0 for contract callsETH you did not mean to send
nonceThe next transaction indexNot the one you expect
safeTxGas, baseGas0 on Safe 1.3.0 and laterNonzero, on 1.3.0 or later
gasPrice0Nonzero
gasToken, refundReceiverThe zero addressAny 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 --simulate and an RPC URL to the script, which runs cast 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

Put a second check on every signature

Guardian's security operations team reviews every multisig transaction before your signers approve it.