I have a safe where one of the required signers is an offline wallet, which is never connected to the internet. The other signer account is my MetaMask wallet.

To send a transaction, I guess I have to sign the transaction data with my offline wallet first, and then use that signature as input for execTransaction on my Gnosis safe. So far, this tutorial helped me a bit, but I am still unsure how to call execTransaction.

Any help is appreciated. I know how to code in Python, JS and Solidity, so the answer can be technical, no worries.

2 Answers 2


I was searching how to do exactly this (multisig with one owner wallet always offline) and came across your post with the very useful link. I eventually got this to work...

To send a transaction, I guess I have to sign the transaction data with my offline wallet first, and then use that signature as input for execTransaction on my Gnosis safe

I tested the following on both Fantom (https://safe.fantom.network/) and Polygon (https://app.safe.global/) network safes and it works. The process is probably is either exactly the same or very similar for other networks:

Once you sign the transaction hash using the offline wallet by following the instructions on your link (on your offline machine), you transfer it somehow to your online machine running Metamask. Let's say that the signature is stored in a variable, offline_signature. Then you would do:

#python code
metamask_wallet = '0x...'   #your metamask wallet address, which is required for transaction approval                                                                                                                            
final_signature = '0x000000000000000000000000' + metamask_wallet[2:] + '000000000000000000000000000000000000000000000000000000000000000001' + offline_signature.signature.hex()[2:] 
print (final_signature)

final_signature looks weird, but it works on the Fantom and Polygon Gnosis Safe implementations (probably ethereum too); the way I came up with this was by submitting a real multisig transaction the 'normal' way, then figuring out how all the data is glued together to create the final sig that get passed to execTransaction

Now you can use the value of final_signature to call execTransaction with your metamask wallet.

There are couple ways you can call execTransaction on your safe contract. One easy way is to open your safe contract on the appropriate web-based blockchain explorer (e.g. ftmscan, etherscan) enter image description here

Then you would click "Connect to Web3" (see red circle at top of image), connect your Metamask wallet, and manually enter the transaction values under "Write as Proxy". In this case I want to send 1 FTM from my safe contract to some arbitrary address.

enter image description here

Click 'Write', approve the transaction in Metamask, and it will execute.

To send a non-native token, you would need to change the data and to fields and also set the value field to zero when you call getTransactionHash and execTransaction. You can get the correct data and to fields by setting up the transaction in your safe for an arbitrary non-native token, then seeing what the values for those fields are in the Metamask window (without actually signing the request).

To go even further, another way you can call execTransaction is with python. In this case you would cut out Metamask. Notice in the first screenshot where it says "ABI for the implementation contract at ...". You'd go to that contract to get the ABI and bytecode for your safe, and then call execTransaction using the same data as was entered using the block explorer UI.

  • Thanks a lot for that detailed explanation, it will be really useful for other people finding this post.
    – BenK
    Dec 28, 2023 at 19:41

If you submit the offline wallet's signature to the safe API endpoint (i.e. the last sentence in your link), you should automatically be prompted to execute the transaction using your MetaMask wallet when you open the GUI again. Just make sure you replace contract address in the API endpoint with the address of your safe contract, and replace the API's base URL with the appropriate address from: here

Additionally, signatures/confirmations that have been submitted, for transactions that have not executed yet, can be viewed at


which I found helpful when getting this figured out.

  • Thanks, this helped a lot
    – BenK
    Mar 9, 2023 at 22:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.