Please note: There is currently no easy UI-way of doing this. Hence this guide requires some technical knowledge.

We use a 0.01 DAI (v1) transfer from a Safe to another account as example. The Safe has 2 owners (1 Metamask, 1 Hardware wallet, i.e. Ledger or Trezor) and uses a threshold of 2, i.e. both Metamask and the hardware wallet have to sign.

Off-chain confirmations with Ledger

1. Initiate the transaction via the web interface. In this example the transaction is initiated via Metamask and the second signer is the Ledger. However it should work similar vice versa.

2. Metamask pops up asking us to sign. The message to be signed (EIP712) can be inspected. The fields in the message correspond to the parameters of the execTransaction method of the core Gnosis Safe contract.

  • to is the token contract.
  • value is set to 0 since no ETH is sent.
  • data contains the transaction data. The first 4bytes (0xa9059cbb) determine that this is the transfer function. It is not part of this guide to verify that this data is correct, rather that what you are signing with the Ledger is actually what you are seeing on the Safe interface.
  • operation is set to 0 since that specifies that it is a simple EVM CALL according to the Safe contracts.
  • safeTxGas is a parameter that determines how much gas forwarded to the inner transaction. baseGas, gasPrice, gasToken and refundReceiver are all set to zero since they belong to the Safe's ability to pay a refund for relayed transactions. Please refer to the contract documentation for an explanation of those.

3. You now see a transaction in your transaction list waiting for the other owner (the Ledger) to be signed. Connect you Ledger to the interface and click "Confirm".

4. This part if about off-chain confirmations, so we remove the checkmark that says "Execute transaction". For executions, please refer to the next section.

5. You will now see a message like 5dafc3c8178f6f56e55e8d44bf55201b8e4fe3ab5a700372f837f8e8a5fff943 on your Ledger - How can you know that this is actually the transaction you wanted to make?

6. Open the transaction on our backend by replacing the Safe address and the nonce with the respective value in the following URL: https://safe-transaction.gnosis.io/api/v1/safes/0xA063Cb7CFd8E57c30c788A0572CBbf2129ae56B6/multisig-transactions/?nonce=52. Note that the transaction parameters from above are the same here.

7. Open your Safe on Etherscan: https://etherscan.io/address/0xA063Cb7CFd8E57c30c788A0572CBbf2129ae56B6#readProxyContract. You need it ti "Read as proxy". In case that does not exist yet, you can mark it as such via Contract -> More options -> Is this a Proxy?

8. Find the getTransactionHash method and fill in all the data from Safe transaction service URL from above. Hit "Query" and compare the result with safeTxHash from the transaction service. They need to be equal. In our case: 0x4ab6e70bdedf5288dc28c03ac3d61603e1fffaf26a1d9c9e1c11a0c2b0274a64

Note: In case you are using the Gnosis Safe Mobile app, you will see this safeTxHash in the "advanced" section of the transaction details. On web, this will be added in the future.

9. This safeTxHash is hashed again with sha256 and the result is displayed on the Ledger.

10. Head to an online hashing tool or any other one of your choice, enter the safeTxHash (i.e. 4ab6e70bdedf5288dc28c03ac3d61603e1fffaf26a1d9c9e1c11a0c2b0274a64, Note: WITHOUT the leading 0x). Make sure that the input type is hex or bytes

10. The resulting hash is the exact same value that is displayed on your Ledger: 5dafc3c8178f6f56e55e8d44bf55201b8e4fe3ab5a700372f837f8e8a5fff943

Now you have verified that what you are signing actually corresponds to the transaction parameters that have been put in.

The next section describes how to verify Safe transaction executions with a Ledger.

On-chain executions with Ledger

0. On your Ledger, go to the Settings of your Ethereum app and switch "Debug data" to "Displayed" in order to see the full details.

For 1 - 3, please refer to the previous section.

4. Instead of taking out that checkmark to "Execute transaction", we now leave it in.

5. Verify selector: That's the method being called. For a Safe this is the execTransaction method. It will always be 6A761202 for current Safe versions.

6. Field 1 is the to param, i.e. 89d24a6b4ccb1b6faa2625fe562bdd9a23260359 in the example above.

7. Field 2 is the value, i.e. 0 in our example above. In case it is >0, please note that it is displayed as hexadecimal value, not as decimal value on the Ledger. Consider using a tool like this one to convert between both systems.

8. Field 3 is the offset to the data (Field 12+ below)

9. Field 4 is operation, i.e. 0 is most cases.

10. Field 5 is safeTxGas, i.e. a455 (hex) or 42069 (decimal)

11. Field 6-9 is baseGas, gasPrice, gasToken and refundReceiver, i.e. 0 in our case.

12: Field 10 is the offset to the signatures (Field 17+ below)

13. Field 11 is the length of the following data field.

14. Field 12-16 is the data, i.e. a9059cbb0000000000000000000000008ed44000983b5789798b43af9d8d9c1e1e2093d0000000000000000000000000000000000000000000000000002386f26fc10000

15. Starting with Field 17 onwards, all required signatures are submitted.

16. Afterwards you review the fees and actually send the transaction.

Note: The actual field numbers will differ depending on the size of the actual transaction and the number of signatures.

Off-chain confirmations with Trezor

Follow the steps 1 - 8 from the section "Off-chain confirmations with Ledger" above.

There is no need for the sha256 (Steps 9 and 10). The Trezor displays the safeTxHash directly.

On-chain execution with Trezor

Trezor does not allow you to verify the full transaction data. Hence you can only verify the target address (i.e. you Safe) and the transaction fee.

Did this answer your question?