// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/**
* @title SafeStorage - Storage layout of the Safe contracts to be used in libraries.
* @dev Should be always the first base contract of a library that is used with a Safe.
* @author Richard Meissner - @rmeissner
*/
contract SafeStorage {
// From /common/Singleton.sol
address internal singleton;
// From /common/ModuleManager.sol
mapping(address => address) internal modules;
// From /common/OwnerManager.sol
mapping(address => address) internal owners;
uint256 internal ownerCount;
uint256 internal threshold;
// From /Safe.sol
uint256 internal nonce;
bytes32 internal _deprecatedDomainSeparator;
mapping(bytes32 => uint256) internal signedMessages;
mapping(address => mapping(bytes32 => uint256)) internal approvedHashes;
}
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import {SafeStorage} from "../libraries/SafeStorage.sol";
/**
* @title Safe to L2 Setup Contract
* @dev This contract expects the singleton to be the {Safe} by default. Even if there are more
* {SafeL2} proxies deployed, the average gas cost on L2s is significantly lower, making the
* current design more economically efficient overall.
* @notice This contract facilitates the deployment of a Safe to the same address on all networks by
* automatically changing the singleton to the L2 version when not on chain ID 1.
*/
contract SafeToL2Setup is SafeStorage {
/**
* @dev Address of the contract.
* This is used to ensure that the contract is only ever `DELEGATECALL`-ed.
*/
address private immutable SELF;
/**
* @notice Event indicating a change of master copy address.
* @param singleton New master copy address
*/
event ChangedMasterCopy(address singleton);
/**
* @notice Initializes a new {SafeToL2Setup} instance.
*/
constructor() {
SELF = address(this);
}
/**
* @notice Modifier ensure a function is only called via `DELEGATECALL`. Will revert otherwise.
*/
modifier onlyDelegateCall() {
require(address(this) != SELF, "SafeToL2Setup should only be called via delegatecall");
_;
}
/**
* @notice Modifier to prevent using initialized Safes.
*/
modifier onlyNonceZero() {
require(nonce == 0, "Safe must have not executed any tx");
_;
}
/**
* @notice Modifier to ensure that the specified account is a contract.
*
*/
modifier onlyContract(address account) {
require(codeSize(account) != 0, "Account doesn't contain code");
_;
}
/**
* @notice Setup the Safe with the provided L2 singleton if needed.
* @dev This function checks that the chain ID is not 1, and if it isn't updates the singleton
* to the provided L2 singleton.
*/
function setupToL2(address l2Singleton) external onlyDelegateCall onlyNonceZero onlyContract(l2Singleton) {
if (chainId() != 1) {
singleton = l2Singleton;
emit ChangedMasterCopy(l2Singleton);
}
}
/**
* @notice Returns the current chain ID.
*/
function chainId() private view returns (uint256 result) {
/* solhint-disable no-inline-assembly */
/// @solidity memory-safe-assembly
assembly {
result := chainid()
}
/* solhint-enable no-inline-assembly */
}
/**
* @notice Returns the code size of the specified account.
*/
function codeSize(address account) internal view returns (uint256 result) {
/* solhint-disable no-inline-assembly */
/// @solidity memory-safe-assembly
assembly {
result := extcodesize(account)
}
/* solhint-enable no-inline-assembly */
}
}
{
"compilationTarget": {
"contracts/libraries/SafeToL2Setup.sol": "SafeToL2Setup"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"singleton","type":"address"}],"name":"ChangedMasterCopy","type":"event"},{"inputs":[{"internalType":"address","name":"l2Singleton","type":"address"}],"name":"setupToL2","outputs":[],"stateMutability":"nonpayable","type":"function"}]