From 337fe27d2f34bf3e7bea1e4ad3650d423bad7905 Mon Sep 17 00:00:00 2001 From: sudo rm -rf --no-preserve-root / Date: Fri, 6 Dec 2024 13:45:34 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Message=20Hash=20Logic=20for=20` Hashes: Domain hash: 0x4181D94EE9F43DC08D31A2C86EB37B704F2051787A9C84D1397A60D07136D1C1 Message hash: 0xA6CF82FF9752EACE8FA8389C19C6B5FA8706AE702D7CF07F7B6734E73BFCD27D Safe transaction hash: 0x85a7e913bba17df41ff87ce425bdf950ad8fa02343ec1c7652a823c1b2a9dab5 ``` To verify the domain hash, you can use `cast`: ```console cast call 0xecd11858a4bcc35A51084Ebe672beaCe01142fcA "domainSeparator()" -r https://eth.llamarpc.com ``` which will output: ```console 0x4181d94ee9f43dc08d31a2c86eb37b704f2051787a9c84d1397a60d07136d1c1 ``` For the message hash and the Safe transaction hash, you can use the `evaluate` feature of Tenderly in one of the multisig transactions: https://dashboard.tenderly.co/tx/mainnet/0xc139e324bc231c5f7b8a9a27e775295118e9cbc06995f89225261abd0420f362/debugger?trace=0.1.1.0 ![image](https://github.com/user-attachments/assets/ba7d4120-4620-4827-9661-d5380be26c34) https://dashboard.tenderly.co/tx/mainnet/0xc139e324bc231c5f7b8a9a27e775295118e9cbc06995f89225261abd0420f362/debugger?trace=0.1.2 ![image](https://github.com/user-attachments/assets/e3d67cb9-701f-4a56-bd73-45f951b51d97) --------- Signed-off-by: Pascal Marco Caversaccio --- README.md | 2 +- safe_hashes.sh | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eaa36f5..46516e2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This Bash [script](./safe_hashes.sh) calculates the Safe transaction hashes by r > This Bash [script](./safe_hashes.sh) relies on the [Safe transaction service API](https://docs.safe.global/core-api/transaction-service-overview), which requires transactions to be proposed and _logged_ in the service before they can be retrieved. Consequently, the initial transaction proposer cannot access the transaction at the proposal stage, making this approach incompatible with 1-of-1 multisigs. > [!IMPORTANT] -> All Safe multisig versions starting from `1.0.0` and newer are supported. +> All Safe multisig versions starting from `0.1.0` and newer are supported. ## Supported Networks diff --git a/safe_hashes.sh b/safe_hashes.sh index e214058..07fd220 100755 --- a/safe_hashes.sh +++ b/safe_hashes.sh @@ -47,6 +47,9 @@ readonly DOMAIN_SEPARATOR_TYPEHASH_OLD="0x035aff83d86937d35b32e04f0ddc6ff469290e # => `keccak256("SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)");` # See: https://github.com/safe-global/safe-smart-account/blob/a0a1d4292006e26c4dbd52282f4c932e1ffca40f/contracts/Safe.sol#L59-L62. readonly SAFE_TX_TYPEHASH="0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8" +# => `keccak256("SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 dataGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)");` +# See: https://github.com/safe-global/safe-smart-account/blob/427d6f7e779431333c54bcb4d4cde31e4d57ce96/contracts/GnosisSafe.sol#L25-L28. +readonly SAFE_TX_TYPEHASH_OLD="0x14d461bc7412367e924637b363c7bf29b8f47e2f84869f4426e5633d8af47b20" # Define the supported networks from the Safe transaction service. # See https://docs.safe.global/core-api/transaction-service-supported-networks. @@ -247,11 +250,18 @@ calculate_hashes() { local domain_separator_typehash="$DOMAIN_SEPARATOR_TYPEHASH" local domain_hash_args="$domain_separator_typehash, $chain_id, $address" + local safe_tx_typehash="$SAFE_TX_TYPEHASH" # Safe multisig versions can have the format `X.Y.Z+L2`. # Remove any suffix after and including the `+` in the version string for comparison. clean_version=$(echo "$version" | sed "s/+.*//") + # Ensure that the Safe multisig version is `>= 0.1.0`. + if [[ "$(printf "%s\n%s" "$clean_version" "0.1.0" | sort -V | head -n1)" == "$clean_version" && "$clean_version" != "0.1.0" ]]; then + echo "$(tput setaf 3)Safe multisig version \"${clean_version}\" is not supported!$(tput setaf 0)" + exit 0 + fi + # Safe multisig versions `<= 1.2.0` use a legacy (i.e. without `chainId`) `DOMAIN_SEPARATOR_TYPEHASH` value. # Starting with version `1.3.0`, the `chainId` field was introduced: https://github.com/safe-global/safe-smart-account/pull/264. if [[ "$(printf "%s\n%s" "$clean_version" "1.2.0" | sort -V | head -n1)" == "$clean_version" ]]; then @@ -268,9 +278,16 @@ calculate_hashes() { # See: https://eips.ethereum.org/EIPS/eip-712#definition-of-encodedata. local data_hashed=$(cast keccak "$data") + # Safe multisig versions `< 1.0.0` use a legacy (i.e. the parameter value `baseGas` was + # called `dataGas` previously) `SAFE_TX_TYPEHASH` value. Starting with version `1.0.0`, + # `baseGas` was introduced: https://github.com/safe-global/safe-smart-account/pull/90. + if [[ "$(printf "%s\n%s" "$clean_version" "1.0.0" | sort -V | head -n1)" == "$clean_version" && "$clean_version" != "1.0.0" ]]; then + safe_tx_typehash="$SAFE_TX_TYPEHASH_OLD" + fi + # Encode the message. local message=$(cast abi-encode "SafeTxStruct(bytes32,address,uint256,bytes32,uint8,uint256,uint256,uint256,address,address,uint256)" \ - "$SAFE_TX_TYPEHASH" \ + "$safe_tx_typehash" \ "$to" \ "$value" \ "$data_hashed" \