Skip to content

Commit

Permalink
✨ Message Hash Logic for < 1.0.0 Safe Versions (#9)
Browse files Browse the repository at this point in the history
### 🕓 Changelog

Safe multisig versions `< 1.0.0` use a legacy (i.e. the parameter value
`baseGas` was called `dataGas` previously)
[`SAFE_TX_TYPEHASH`](https://github.com/safe-global/safe-smart-account/blob/427d6f7e779431333c54bcb4d4cde31e4d57ce96/contracts/GnosisSafe.sol#L25-L28)
value:
```solidity
//keccak256(
//    "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 dataGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
//);
bytes32 public constant SAFE_TX_TYPEHASH = 0x14d461bc7412367e924637b363c7bf29b8f47e2f84869f4426e5633d8af47b20;
```

Starting with version
[`1.0.0`](https://github.com/safe-global/safe-smart-account/releases/tag/v1.0.0),
`baseGas` was introduced:
safe-global/safe-smart-account#90.

This PR introduces additional logic to the script to accommodate the
breaking change in Safe versions. Furthermore, all Safe versions `<
0.1.0` are not supported and the script issues as warning to the user.

### Testing

**Ethereum:**

The Safe multisig
[`0xecd11858a4bcc35A51084Ebe672beaCe01142fcA`](https://etherscan.io/address/0xecd11858a4bcc35A51084Ebe672beaCe01142fcA)
is based on version
[`0.1.0`](https://github.com/safe-global/safe-smart-account/releases/tag/v0.1.0).

Run:

```console
./safe_hashes.sh --network ethereum --address 0xecd11858a4bcc35A51084Ebe672beaCe01142fcA --nonce 61
```

```console
> 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 <[email protected]>
  • Loading branch information
pcaversaccio authored Dec 6, 2024
1 parent eefa051 commit 337fe27
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 18 additions & 1 deletion safe_hashes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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" \
Expand Down

0 comments on commit 337fe27

Please sign in to comment.