Skip to content
14 changes: 7 additions & 7 deletions src/improvements/SimpleAddressRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ contract SimpleAddressRegistry is StdChains {

/// @notice Initializes the contract by loading addresses from the TOML config file.
constructor(string memory _configPath) {
string memory chainKey;
if (block.chainid == getChain("mainnet").chainId) chainKey = ".eth";
else if (block.chainid == getChain("sepolia").chainId) chainKey = ".sep";
else if (block.chainid == getChain("optimism").chainId) chainKey = ".oeth";

if (bytes(chainKey).length > 0) _loadHardcodedAddresses(chainKey);

string memory toml = vm.readFile(_configPath);
if (!toml.keyExists(".addresses")) return; // If the addresses section is missing, do nothing.

Expand All @@ -32,13 +39,6 @@ contract SimpleAddressRegistry is StdChains {

_registerAddress(key, who);
}

string memory chainKey;
if (block.chainid == getChain("mainnet").chainId) chainKey = ".eth";
else if (block.chainid == getChain("sepolia").chainId) chainKey = ".sep";
else if (block.chainid == getChain("optimism").chainId) chainKey = ".oeth";

if (bytes(chainKey).length > 0) _loadHardcodedAddresses(chainKey);
}

/// @notice Retrieves an address by its contract identifier.
Expand Down
2 changes: 2 additions & 0 deletions src/improvements/addresses.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ ChainGovernorSafe = "0xb0c4C487C5cf6d67807Bc2008c66fa7e2cE744EC"
FoundationOperationSafe = "0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A"
FoundationUpgradeSafe = "0x847B5c174615B1B7fDF770882256e2D3E95b9D92"
SecurityCouncil = "0xc2819DC788505Aac350142A7A707BF9D03E3Bd03"
SuperchainConfig = "0x95703e0982140D16f8ebA6d158FccEde42f04a4C"

[sep]
FoundationOperationSafe = "0x837DE453AD5F21E89771e3c06239d8236c0EFd5E"
FoundationUpgradeSafe = "0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B"
SecurityCouncil = "0xf64bc17485f0B4Ea5F06A96514182FC4cB561977"
SuperchainConfig = "0xC2Be75506d5724086DEB7245bd260Cc9753911Be"

[oeth]
4 changes: 3 additions & 1 deletion src/improvements/script/get-rpc-url.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ if [[ "$TASK_PATH" == *"/eth/"* ]]; then
echo "mainnet"
elif [[ "$TASK_PATH" == *"/sep/"* ]]; then
echo "sepolia"
elif [[ "$TASK_PATH" == *"/oeth/"* ]]; then
echo "opMainnet"
else
echo "Error: Task path must contain either /eth/ or /sep/" >&2
echo "Error: Task path must contain either /eth/ or /sep/ or /oeth/" >&2
exit 1
fi
141 changes: 99 additions & 42 deletions src/improvements/tasks/MultisigTask.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ import {console} from "forge-std/console.sol";
import {Script} from "forge-std/Script.sol";
import {VmSafe} from "forge-std/Vm.sol";
import {Test} from "forge-std/Test.sol";
import {stdToml} from "forge-std/StdToml.sol";

import {Signatures} from "@base-contracts/script/universal/Signatures.sol";
import {Simulation} from "@base-contracts/script/universal/Simulation.sol";
import {IGnosisSafe, Enum} from "@base-contracts/script/universal/IGnosisSafe.sol";

import {SimpleAddressRegistry} from "src/improvements/SimpleAddressRegistry.sol";
import {SuperchainAddressRegistry} from "src/improvements/SuperchainAddressRegistry.sol";
import {AccountAccessParser} from "src/libraries/AccountAccessParser.sol";
import {StateOverrideManager} from "src/improvements/tasks/StateOverrideManager.sol";

type AddressRegistry is address;

abstract contract MultisigTask is Test, Script {
abstract contract MultisigTask is Test, Script, StateOverrideManager {
using EnumerableSet for EnumerableSet.AddressSet;
using AccountAccessParser for VmSafe.AccountAccess[];

Expand Down Expand Up @@ -72,6 +75,12 @@ abstract contract MultisigTask is Test, Script {
bytes32 newValue;
}

/// @notice Enum to determine the type of task
enum TaskType {
L2TaskBase,
SimpleBase
}

/// @notice transfers during task execution
mapping(address => TransferInfo[]) private _taskTransfers;

Expand Down Expand Up @@ -122,6 +131,9 @@ abstract contract MultisigTask is Test, Script {
// ==================================================
// These are functions have no default implementation and MUST be implemented by the inheriting contract.

/// @notice Returns the type of task. L2TaskBase or SimpleBase.
function taskType() public pure virtual returns (TaskType);

/// @notice Specifies the safe address string to run the template from. This string refers
/// to a named contract, where the name is read from an address registry contract.
function safeAddressString() public pure virtual returns (string memory);
Expand Down Expand Up @@ -175,6 +187,9 @@ abstract contract MultisigTask is Test, Script {
// sets safe to the safe specified by the current template from addresses.json
_taskSetup(taskConfigFilePath);

// Overrides only get applied when simulating
_overrideState(taskConfigFilePath);

// now execute task actions
Action[] memory actions = build();
VmSafe.AccountAccess[] memory accountAccesses = simulate(signatures, actions);
Expand Down Expand Up @@ -271,11 +286,12 @@ abstract contract MultisigTask is Test, Script {

IGnosisSafe _parentMultisig; // TODO parentMultisig should be of type IGnosisSafe
(addrRegistry, _parentMultisig, multicallTarget) = _configureTask(taskConfigFilePath);

parentMultisig = address(_parentMultisig);

_templateSetup(taskConfigFilePath);
nonce = IGnosisSafe(parentMultisig).nonce(); // Maybe be overridden later by state overrides

nonce = IGnosisSafe(parentMultisig).nonce(); // TODO change this once we implement task stacking
startingOwners = IGnosisSafe(parentMultisig).getOwners();

vm.label(AddressRegistry.unwrap(addrRegistry), "AddrRegistry");
Expand Down Expand Up @@ -370,9 +386,7 @@ abstract contract MultisigTask is Test, Script {

// Execute the transaction
execTransaction(parentMultisig, multicallTarget, 0, callData, Enum.Operation.DelegateCall, signatures);

VmSafe.AccountAccess[] memory accountAccesses = vm.stopAndReturnStateDiff();

return accountAccesses;
}

Expand Down Expand Up @@ -660,13 +674,19 @@ abstract contract MultisigTask is Test, Script {

/// @notice print the tenderly simulation link with the state overrides
function printTenderlySimulationLink(Action[] memory actions) internal view {
Simulation.StateOverride[] memory overrides = new Simulation.StateOverride[](1);
overrides[0] =
Simulation.overrideSafeThresholdOwnerAndNonce(parentMultisig, msg.sender, _getNonce(parentMultisig));
Simulation.StateOverride[] memory allStateOverrides =
getStateOverrides(parentMultisig, _getNonce(parentMultisig));

bytes memory txData = _execTransationCalldata(
parentMultisig, getMulticall3Calldata(actions), Signatures.genPrevalidatedSignature(msg.sender)
);
Simulation.logSimulationLink({_to: parentMultisig, _data: txData, _from: msg.sender, _overrides: overrides});

Simulation.logSimulationLink({
_to: parentMultisig,
_data: txData,
_from: msg.sender,
_overrides: allStateOverrides
});
}

/// @notice get the hash for this safe transaction
Expand Down Expand Up @@ -874,6 +894,12 @@ abstract contract MultisigTask is Test, Script {
return validActions;
}

/// @notice Override the state of the task. Function is called only when simulating.
function _overrideState(string memory taskConfigFilePath) private {
_applyStateOverrides(taskConfigFilePath);
nonce = _getNonceOrOverride(address(parentMultisig));
}

/// @dev Returns true if the given account access should be recorded as an action.
function _isValidAction(VmSafe.AccountAccess memory access, uint256 topLevelDepth) internal view returns (bool) {
bool accountNotRegistryOrVm =
Expand Down Expand Up @@ -1053,6 +1079,15 @@ abstract contract L2TaskBase is MultisigTask {

SuperchainAddressRegistry public superchainAddrRegistry;

/// @notice Returns the type of task. L2TaskBase.
/// Overrides the taskType function in the MultisigTask contract.
function taskType() public pure override returns (TaskType) {
return TaskType.L2TaskBase;
}

/// @notice Configures the task for L2TaskBase type tasks.
/// Overrides the configureTask function in the MultisigTask contract.
/// For L2TaskBase, we need to configure the superchain address registry.
function _configureTask(string memory taskConfigFilePath)
internal
virtual
Expand All @@ -1065,28 +1100,20 @@ abstract contract L2TaskBase is MultisigTask {
addrRegistry_ = AddressRegistry.wrap(address(superchainAddrRegistry));

SuperchainAddressRegistry.ChainInfo[] memory chains = superchainAddrRegistry.getChains();
// TODO: This is a hack to support the EnableDeputyPauseModuleTemplate.
// remove this once we have SimpleTaskBase contract as then this template will derive from SimpleTaskBase
// instead of L2TaskBase.
if (keccak256(bytes(config.safeAddressString)) == keccak256(bytes("FoundationOperationSafe"))) {
console.log("Using FoundationOperationSafe");
parentMultisig_ = IGnosisSafe(superchainAddrRegistry.get(config.safeAddressString));
} else {
parentMultisig_ =
IGnosisSafe(superchainAddrRegistry.getAddress(config.safeAddressString, chains[0].chainId));
// Ensure that all chains have the same parentMultisig.
for (uint256 i = 1; i < chains.length; i++) {
require(
address(parentMultisig_)
== superchainAddrRegistry.getAddress(config.safeAddressString, chains[i].chainId),
string.concat(
"MultisigTask: safe address mismatch. Caller: ",
getAddressLabel(address(parentMultisig_)),
". Actual address: ",
getAddressLabel(superchainAddrRegistry.getAddress(config.safeAddressString, chains[i].chainId))
)
);
}

parentMultisig_ = IGnosisSafe(superchainAddrRegistry.getAddress(config.safeAddressString, chains[0].chainId));
// Ensure that all chains have the same parentMultisig.
for (uint256 i = 1; i < chains.length; i++) {
require(
address(parentMultisig_)
== superchainAddrRegistry.getAddress(config.safeAddressString, chains[i].chainId),
string.concat(
"MultisigTask: safe address mismatch. Caller: ",
getAddressLabel(address(parentMultisig_)),
". Actual address: ",
getAddressLabel(superchainAddrRegistry.getAddress(config.safeAddressString, chains[i].chainId))
)
);
}

console.log("Parent multisig: ", address(parentMultisig_));
Expand All @@ -1097,19 +1124,49 @@ abstract contract L2TaskBase is MultisigTask {
// update the config to include the addresses whose storage slots changed,
// or figure out why the storage slots are being changed when they should not be.
for (uint256 i = 0; i < config.allowedStorageWriteAccesses.length; i++) {
// TODO: This is a hack to support the EnableDeputyPauseModuleTemplate.
// remove this once we have SimpleTaskBase contract as then this template will derive from SimpleTaskBase
// instead of L2TaskBase.
if (keccak256(bytes(config.allowedStorageWriteAccesses[i])) == keccak256(bytes("FoundationOperationSafe")))
{
_allowedStorageAccesses.add(superchainAddrRegistry.get(config.allowedStorageWriteAccesses[i]));
} else {
for (uint256 j = 0; j < chains.length; j++) {
_allowedStorageAccesses.add(
superchainAddrRegistry.getAddress(config.allowedStorageWriteAccesses[i], chains[j].chainId)
);
}
for (uint256 j = 0; j < chains.length; j++) {
_allowedStorageAccesses.add(
superchainAddrRegistry.getAddress(config.allowedStorageWriteAccesses[i], chains[j].chainId)
);
}
}
}
}

abstract contract SimpleBase is MultisigTask {
using EnumerableSet for EnumerableSet.AddressSet;

SimpleAddressRegistry public simpleAddrRegistry;

/// @notice Returns the type of task. SimpleBase.
/// Overrides the taskType function in the MultisigTask contract.
function taskType() public pure override returns (TaskType) {
return TaskType.SimpleBase;
}

/// @notice Configures the task for SimpleBase type tasks.
/// Overrides the configureTask function in the MultisigTask contract.
/// For SimpleBase, we need to configure the simple address registry.
function _configureTask(string memory taskConfigFilePath)
internal
virtual
override
returns (AddressRegistry addrRegistry_, IGnosisSafe parentMultisig_, address multicallTarget_)
{
multicallTarget_ = MULTICALL3_ADDRESS;

simpleAddrRegistry = new SimpleAddressRegistry(taskConfigFilePath);
addrRegistry_ = AddressRegistry.wrap(address(simpleAddrRegistry));

parentMultisig_ = IGnosisSafe(simpleAddrRegistry.get(config.safeAddressString));

// This loads the allowed storage write accesses to storage for this task.
// If this task changes storage slots outside of the allowed write accesses,
// then the task will fail at runtime and the task developer will need to
// update the config to include the addresses whose storage slots changed,
// or figure out why the storage slots are being changed when they should not be.
for (uint256 i = 0; i < config.allowedStorageWriteAccesses.length; i++) {
_allowedStorageAccesses.add(simpleAddrRegistry.get(config.allowedStorageWriteAccesses[i]));
}
}
}
Loading