The Bybit hack of February 21, 2025, which resulted in the theft of 401,347 ETH worth approximately $1.46 billion, has fundamentally changed how security professionals approach transaction verification in cryptocurrency. The attack did not exploit a vulnerability in the Ethereum protocol or in Bybit’s wallet cryptography. Instead, it manipulated the Safe{Wallet} signing interface through a compromised AWS S3 bucket, presenting legitimate-looking transaction details while executing a malicious smart contract replacement. With Bitcoin at $88,643 and Ethereum at $2,494, the financial stakes of transaction verification failures continue to escalate. This advanced tutorial provides a technical walkthrough for independently verifying smart contract transactions before signing, using tools and techniques that would have caught the Bybit attack.
The Objective
This tutorial will teach you how to decode and verify the actual on-chain operations of a smart contract transaction independently of any signing interface. By the end, you will be able to identify transaction manipulation attempts like those used in the Bybit hack, the WazirX breach, and the Radiant Capital exploit. The techniques covered are applicable to Ethereum and EVM-compatible chains, which represent the vast majority of high-value DeFi and exchange operations.
The core problem we are solving is the trust gap between what a signing interface displays and what the underlying transaction actually does. When you sign a transaction through any interface — whether Safe{Wallet}, MetaMask, or a hardware wallet — you are trusting that interface to accurately represent the transaction data. The Bybit hack demonstrated that this trust can be catastrophically misplaced.
Prerequisites
To follow this tutorial, you will need the following tools and knowledge:
Technical prerequisites: Basic familiarity with Ethereum transactions, smart contract ABIs, and hexadecimal encoding. Access to an Ethereum RPC endpoint (Alchemy, Infura, or a self-hosted node). Python 3 with web3.py installed, or equivalent tooling.
Tool prerequisites: A transaction simulation service such as Tenderly, Blockaid, or Foundry’s Simulate API. A calldata decoder — either the web3.js/web3.py decodeTransaction functions or an online tool like Samczsun’s Transaction Decoder. Access to Etherscan’s verified contract source code for any contracts you interact with.
Security prerequisites: A clean, isolated environment for transaction analysis. Never use your primary signing device for analysis and signing simultaneously — the Bybit hack worked precisely because the compromised interface was on the same system used for signing.
Step-by-Step Walkthrough
Step 1: Extract the raw transaction calldata.
Before signing any transaction through an interface, extract the raw calldata that the interface is asking you to sign. In MetaMask, this is visible by clicking “Hex Data” in the transaction confirmation window. For Safe{Wallet} transactions, the raw calldata is accessible through the transaction details panel before any signatures are collected.
The calldata is a hexadecimal string that encodes the function being called and its parameters. For example, a simple ETH transfer contains only the recipient and value, but a complex DeFi interaction might contain nested function calls affecting multiple contracts.
Step 2: Decode the function selector.
The first four bytes of calldata represent the function selector, a keccak256 hash of the function signature. Use a selector database like 4byte.directory or Samczsun’s selector lookup to identify which function is being called. In the Bybit attack, the function selector would have revealed that the transaction was calling a contract replacement function rather than a simple transfer.
Step 3: Decode all parameters.
Using the contract’s ABI (available from Etherscan for verified contracts), decode all parameters passed to the function. Pay particular attention to address parameters and value parameters. In the Bybit attack, the decoded parameters would have shown an unexpected implementation contract address being set as the new wallet controller.
Step 4: Simulate the transaction.
Use a transaction simulation tool to execute the transaction in a sandboxed environment. The simulation reveals the exact state changes the transaction will produce, including token transfers, contract modifications, and approval changes. Blockaid’s transaction simulation would have immediately flagged the Bybit attack by showing that the wallet’s implementation contract was being replaced.
Using Foundry’s simulation capabilities:
“`cast run –rpc-url YOUR_RPC_URL –trace TRANSACTION_HASH“`
This provides a full trace of every internal transaction and state change, making any malicious operations immediately visible.
Step 5: Verify the contract addresses.
Cross-reference every contract address in the transaction against known, verified addresses. The Bybit attack involved redirecting wallet control to an attacker-controlled contract. Maintaining a local registry of verified contract addresses for the protocols you interact with provides an independent verification layer that cannot be compromised by interface manipulation.
Step 6: Implement programmatic pre-sign checks.
For institutional or high-volume operations, implement automated pre-sign checks that verify transactions against a whitelist of expected behaviors. These checks should run independently of the signing interface and should include function selector validation, parameter range checks, and counterparty address verification.
Troubleshooting
Problem: Calldata appears to be valid but simulation shows unexpected behavior.
This typically indicates a complex interaction where the immediate function call triggers secondary operations through delegate calls or internal transactions. Trace through the full execution path using Tenderly’s transaction debugger or Foundry’s trace functionality to identify where the unexpected behavior originates.
Problem: Contract is not verified on Etherscan.
Unverified contracts are inherently suspicious in legitimate DeFi interactions. If a protocol is asking you to interact with an unverified contract, treat it as a potential red flag. At minimum, compare the contract’s bytecode against known good versions using a diff tool.
Problem: Simulation returns an error but the interface shows a valid transaction.
This discrepancy is itself a warning sign. It could indicate that the interface is misrepresenting the transaction data, that the simulation environment is misconfigured, or that the transaction is conditionally malicious (for example, it behaves differently depending on the block number or the caller’s address). Investigate thoroughly before proceeding.
Problem: Multi-signature transactions are difficult to simulate before all signatures are collected.
Use the Safe{Wallet} transaction API to retrieve the full transaction object before signing. This can be decoded and simulated independently. The critical step is to perform your verification on a different device than the one you use for signing.
Mastering the Skill
The techniques in this tutorial represent the minimum standard for transaction verification in the post-Bybit era. To build deeper expertise, explore formal verification of smart contracts, which mathematically proves that a contract’s behavior matches its specification. Tools like Certora and Halmos provide formal verification capabilities that can catch vulnerabilities invisible to testing.
Contribute to and maintain shared security databases like the Blockchain Threat Intelligence initiative, which catalogs attack patterns and indicators of compromise. The crypto industry’s collective security improves when defensive intelligence is shared rapidly and broadly.
Finally, practice these techniques regularly with intentionally malicious test transactions. Create a safe testing environment using forked mainnet state and attempt to craft transactions that would fool different verification layers. This adversarial practice builds the intuition needed to spot real attacks when the stakes are high.
Disclaimer: This article is for informational purposes only and does not constitute financial or investment advice. Always conduct your own research before making any investment decisions.
the bybit hack didnt break any cryptography. it just showed that humans will trust a pretty UI over raw calldata every single time. 1.46B for a display layer exploit
the Safe{Wallet} manipulation worked because the UI showed a legitimate transaction while the actual calldata did something completely different. display layer vs execution layer gap
calldata_nerd_ the scary part is even raw calldata decoding wont help if the contract at that address is itself a clone with subtle logic changes. you need full source verification too
This is the technical content we actually need. Decoding calldata independently of the signing UI would have caught the Safe{Wallet} manipulation immediately.
agreed. the irony is these verification tools have existed for years but nobody used them because the UX was good enough. $1.46B says it wasn’t
0xWizard the tools existed but wallet devs prioritized gas estimation over security display. the UX was good enough to be dangerous. 1.46B later and rabby wallet is the only one that learned
Rabby wallet showing simulation before signing is literally the only feature that matters now. every wallet that doesnt do transaction simulation is negligent at this point
The walkthrough for verifying the implementation contract address is solid. Anyone running multisig operations should have this as mandatory reading.
Mandatory reading for sure but let’s be real, most signers won’t do this manually. We need this built into the signing workflow itself.
Anika J. building it into the signing workflow is the only way. expecting users to manually decode calldata is like expecting them to verify ssl certificates
sig_verify_ exactly. ssl cert verification is built into browsers. calldata decoding needs to be built into wallets the same way. no one manually checks certificates
1.46B for a UI spoofing attack and people still blindly sign on metamabox. nothing changed except the headlines got bigger