You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
### 🕓 Changelog
This PR adds support for calculating the Safe transaction hashes for
nested Safe (i.e. use a Safe as a signatory to another Safe) approval
transactions. When a nested Safe needs to approve a transaction on the
primary Safe, it must call the
[`approveHash(bytes32)`](https://github.com/safe-global/safe-smart-account/blob/bdcfce3a76c4d1dfb256ac2ca971be7cfd6e493a/contracts/Safe.sol#L372-L379)
function on the target Safe with the Safe transaction hash to approve:
```solidity
/**
* @inheritdoc ISafe
*/
function approveHash(bytes32 hashToApprove) external override {
if (owners[msg.sender] == address(0)) revertWithError("GS030");
approvedHashes[msg.sender][hashToApprove] = 1;
emit ApproveHash(hashToApprove, msg.sender);
}
```
> Reference:
https://github.com/safe-global/safe-smart-account/blob/bdcfce3a76c4d1dfb256ac2ca971be7cfd6e493a/contracts/Safe.sol#L372-L379.
```solidity
/**
* @inheritdoc ISafe
*/
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures
) external payable override returns (bool success) {
onBeforeExecTransaction(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, signatures);
bytes32 txHash;
// Use scope here to limit variable lifetime and prevent `stack too deep` errors
{
= getTransactionHash( // Transaction info
to,
value,
data,
operation,
safeTxGas,
// Payment info
baseGas,
gasPrice,
gasToken,
refundReceiver,
// Signature info
// We use the post-increment here, so the current nonce value is used and incremented afterwards.
nonce++
);
checkSignatures(msg.sender, txHash, signatures); // <-- For a nested Safe `txHash` is used as input for `approveHash(bytes32)`.
}
...
```
> Reference:
https://github.com/safe-global/safe-smart-account/blob/bdcfce3a76c4d1dfb256ac2ca971be7cfd6e493a/contracts/Safe.sol#L108-L143.
```soldity
...
} else if (v == 1) {
// If v is 1 then it is an approved hash
// When handling approved hashes the address of the approver is encoded into r
currentOwner = address(uint160(uint256(r)));
// Hashes are automatically approved by the sender of the message or when they have been pre-approved via a separate transaction
if (executor != currentOwner && approvedHashes[currentOwner][dataHash] == 0) revertWithError("GS025");
...
```
> Reference:
https://github.com/safe-global/safe-smart-account/blob/bdcfce3a76c4d1dfb256ac2ca971be7cfd6e493a/contracts/Safe.sol#L318-L323.
### New Features
- **New Command-Line Arguments:**
- `--nested-safe-address`: Specifies the address of the nested Safe
approving the transaction.
- `--nested-safe-nonce`: Defines the nonce for the nested Safe's
approval transaction.
- **Updated Transaction Flow:**
When these arguments are provided, the script:
1. Computes and displays the primary Safe transaction hashes as usual.
2. Constructs an `approveHash` transaction with:
- `to`: The primary Safe multisig address,
- `data`: Encoded `approveHash(bytes32)` function call with the Safe
transaction hash as argument,
- `value`: Set to `0`,
- `operation`: Set to `0` (i.e. `CALL`),
- All other parameters are set to their default values (`0` or the zero
address `0x0000000000000000000000000000000000000000`).
3. Calculates and displays the hashes for the approval transaction.
- **Additional Improvements:**
- Adds validation for nested Safe parameters.
- Enhances the `--interactive` mode support to override the nested Safe
version.
- Updates the help documentation with the new parameters and an example
usage for nested Safes.
- Adds a CI Bash formatting step using
[`shfmt`](https://github.com/mvdan/sh).
- Add support of nested Safes for calculating Safe message hashes.
### Example Usage
```bash
./safe_hashes.sh --network sepolia --address 0x657ff0D4eC65D82b2bC1247b0a558bcd2f80A0f1 --nonce 4 --nested-safe-address 0x6bc56d6CE87C86CB0756c616bECFD3Cd32b09251 --nested-safe-nonce 4
```
This will calculate and return both the primary Safe transaction hashes
and the nested Safe approval transaction hashes, displaying all relevant
details for each.
---------
Signed-off-by: Pascal Marco Caversaccio <pascal.caversaccio@hotmail.ch>
Co-authored-by: Pascal Marco Caversaccio <pascal.caversaccio@hotmail.ch>
This [script](./safe_hashes.sh) supports calculating the Safe transaction hashes for nested Safe (i.e. use a Safe as a signatory to another Safe) approval transactions. When a nested Safe needs to approve a transaction on the primary Safe, it must call the [`approveHash(bytes32)`](https://github.com/safe-global/safe-smart-account/blob/bdcfce3a76c4d1dfb256ac2ca971be7cfd6e493a/contracts/Safe.sol#L372-L379) function on the target Safe with the Safe transaction hash to approve:
318
+
319
+
```solidity
320
+
function approveHash(bytes32 hashToApprove) external override {
321
+
if (owners[msg.sender] == address(0)) revertWithError("GS030");
322
+
approvedHashes[msg.sender][hashToApprove] = 1;
323
+
emit ApproveHash(hashToApprove, msg.sender);
324
+
}
325
+
```
326
+
327
+
To calculate both the primary transaction hash and the nested Safe `approveHash` transaction hash, specify the `network`, `address`, `nonce`, `nested-safe-address`, and `nested-safe-nonce` parameters:
The [script](./safe_hashes.sh) will first calculate and display the primary transaction hashes. Then, it will construct and calculate the hashes for the `approveHash` transaction:
The nested Safe `approveHash` transaction is constructed with the following parameters:
403
+
404
+
-`to`: The primary Safe multisig address.
405
+
-`data`: Encoded `approveHash(bytes32)` function call with the Safe transaction hash as argument.
406
+
-`value`: Set to `0`.
407
+
-`operation`: Set to `0` (i.e. `CALL`).
408
+
- All other parameters are set to their default values (`0` or the zero address `0x0000000000000000000000000000000000000000`).
409
+
410
+
> [!NOTE]
411
+
> The `--interactive` mode supports nested Safe transactions but only allows overriding the nested Safe version, not other transaction values in the `approveHash` transaction.
412
+
310
413
## Safe Message Hashes
311
414
312
415
> [!IMPORTANT]
@@ -365,12 +468,15 @@ Nonce:
365
468
ea499f2f-fdbc-4d04-92c4-b60aba887e06
366
469
367
470
> Hashes:
368
-
Raw message hash: 0xcb1a9208c1a7c191185938c7d304ed01db68677eea4e689d688469aa72e34236
> The `--interactive` mode is not supported when calculating Safe message hashes. If using a nested Safe as the signer for the primary message, you must provide the `--nested-safe-address` argument along with the other parameters to retrieve the additional computed hashes for the nested Safe.
0 commit comments