Skip to content

Commit e24e192

Browse files
committed
Reduce stack and remove viaIR
1 parent 7c28928 commit e24e192

File tree

3 files changed

+91
-106
lines changed

3 files changed

+91
-106
lines changed

foundry.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@ fs_permissions = [ {access = "read-write", path = "./"} ]
44
optimizer = true
55
optimizer_runs = 999999
66
solc_version = "0.8.15"
7-
viaIR = true
87

98
# See more config options https://github.com/foundry-rs/foundry/tree/master/config

script/universal/MultisigBase.sol

Lines changed: 56 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,39 @@ abstract contract MultisigBase is Simulator {
9292
});
9393
}
9494

95+
function _encodeCall(IGnosisSafe _safe, bytes memory _data, bytes memory _signatures) internal pure returns (bytes memory) {
96+
return abi.encodeCall(
97+
_safe.execTransaction,
98+
(
99+
address(multicall),
100+
0,
101+
_data,
102+
Enum.Operation.DelegateCall,
103+
0,
104+
0,
105+
0,
106+
address(0),
107+
payable(address(0)),
108+
_signatures
109+
)
110+
);
111+
}
112+
113+
function _execTransaction(IGnosisSafe _safe, bytes memory _data, bytes memory _signatures) internal returns (bool) {
114+
return _safe.execTransaction({
115+
to: address(multicall),
116+
value: 0,
117+
data: _data,
118+
operation: Enum.Operation.DelegateCall,
119+
safeTxGas: 0,
120+
baseGas: 0,
121+
gasPrice: 0,
122+
gasToken: address(0),
123+
refundReceiver: payable(address(0)),
124+
signatures: _signatures
125+
});
126+
}
127+
95128
function _executeTransaction(address _safe, IMulticall3.Call3[] memory _calls, bytes memory _signatures)
96129
internal
97130
returns (Vm.AccountAccess[] memory, SimulationPayload memory)
@@ -101,39 +134,15 @@ abstract contract MultisigBase is Simulator {
101134
bytes32 hash = _getTransactionHash(_safe, data);
102135
_signatures = prepareSignatures(_safe, hash, _signatures);
103136

137+
bytes memory simData = _encodeCall(safe, data, _signatures);
104138
logSimulationLink({
105139
_to: _safe,
106140
_from: msg.sender,
107-
_data: abi.encodeCall(
108-
safe.execTransaction,
109-
(
110-
address(multicall),
111-
0,
112-
data,
113-
Enum.Operation.DelegateCall,
114-
0,
115-
0,
116-
0,
117-
address(0),
118-
payable(address(0)),
119-
_signatures
120-
)
121-
)
141+
_data: simData
122142
});
123143

124144
vm.startStateDiffRecording();
125-
bool success = safe.execTransaction({
126-
to: address(multicall),
127-
value: 0,
128-
data: data,
129-
operation: Enum.Operation.DelegateCall,
130-
safeTxGas: 0,
131-
baseGas: 0,
132-
gasPrice: 0,
133-
gasToken: address(0),
134-
refundReceiver: payable(address(0)),
135-
signatures: _signatures
136-
});
145+
bool success = _execTransaction(safe, data, _signatures);
137146
Vm.AccountAccess[] memory accesses = vm.stopAndReturnStateDiff();
138147
require(success, "MultisigBase::_executeTransaction: Transaction failed");
139148
require(accesses.length > 0, "MultisigBase::_executeTransaction: No state changes");
@@ -143,18 +152,7 @@ abstract contract MultisigBase is Simulator {
143152
SimulationPayload memory simPayload = SimulationPayload({
144153
from: msg.sender,
145154
to: address(safe),
146-
data: abi.encodeCall(safe.execTransaction, (
147-
address(multicall),
148-
0,
149-
data,
150-
Enum.Operation.DelegateCall,
151-
0,
152-
0,
153-
0,
154-
address(0),
155-
payable(address(0)),
156-
_signatures
157-
)),
155+
data: simData,
158156
stateOverrides: new SimulationStateOverride[](0)
159157
});
160158
return (accesses, simPayload);
@@ -241,15 +239,7 @@ abstract contract MultisigBase is Simulator {
241239
uint256 j;
242240
for (uint256 i; i < count; i++) {
243241
(v, r, s) = signatureSplit(_signatures, i);
244-
address owner;
245-
if (v <= 1) {
246-
owner = address(uint160(uint256(r)));
247-
} else if (v > 30) {
248-
owner =
249-
ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
250-
} else {
251-
owner = ecrecover(dataHash, v, r, s);
252-
}
242+
address owner = extractOwner(dataHash, r, s, v);
253243

254244
// skip duplicate owners
255245
uint256 k;
@@ -281,13 +271,28 @@ abstract contract MultisigBase is Simulator {
281271

282272
// append the non-static part of the signatures (can contain EIP-1271 signatures if contracts are signers)
283273
// if there were any duplicates detected above, they will be safely ignored by Safe's checkNSignatures method
284-
if (_signatures.length > sorted.length) {
285-
sorted = bytes.concat(sorted, Bytes.slice(_signatures, sorted.length, _signatures.length - sorted.length));
286-
}
274+
sorted = appendRemainingBytes(sorted, _signatures);
287275

288276
return sorted;
289277
}
290278

279+
function appendRemainingBytes(bytes memory a1, bytes memory a2) internal pure returns (bytes memory) {
280+
if (a2.length > a1.length) {
281+
a1 = bytes.concat(a1, Bytes.slice(a2, a1.length, a2.length - a1.length));
282+
}
283+
return a1;
284+
}
285+
286+
function extractOwner(bytes32 dataHash, bytes32 r, bytes32 s, uint8 v) internal pure returns (address) {
287+
if (v <= 1) {
288+
return address(uint160(uint256(r)));
289+
}
290+
if (v > 30) {
291+
return ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
292+
}
293+
return ecrecover(dataHash, v, r, s);
294+
}
295+
291296
// see https://github.com/safe-global/safe-contracts/blob/1ed486bb148fe40c26be58d1b517cec163980027/contracts/common/SignatureDecoder.sol
292297
function signatureSplit(bytes memory signatures, uint256 pos)
293298
internal

script/universal/NestedMultisigBuilder.sol

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -156,62 +156,10 @@ abstract contract NestedMultisigBuilder is MultisigBase {
156156
internal
157157
returns (Vm.AccountAccess[] memory, SimulationPayload memory)
158158
{
159-
IGnosisSafe safe = IGnosisSafe(payable(_safe));
160159
IGnosisSafe signerSafe = IGnosisSafe(payable(_signerSafe));
160+
IGnosisSafe safe = IGnosisSafe(payable(_safe));
161161
bytes memory data = abi.encodeCall(IMulticall3.aggregate3, (_calls));
162-
bytes32 hash = _getTransactionHash(_safe, data);
163-
IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](2);
164-
165-
// simulate an approveHash, so that signer can verify the data they are signing
166-
bytes memory approveHashData = abi.encodeCall(IMulticall3.aggregate3, (toArray(
167-
IMulticall3.Call3({
168-
target: _safe,
169-
allowFailure: false,
170-
callData: abi.encodeCall(safe.approveHash, (hash))
171-
})
172-
)));
173-
bytes memory approveHashExec = abi.encodeCall(
174-
signerSafe.execTransaction,
175-
(
176-
address(multicall),
177-
0,
178-
approveHashData,
179-
Enum.Operation.DelegateCall,
180-
0,
181-
0,
182-
0,
183-
address(0),
184-
payable(address(0)),
185-
genPrevalidatedSignature(address(multicall))
186-
)
187-
);
188-
calls[0] = IMulticall3.Call3({
189-
target: _signerSafe,
190-
allowFailure: false,
191-
callData: approveHashExec
192-
});
193-
194-
// simulate the final state changes tx, so that signer can verify the final results
195-
bytes memory finalExec = abi.encodeCall(
196-
safe.execTransaction,
197-
(
198-
address(multicall),
199-
0,
200-
data,
201-
Enum.Operation.DelegateCall,
202-
0,
203-
0,
204-
0,
205-
address(0),
206-
payable(address(0)),
207-
genPrevalidatedSignature(_signerSafe)
208-
)
209-
);
210-
calls[1] = IMulticall3.Call3({
211-
target: _safe,
212-
allowFailure: false,
213-
callData: finalExec
214-
});
162+
IMulticall3.Call3[] memory calls = _simulateForSignerCalls(signerSafe, safe, data);
215163

216164
// For each safe, determine if a nonce override is needed. At this point,
217165
// no state overrides (i.e. vm.store) have been applied to the Foundry VM,
@@ -261,4 +209,37 @@ abstract contract NestedMultisigBuilder is MultisigBase {
261209
Vm.AccountAccess[] memory accesses = simulateFromSimPayload(simPayload);
262210
return (accesses, simPayload);
263211
}
212+
213+
function _simulateForSignerCalls(IGnosisSafe _signerSafe, IGnosisSafe _safe, bytes memory _data)
214+
internal
215+
returns (IMulticall3.Call3[] memory)
216+
{
217+
IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](2);
218+
bytes32 hash = _getTransactionHash(address(_safe), _data);
219+
220+
// simulate an approveHash, so that signer can verify the data they are signing
221+
bytes memory approveHashData = abi.encodeCall(IMulticall3.aggregate3, (toArray(
222+
IMulticall3.Call3({
223+
target: address(_safe),
224+
allowFailure: false,
225+
callData: abi.encodeCall(_safe.approveHash, (hash))
226+
})
227+
)));
228+
bytes memory approveHashExec = _encodeCall(_signerSafe, approveHashData, genPrevalidatedSignature(address(multicall)));
229+
calls[0] = IMulticall3.Call3({
230+
target: address(_signerSafe),
231+
allowFailure: false,
232+
callData: approveHashExec
233+
});
234+
235+
// simulate the final state changes tx, so that signer can verify the final results
236+
bytes memory finalExec = _encodeCall(_safe, _data, genPrevalidatedSignature(address(_signerSafe)));
237+
calls[1] = IMulticall3.Call3({
238+
target: address(_safe),
239+
allowFailure: false,
240+
callData: finalExec
241+
});
242+
243+
return calls;
244+
}
264245
}

0 commit comments

Comments
 (0)