Skip to content

Commit

Permalink
Reduce stack and remove viaIR
Browse files Browse the repository at this point in the history
  • Loading branch information
mdehoog committed Oct 19, 2024
1 parent 7c28928 commit eb56fef
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 106 deletions.
1 change: 0 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ fs_permissions = [ {access = "read-write", path = "./"} ]
optimizer = true
optimizer_runs = 999999
solc_version = "0.8.15"
viaIR = true

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
107 changes: 56 additions & 51 deletions script/universal/MultisigBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,39 @@ abstract contract MultisigBase is Simulator {
});
}

function _encodeCall(IGnosisSafe _safe, bytes memory _data, bytes memory _signatures) internal pure returns (bytes memory) {
return abi.encodeCall(
_safe.execTransaction,
(
address(multicall),
0,
_data,
Enum.Operation.DelegateCall,
0,
0,
0,
address(0),
payable(address(0)),
_signatures
)
);
}

function _execTransaction(IGnosisSafe _safe, bytes memory _data, bytes memory _signatures) internal returns (bool) {
return _safe.execTransaction({
to: address(multicall),
value: 0,
data: _data,
operation: Enum.Operation.DelegateCall,
safeTxGas: 0,
baseGas: 0,
gasPrice: 0,
gasToken: address(0),
refundReceiver: payable(address(0)),
signatures: _signatures
});
}

function _executeTransaction(address _safe, IMulticall3.Call3[] memory _calls, bytes memory _signatures)
internal
returns (Vm.AccountAccess[] memory, SimulationPayload memory)
Expand All @@ -101,39 +134,15 @@ abstract contract MultisigBase is Simulator {
bytes32 hash = _getTransactionHash(_safe, data);
_signatures = prepareSignatures(_safe, hash, _signatures);

bytes memory simData = _encodeCall(safe, data, _signatures);
logSimulationLink({
_to: _safe,
_from: msg.sender,
_data: abi.encodeCall(
safe.execTransaction,
(
address(multicall),
0,
data,
Enum.Operation.DelegateCall,
0,
0,
0,
address(0),
payable(address(0)),
_signatures
)
)
_data: simData
});

vm.startStateDiffRecording();
bool success = safe.execTransaction({
to: address(multicall),
value: 0,
data: data,
operation: Enum.Operation.DelegateCall,
safeTxGas: 0,
baseGas: 0,
gasPrice: 0,
gasToken: address(0),
refundReceiver: payable(address(0)),
signatures: _signatures
});
bool success = _execTransaction(safe, data, _signatures);
Vm.AccountAccess[] memory accesses = vm.stopAndReturnStateDiff();
require(success, "MultisigBase::_executeTransaction: Transaction failed");
require(accesses.length > 0, "MultisigBase::_executeTransaction: No state changes");
Expand All @@ -143,18 +152,7 @@ abstract contract MultisigBase is Simulator {
SimulationPayload memory simPayload = SimulationPayload({
from: msg.sender,
to: address(safe),
data: abi.encodeCall(safe.execTransaction, (
address(multicall),
0,
data,
Enum.Operation.DelegateCall,
0,
0,
0,
address(0),
payable(address(0)),
_signatures
)),
data: simData,
stateOverrides: new SimulationStateOverride[](0)
});
return (accesses, simPayload);
Expand Down Expand Up @@ -241,15 +239,7 @@ abstract contract MultisigBase is Simulator {
uint256 j;
for (uint256 i; i < count; i++) {
(v, r, s) = signatureSplit(_signatures, i);
address owner;
if (v <= 1) {
owner = address(uint160(uint256(r)));
} else if (v > 30) {
owner =
ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
} else {
owner = ecrecover(dataHash, v, r, s);
}
address owner = extractOwner(dataHash, r, s, v);

// skip duplicate owners
uint256 k;
Expand Down Expand Up @@ -281,13 +271,28 @@ abstract contract MultisigBase is Simulator {

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

return sorted;
}

function appendRemainingBytes(bytes memory a1, bytes memory a2) internal pure returns (bytes memory) {
if (a2.length > a1.length) {
a1 = bytes.concat(a1, Bytes.slice(a2, a1.length, a2.length - a1.length));
}
return a1;
}

function extractOwner(bytes32 dataHash, bytes32 r, bytes32 s, uint8 v) internal pure returns (address) {
if (v <= 1) {
return address(uint160(uint256(r)));
}
if (v > 30) {
return ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
}
return ecrecover(dataHash, v, r, s);
}

// see https://github.com/safe-global/safe-contracts/blob/1ed486bb148fe40c26be58d1b517cec163980027/contracts/common/SignatureDecoder.sol
function signatureSplit(bytes memory signatures, uint256 pos)
internal
Expand Down
89 changes: 35 additions & 54 deletions script/universal/NestedMultisigBuilder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -156,62 +156,10 @@ abstract contract NestedMultisigBuilder is MultisigBase {
internal
returns (Vm.AccountAccess[] memory, SimulationPayload memory)
{
IGnosisSafe safe = IGnosisSafe(payable(_safe));
IGnosisSafe signerSafe = IGnosisSafe(payable(_signerSafe));
IGnosisSafe safe = IGnosisSafe(payable(_safe));
bytes memory data = abi.encodeCall(IMulticall3.aggregate3, (_calls));
bytes32 hash = _getTransactionHash(_safe, data);
IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](2);

// simulate an approveHash, so that signer can verify the data they are signing
bytes memory approveHashData = abi.encodeCall(IMulticall3.aggregate3, (toArray(
IMulticall3.Call3({
target: _safe,
allowFailure: false,
callData: abi.encodeCall(safe.approveHash, (hash))
})
)));
bytes memory approveHashExec = abi.encodeCall(
signerSafe.execTransaction,
(
address(multicall),
0,
approveHashData,
Enum.Operation.DelegateCall,
0,
0,
0,
address(0),
payable(address(0)),
genPrevalidatedSignature(address(multicall))
)
);
calls[0] = IMulticall3.Call3({
target: _signerSafe,
allowFailure: false,
callData: approveHashExec
});

// simulate the final state changes tx, so that signer can verify the final results
bytes memory finalExec = abi.encodeCall(
safe.execTransaction,
(
address(multicall),
0,
data,
Enum.Operation.DelegateCall,
0,
0,
0,
address(0),
payable(address(0)),
genPrevalidatedSignature(_signerSafe)
)
);
calls[1] = IMulticall3.Call3({
target: _safe,
allowFailure: false,
callData: finalExec
});
IMulticall3.Call3[] memory calls = _simulateForSignerCalls(signerSafe, safe, data);

// For each safe, determine if a nonce override is needed. At this point,
// no state overrides (i.e. vm.store) have been applied to the Foundry VM,
Expand Down Expand Up @@ -261,4 +209,37 @@ abstract contract NestedMultisigBuilder is MultisigBase {
Vm.AccountAccess[] memory accesses = simulateFromSimPayload(simPayload);
return (accesses, simPayload);
}

function _simulateForSignerCalls(IGnosisSafe _signerSafe, IGnosisSafe _safe, bytes memory _data)
internal view
returns (IMulticall3.Call3[] memory)
{
IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](2);
bytes32 hash = _getTransactionHash(address(_safe), _data);

// simulate an approveHash, so that signer can verify the data they are signing
bytes memory approveHashData = abi.encodeCall(IMulticall3.aggregate3, (toArray(
IMulticall3.Call3({
target: address(_safe),
allowFailure: false,
callData: abi.encodeCall(_safe.approveHash, (hash))
})
)));
bytes memory approveHashExec = _encodeCall(_signerSafe, approveHashData, genPrevalidatedSignature(address(multicall)));
calls[0] = IMulticall3.Call3({
target: address(_signerSafe),
allowFailure: false,
callData: approveHashExec
});

// simulate the final state changes tx, so that signer can verify the final results
bytes memory finalExec = _encodeCall(_safe, _data, genPrevalidatedSignature(address(_signerSafe)));
calls[1] = IMulticall3.Call3({
target: address(_safe),
allowFailure: false,
callData: finalExec
});

return calls;
}
}

0 comments on commit eb56fef

Please sign in to comment.