1/5
## Understanding Safe Multi-Sig Transaction Verification Securely verifying wallet transactions is paramount in the Web3 space, especially when dealing with multi-signature wallets like Safe (formerly Gnosis Safe) that often control significant assets. This lesson walks you through a practical demonstration of how to scrutinize a Safe multi-sig transaction signature request using the Wise Signer platform (`wise-signer.cyfrin.io`), ensuring you understand what you are approving before signing. ## Dissecting the Safe Transaction: The Challenge Scenario We'll be working through a specific challenge (Question 11 from the "Simulated Wallet" on Wise Signer) designed to test your verification skills. **The Task:** You need to decide whether to sign or reject an incoming signature request. **Key Details Provided:** * **Your Wallet Address (Signer):** `0x39fD6e51aad88F6F4ce6a8B827279cffb92266` * **Multi-sig Wallet Address (The Safe):** `0x4087d2046A7435911fC26DCFac1c2Db26957Ab72` * **Safe Version:** `1.4.1` * **Intended Action:** The transaction aims to send `1 WETH` to the address `0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045` on the `Arbitrum network`. * **Simulated Safe UI Presentation:** * **Recipient:** `arb:0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045` * **Amount:** `1 Wrapped Ether (WETH)` * **Nonce:** `29` ## The Signature Process: Unpacking the EIP-712 Message Upon initiating the signing process in the simulated Safe UI (clicking "Next" then "Sign"), your wallet (e.g., Metamask) will present a "Message to sign." This isn't just a random string of characters; for modern Safe transactions, it's typically structured according to the **EIP-712 standard**. EIP-712 is a standard for signing typed structured data. It enhances security and user experience by making signature requests more human-readable compared to signing opaque hashes. The data is presented in a structured way, with clear delineations between the domain (information about the contract and chain) and the message itself (the transaction details). The EIP-712 data presented by Metamask for this scenario looks like this when formatted as JSON: ```json { "types": { "SafeTx": [ { "type": "address", "name": "to" }, { "type": "uint256", "name": "value" }, { "type": "bytes", "name": "data" }, { "type": "uint8", "name": "operation" }, { "type": "uint256", "name": "safeTxGas" }, { "type": "uint256", "name": "baseGas" }, { "type": "uint256", "name": "gasPrice" }, { "type": "address", "name": "gasToken" }, { "type": "address", "name": "refundReceiver" }, { "type": "uint256", "name": "nonce" } ], "EIP712Domain": [ { "type": "uint256", "name": "chainId" }, { "type": "address", "name": "verifyingContract" } ] }, "domain": { "chainId": "42161", "verifyingContract": "0x4087d2046A7435911fC26DCFac1c2Db26957Ab72" }, "primaryType": "SafeTx", "message": { "to": "0x82af49447d8a07e3bd95bd0d56f35241523fbab1", "value": "0", "data": "0xa9059cbb000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000de0b6b3a7640000", "operation": "0", "safeTxGas": "0", "baseGas": "0", "gasPrice": "0", "gasToken": "0x0000000000000000000000000000000000000000", "refundReceiver": "0x0000000000000000000000000000000000000000", "nonce": "29" } } ``` ## Deep Dive: Verifying Each Component of the EIP-712 Payload To ensure the transaction is safe, we must meticulously verify each part of this EIP-712 payload against the intended action. ### 1. Verifying the `domain` Object The `domain` object provides context for the signature, specifying the chain and the contract asking for the signature. * **`chainId: "42161"`:** This field specifies the blockchain network. To verify, you can use a resource like `chainlist.org`. Searching for chain ID `42161` on Chainlist confirms this is "Arbitrum One," matching the intended network. * **`verifyingContract: "0x4087d2046A7435911fC26DCFac1c2Db26957Ab72"`:** This address *must* match the address of the Safe multi-sig wallet whose transaction you are authorizing. Comparing this to the "Multi-sig Wallet Address" (`0x4087d2046A7435911fC26DCFac1c2Db26957Ab72`) from the challenge scenario, we see a perfect match. This is a critical check: it confirms you are signing a message intended for *your* Safe, not a malicious one. ### 2. Verifying the `message` (SafeTx) Object The `message` object contains the actual details of the transaction initiated by the Safe. * **`to: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"`:** This is the target contract address for the transaction. Since the intended action is to send WETH, this address should be the WETH contract on Arbitrum. A quick search for "WETH on Arbitrum" leads to resources like Arbiscan (Arbitrum's block explorer). The Arbiscan page for `0x82af49447d8a07e3bd95bd0d56f35241523fbab1` (`arbiscan.io/token/0x82af49447d8a07e3bd95bd0d56f35241523fbab1`) confirms this is indeed the Wrapped Ether (WETH) token contract on Arbitrum. This matches our expectation. * **`value: "0"`:** This field indicates the amount of native currency (ETH on Arbitrum) being sent directly from the Safe wallet in this call. Since this is an ERC20 token transfer (WETH), the value transfer is handled within the `data` field, not as a direct native currency transfer from the Safe itself. Therefore, a `value` of `0` is correct and expected. * **`data: "0xa9059cbb000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000de0b6b3a7640000"`:** This hexadecimal string is the encoded call data. It specifies the function to be called on the `to` contract and the arguments for that function. This is the most crucial part to decode to understand the transaction's true intent. You can use a call data decoder tool, such as the "Swiss Knife Decoder" (`calldata.swiss-knife.xyz/decoder`, accessible via `wise-signer.cyfrin.io/tools/`). Pasting the `data` value into the decoder reveals: * **Function:** `transfer(address _to, uint256 _value)` – This is the standard ERC20 function for transferring tokens. * **Parameter 1 (address `_to`):** The decoder might show an ENS name like `vitalik.eth`, which resolves to `0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045`. This matches the intended recipient address from the challenge scenario. * **Parameter 2 (uint256 `_value`):** `1000000000000000000`. To confirm this amount represents `1 WETH`, we need to know the token's decimals. WETH, like ETH, typically has 18 decimals. Therefore, `1` followed by 18 zeros (`10^18` Wei) equals 1 full WETH token. This matches the intended amount. You can verify the decimals by checking the WETH contract on Arbiscan: navigate to the "Contract" tab, select "Read as Proxy," and find the `decimals` function, which should return `18`. * **`operation: "0"`:** This field specifies the type of call. `0` indicates a standard `CALL` operation. If this were `1`, it would signify a `DELEGATECALL`, which is a more powerful and potentially riskier operation that executes code from another contract in the context of the Safe. For a simple token transfer, `0` is expected and correct. * **Gas-related fields (`safeTxGas`, `baseGas`, `gasPrice`, `gasToken`, `refundReceiver`):** In this transaction, these are all `0` or the zero address (`0x00...00`). This is acceptable. These fields relate to how gas is handled for the transaction execution, often allowing for gas sponsoring or payment in tokens. For many standard Safe transactions, especially when the actual execution gas will be paid by the externally owned account (EOA) that submits the fully signed transaction, these being zero is fine. * **`nonce: "29"`:** This is the transaction nonce for the Safe wallet. It must match the current nonce of the Safe to prevent replay attacks and ensure transactions are executed in order. This value (`29`) matches the nonce displayed in the simulated Safe UI (`Nonce # 29`). ## Confirming the Transaction: Verification Results and Insights After thoroughly verifying all fields in the EIP-712 payload, we find that every piece of information aligns with the intended transaction as described in the challenge: sending 1 WETH to `0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045` on Arbitrum from Safe `0x4087d2046A7435911fC26DCFac1c2Db26957Ab72` with nonce `29`. At this point, it is safe to sign the transaction in Metamask. The Wise Signer platform would then confirm the correctness of your decision, typically highlighting: 1. The transaction originates from a legitimate source (a simulation of `app.safe.global`). 2. The `verifyingContract` correctly matches the multi-sig wallet address. 3. The `chainId` correctly specifies the Arbitrum network. 4. The transaction `data`, when decoded, accurately reflects the intent to send 1 WETH to the specified recipient. 5. There are no suspicious parameters, such as `operation: 1` (DELEGATECALL) where a simple `CALL` is expected, or unexpected `gasToken` or `refundReceiver` addresses. ## Key Web3 Concepts for Robust Signature Verification Understanding the following concepts is crucial for securely verifying transactions: * **Safe Multi-sig:** A smart contract wallet that requires a predefined number of owner signatures to approve and execute a transaction, enhancing security for pooled funds. * **EIP-712:** A standard for hashing and signing typed structured data. It improves clarity and security by presenting signature requests in a more human-readable format, detailing the `domain` (chain and contract context) and `message` (transaction specifics). * **Call Data:** The `data` field in an Ethereum-compatible transaction. It contains instructions for the target smart contract, typically including the function to execute and its arguments. Decoding this data is essential to understand what a transaction will *actually* do. * **ERC20 Token Transfers:** Standardized token transfers on EVM-compatible chains. These are not native currency transfers but function calls (usually `transfer(address, uint256)`) to the specific token contract. * **Token Decimals:** The number of decimal places a token uses to represent its smallest unit (e.g., WETH has 18 decimals). This is vital for correctly interpreting token amounts in raw transaction data. ## Essential Tools and Best Practices for Secure Signing Adopting rigorous verification habits and using the right tools can significantly reduce the risk of signing malicious transactions. * **Always Decode the `data` Field:** Never blindly trust a UI presentation. The raw `data` field holds the ground truth of what a contract interaction will perform. Use tools like: * **Wise Signer Tools:** `wise-signer.cyfrin.io/tools/` (links to various decoders) * **Swiss Knife Decoder:** `calldata.swiss-knife.xyz/decoder` * **Deth CallData Decoder:** `tools.deth.net/calldata-decoder` * **Verify All EIP-712 Components:** Systematically check the `domain` (especially `chainId` and `verifyingContract`) and every field within the `message` object (`to`, `value`, `data`, `operation`, `nonce`, and gas-related parameters). * **Be Extremely Cautious with `operation: 1` (DELEGATECALL):** While DELEGATECALL has legitimate uses (e.g., for proxy contract upgrades), it can be highly dangerous if the target contract is malicious, as it executes arbitrary code in the context of your Safe. Scrutinize such transactions with extreme care. * **Understand Token Decimals:** Always confirm the number of decimals for any token involved to correctly interpret amounts being transferred or approved. This information is usually available on block explorers like Arbiscan (for Arbitrum tokens) or Etherscan (for Ethereum mainnet tokens) on the token's contract page. * **Prioritize Secure Decoding Tools:** While web-based decoders are convenient, for maximum security, consider using local command-line tools like Foundry's `cast` (`book.getfoundry.sh`). Local tools avoid the risk of a compromised third-party website intercepting or manipulating the data you are decoding. * **Utilize Block Explorers:** Resources like Arbiscan (`arbiscan.io`) or Etherscan (`etherscan.io`) are invaluable for verifying contract addresses, checking token details, and reviewing past transaction history. * **Consult Chainlist:** Use `chainlist.org` to reliably verify chain IDs. By diligently applying these verification steps and understanding the underlying principles, you can confidently navigate the Web3 landscape and protect the assets managed by your Safe multi-sig wallet.
A detailed guide to Verifying Safe Multi-Sig Transactions: Decoding EIP-712 Payloads - This lesson provides a practical walkthrough of scrutinizing Safe (Gnosis Safe) transaction signature requests via EIP-712. You'll master how to dissect domain and message components, decode call data, and verify every detail before signing.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Advanced transaction verification techniques
Set up and configuration of a Safe wallet multi-sig
EIP-712 structured data signatures
EIP-712 verification tools
To analyze real-world hacks and apply lessons
How to decode and verify complex transactions
Cyfrin Safe Hash
Safe TX Hashes Util
OpenZeppelin Safe Utils
Swiss Knife and Deth Calldata Decoders
Last updated on June 6, 2025
Duration: 1h 07min
Course Overview
About the course
Advanced transaction verification techniques
Set up and configuration of a Safe wallet multi-sig
EIP-712 structured data signatures
EIP-712 verification tools
To analyze real-world hacks and apply lessons
How to decode and verify complex transactions
Cyfrin Safe Hash
Safe TX Hashes Util
OpenZeppelin Safe Utils
Swiss Knife and Deth Calldata Decoders
Last updated on June 6, 2025