Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Safe to L2 Setup Contract #759

Merged
merged 15 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ SAFE_CONTRACT_UNDER_TEST="Safe"
SOLIDITY_VERSION= # Example: '0.8.19'
# For running coverage tests, `details` section of solidity settings are required, else could be removed.
SOLIDITY_SETTINGS= # Example: '{"viaIR":true,"optimizer":{"enabled":true, "details": {"yul": true, "yulDetails": { "optimizerSteps": ""}}}}'
# Sets hardhat chain id. In general, you don't need this, it's only used for testing the SafeToL2Setup contract.
HARDHAT_CHAIN_ID=31337
nlordell marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
solidity: ["0.7.6", "0.8.24"]
include:
- solidity: "0.8.24"
settings: '{"viaIR":true,"optimizer":{"enabled":true,"runs":1000000}}'
settings: '{"viaIR":false,"optimizer":{"enabled":true,"runs":1000000}}'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set this to false because hardhat fails to infer the revert reason in some tests.

env:
SOLIDITY_VERSION: ${{ matrix.solidity }}
SOLIDITY_SETTINGS: ${{ matrix.settings }}
Expand Down
2 changes: 2 additions & 0 deletions benchmark/utils/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const generateTarget = async (owners: number, threshold: number, guardAddress: s
const safe = await getSafeWithOwners(
signers.map((owner) => owner.address),
threshold,
ethers.ZeroAddress,
"0x",
fallbackHandlerAddress,
logGasUsage,
saltNumber,
Expand Down
92 changes: 92 additions & 0 deletions contracts/libraries/SafeToL2Setup.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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 We made the L1 "default" because on average the L2 price per gas is ~1000x cheaper than L1, so defaulting to L1 and going to L2 is overall more efficient.
mmv08 marked this conversation as resolved.
Show resolved Hide resolved
* @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 singleton address.
* @param singleton New singleton address
*/
event ChangedSingleton(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, "GS900");
_;
}

/**
* @notice Modifier to prevent using initialized Safes.
*/
modifier onlyNonceZero() {
require(nonce == 0, "GS901");
_;
}

/**
* @notice Modifier to ensure that the specified account is a contract.
*
*/
modifier onlyContract(address account) {
require(_codeSize(account) != 0, "GS902");
_;
}

/**
* @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 ChangedSingleton(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 */
}
}
3 changes: 2 additions & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const argv = yargs

// Load environment variables.
dotenv.config();
const { NODE_URL, INFURA_KEY, MNEMONIC, ETHERSCAN_API_KEY, PK, SOLIDITY_VERSION, SOLIDITY_SETTINGS } = process.env;
const { NODE_URL, INFURA_KEY, MNEMONIC, ETHERSCAN_API_KEY, PK, SOLIDITY_VERSION, SOLIDITY_SETTINGS, HARDHAT_CHAIN_ID } = process.env;

const DEFAULT_MNEMONIC = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat";

Expand Down Expand Up @@ -78,6 +78,7 @@ const userConfig: HardhatUserConfig = {
allowUnlimitedContractSize: true,
blockGasLimit: 100000000,
gas: 100000000,
chainId: typeof HARDHAT_CHAIN_ID === "string" ? parseInt(HARDHAT_CHAIN_ID) : 31337,
nlordell marked this conversation as resolved.
Show resolved Hide resolved
},
mainnet: {
...sharedNetworkConfig,
Expand Down
Loading
Loading