-
Notifications
You must be signed in to change notification settings - Fork 925
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Safe to L2 Setup Contract (#759)
This PR introduces a setup contract that can be called from the `Safe` setup function in order to automatically promote a Safe at setup time if the code is executing on an L2. Namely, this allows the Safe Proxy factory to use a single singleton and initializer for all chains, but end up with different `singleton`s depending on the chain ID. The expected use of this contract is to use the standard proxy factory: ```solidity Safe l1Singleton; SafeL2 l2Singleton; SafeToL2Setup l2Setup; proxyFactory.createProxyWithNonce( address(l1Singleton), abi.encodeCall( l1Singleton.setup, ( owners, threshold, address(l2Setup), abi.encodeCall(l2Setup.setupToL2, address(l2Singleton)), fallbackHandler, paymentToken, payment, paymentReceiver ) ), saltNonce ) ``` On L1 (i.e. Ethereum Mainnet where `chainId == 1`), you would end up with a Safe where `safe.singleton == l1Singleton` and on any other chains, you would end up with `safe.singleton == l2Singleton`. This would happen _before_ the first transaction. --------- Co-authored-by: Mikhail <[email protected]>
- Loading branch information
Showing
16 changed files
with
550 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// 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 { | ||
/** | ||
* @notice Address of the contract. | ||
* @dev This is used to ensure that the contract is only ever `DELEGATECALL`-ed. | ||
*/ | ||
address public 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) public 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 */ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.