Skip to content

Commit cab46f4

Browse files
authored
chore: improve multisig builder transaction simulation building (#144)
* improve multisig builder transaction simulation building * use encodeCall in tests * inline safeOverrides internal function * refactor transaction overrides and simplify tests * remove unnecessary comment
1 parent 96d71a8 commit cab46f4

8 files changed

+67
-37
lines changed

script/universal/DoubleNestedMultisigBuilder.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ abstract contract DoubleNestedMultisigBuilder is MultisigScript {
1111
/*
1212
* @custom:deprecated Use `sign(address[] memory _safes)` instead.
1313
*/
14-
function sign(address _signerSafe, address _intermediateSafe) public {
14+
function sign(address _signerSafe, address _intermediateSafe) external {
1515
sign(_toArray(_signerSafe, _intermediateSafe));
1616
}
1717

script/universal/MultisigBuilder.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ abstract contract MultisigBuilder is MultisigScript {
1111
/*
1212
* @custom:deprecated Use `sign(address[] memory _safes)` instead, with an empty array.
1313
*/
14-
function sign() public {
14+
function sign() external {
1515
sign(new address[](0));
1616
}
1717

script/universal/MultisigScript.sol

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,10 @@ abstract contract MultisigScript is Script {
433433
{
434434
IMulticall3.Call3[] memory calls = _simulateForSignerCalls(_safes, _datas, _value);
435435

436+
bytes32 firstCallDataHash = _getTransactionHash(_safes[0], _datas[0], _value);
437+
436438
// Now define the state overrides for the simulation.
437-
Simulation.StateOverride[] memory overrides = _overrides(_safes);
439+
Simulation.StateOverride[] memory overrides = _overrides(_safes, firstCallDataHash);
438440

439441
bytes memory txData = abi.encodeCall(IMulticall3.aggregate3, (calls));
440442
console.log("---\nSimulation link:");
@@ -451,13 +453,12 @@ abstract contract MultisigScript is Script {
451453

452454
function _simulateForSignerCalls(address[] memory _safes, bytes[] memory _datas, uint256 _value)
453455
private
454-
pure
456+
view
455457
returns (IMulticall3.Call3[] memory)
456458
{
457459
IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](_safes.length);
458460
for (uint256 i; i < _safes.length; i++) {
459-
address signer = i == 0 ? MULTICALL3_ADDRESS : _safes[i - 1];
460-
// uint256 value = i == _safes.length - 1 ? _value : 0;
461+
address signer = i == 0 ? msg.sender : _safes[i - 1];
461462

462463
calls[i] = IMulticall3.Call3({
463464
target: _safes[i],
@@ -471,35 +472,31 @@ abstract contract MultisigScript is Script {
471472
return calls;
472473
}
473474

474-
function _overrides(address[] memory _safes) internal view returns (Simulation.StateOverride[] memory) {
475-
Simulation.StateOverride[] memory simOverrides = _simulationOverrides();
476-
Simulation.StateOverride[] memory overrides =
477-
new Simulation.StateOverride[](_safes.length + simOverrides.length);
478-
for (uint256 i = 0; i < _safes.length; i++) {
479-
address owner = i == 0 ? MULTICALL3_ADDRESS : address(0);
480-
overrides[i] = _safeOverrides(_safes[i], owner);
481-
}
482-
for (uint256 i = 0; i < simOverrides.length; i++) {
483-
overrides[i + _safes.length] = simOverrides[i];
484-
}
485-
return overrides;
486-
}
487-
488475
// The state change simulation can set the threshold, owner address and/or nonce.
489476
// This allows simulation of the final transaction by overriding the threshold to 1.
490477
// State changes reflected in the simulation as a result of these overrides will
491478
// not be reflected in the prod execution.
492-
function _safeOverrides(address _safe, address _owner)
479+
function _overrides(address[] memory _safes, bytes32 firstCallDataHash)
493480
internal
494481
view
495-
virtual
496-
returns (Simulation.StateOverride memory)
482+
returns (Simulation.StateOverride[] memory)
497483
{
498-
uint256 _nonce = _getNonce(_safe);
499-
if (_owner == address(0)) {
500-
return Simulation.overrideSafeThresholdAndNonce(_safe, _nonce);
484+
Simulation.StateOverride[] memory simOverrides = _simulationOverrides();
485+
Simulation.StateOverride[] memory overrides =
486+
new Simulation.StateOverride[](_safes.length + simOverrides.length);
487+
488+
uint256 nonce = _getNonce(_safes[0]);
489+
overrides[0] = Simulation.overrideSafeThresholdApprovalAndNonce(_safes[0], nonce, msg.sender, firstCallDataHash);
490+
491+
for (uint256 i = 1; i < _safes.length; i++) {
492+
overrides[i] = Simulation.overrideSafeThresholdAndNonce(_safes[i], _getNonce(_safes[i]));
501493
}
502-
return Simulation.overrideSafeThresholdOwnerAndNonce(_safe, _owner, _nonce);
494+
495+
for (uint256 i = 0; i < simOverrides.length; i++) {
496+
overrides[i + _safes.length] = simOverrides[i];
497+
}
498+
499+
return overrides;
503500
}
504501

505502
// Get the nonce to use for the given safe, for signing and simulations.

script/universal/NestedMultisigBuilder.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ abstract contract NestedMultisigBuilder is MultisigScript {
1111
/*
1212
* @custom:deprecated Use `sign(address[] memory _safes)` instead.
1313
*/
14-
function sign(address _signerSafe) public {
14+
function sign(address _signerSafe) external {
1515
sign(_toArray(_signerSafe));
1616
}
1717

script/universal/Simulation.sol

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,44 @@ library Simulation {
5454
return accesses;
5555
}
5656

57-
function overrideSafeThresholdAndNonce(address _safe, uint256 _nonce)
57+
function overrideSafeThresholdApprovalAndNonce(address _safe, uint256 _nonce, address owner, bytes32 dataHash)
5858
internal
5959
view
6060
returns (StateOverride memory)
6161
{
62+
// solhint-disable-next-line max-line-length
6263
StateOverride memory state = StateOverride({contractAddress: _safe, overrides: new StorageOverride[](0)});
6364
state = addThresholdOverride(_safe, state);
6465
state = addNonceOverride(_safe, state, _nonce);
66+
state = addApprovalOverride(state, owner, dataHash);
6567
return state;
6668
}
6769

68-
function overrideSafeThresholdOwnerAndNonce(address _safe, address _owner, uint256 _nonce)
70+
function overrideSafeThresholdAndNonce(address _safe, uint256 _nonce)
6971
internal
7072
view
7173
returns (StateOverride memory)
7274
{
7375
StateOverride memory state = StateOverride({contractAddress: _safe, overrides: new StorageOverride[](0)});
7476
state = addThresholdOverride(_safe, state);
75-
state = addOwnerOverride(_safe, state, _owner);
7677
state = addNonceOverride(_safe, state, _nonce);
7778
return state;
7879
}
7980

81+
function addApprovalOverride(StateOverride memory _state, address owner, bytes32 dataHash)
82+
internal
83+
pure
84+
returns (StateOverride memory)
85+
{
86+
return addOverride(
87+
_state,
88+
StorageOverride({
89+
key: keccak256(abi.encode(dataHash, keccak256(abi.encode(owner, uint256(8))))),
90+
value: bytes32(uint256(0x1))
91+
})
92+
);
93+
}
94+
8095
function addThresholdOverride(address _safe, StateOverride memory _state)
8196
internal
8297
view

test/universal/DoubleNestedMultisigBuilder.t.sol

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,20 @@ contract DoubleNestedMultisigBuilderTest is Test, DoubleNestedMultisigBuilder {
7979

8080
function test_sign_double_nested_safe1() external {
8181
vm.recordLogs();
82-
sign(safe1, safe3);
82+
bytes memory txData = abi.encodeCall(DoubleNestedMultisigBuilder.sign, (safe1, safe3));
83+
vm.prank(wallet1.addr);
84+
(bool success,) = address(this).call(txData);
85+
vm.assertTrue(success);
8386
Vm.Log[] memory logs = vm.getRecordedLogs();
8487
assertEq(keccak256(logs[logs.length - 1].data), keccak256(abi.encode(dataToSign1)));
8588
}
8689

8790
function test_sign_double_nested_safe2() external {
8891
vm.recordLogs();
89-
sign(safe2, safe3);
92+
bytes memory txData = abi.encodeCall(DoubleNestedMultisigBuilder.sign, (safe2, safe3));
93+
vm.prank(wallet2.addr);
94+
(bool success,) = address(this).call(txData);
95+
vm.assertTrue(success);
9096
Vm.Log[] memory logs = vm.getRecordedLogs();
9197
assertEq(keccak256(logs[logs.length - 1].data), keccak256(abi.encode(dataToSign2)));
9298
}

test/universal/MultisigBuilder.t.sol

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ contract MultisigBuilderTest is Test, MultisigBuilder {
5858
buildCallsInternal = _buildCallsNoValue;
5959

6060
vm.recordLogs();
61-
sign();
61+
bytes memory txData = abi.encodeCall(MultisigBuilder.sign, ());
62+
vm.prank(wallet1.addr);
63+
(bool success,) = address(this).call(txData);
64+
vm.assertTrue(success);
6265
Vm.Log[] memory logs = vm.getRecordedLogs();
6366
assertEq(keccak256(logs[logs.length - 1].data), keccak256(abi.encode(dataToSignNoValue)));
6467
}
@@ -67,7 +70,10 @@ contract MultisigBuilderTest is Test, MultisigBuilder {
6770
buildCallsInternal = _buildCallsWithValue;
6871

6972
vm.recordLogs();
70-
sign();
73+
bytes memory txData = abi.encodeCall(MultisigBuilder.sign, ());
74+
vm.prank(wallet1.addr);
75+
(bool success,) = address(this).call(txData);
76+
vm.assertTrue(success);
7177
Vm.Log[] memory logs = vm.getRecordedLogs();
7278
assertEq(keccak256(logs[logs.length - 1].data), keccak256(abi.encode(dataToSignWithValue)));
7379
}

test/universal/NestedMultisigBuilder.t.sol

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,20 @@ contract NestedMultisigBuilderTest is Test, NestedMultisigBuilder {
7373

7474
function test_sign_safe1() external {
7575
vm.recordLogs();
76-
sign(safe1);
76+
bytes memory txData = abi.encodeCall(NestedMultisigBuilder.sign, (safe1));
77+
vm.prank(wallet1.addr);
78+
(bool success,) = address(this).call(txData);
79+
vm.assertTrue(success);
7780
Vm.Log[] memory logs = vm.getRecordedLogs();
7881
assertEq(keccak256(logs[logs.length - 1].data), keccak256(abi.encode(dataToSign1)));
7982
}
8083

8184
function test_sign_safe2() external {
8285
vm.recordLogs();
83-
sign(safe2);
86+
bytes memory txData = abi.encodeCall(NestedMultisigBuilder.sign, (safe2));
87+
vm.prank(wallet2.addr);
88+
(bool success,) = address(this).call(txData);
89+
vm.assertTrue(success);
8490
Vm.Log[] memory logs = vm.getRecordedLogs();
8591
assertEq(keccak256(logs[logs.length - 1].data), keccak256(abi.encode(dataToSign2)));
8692
}

0 commit comments

Comments
 (0)