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

Feature: 1.5.0 Migration #652

Merged
merged 4 commits into from
Oct 9, 2023
Merged

Feature: 1.5.0 Migration #652

merged 4 commits into from
Oct 9, 2023

Conversation

mmv08
Copy link
Member

@mmv08 mmv08 commented Sep 4, 2023

This PR focuses on adding a new migration contract for Safe Upgrade to version 1.5.0. The notable changes include:

The functions available in Safe150Migration.sol are:

  1. constructor(): Initializes the migrationSingleton with the contract's own address.
  2. migrateSingleton(): Migrates to Safe 1.5.0 Singleton (L1) at SAFE_150_SINGLETON.
  3. migrateWithFallbackHandler(): Migrates and sets the fallback handler to Safe 1.5.0 Compatibility Fallback Handler.
  4. migrateWithSetGuard(address guard): Migrates and sets the guard to the specified address.
  5. migrateWithSetGuardAndFallbackHandler(address guard): Migrates, sets the guard to the specified address, and sets the fallback handler to Safe 1.5.0 Compatibility Fallback Handler.
  6. migrateL2Singleton(): Migrates to Safe 1.5.0 Singleton (L2) at SAFE_150_SINGLETON_L2.
  7. migrateL2WithFallbackHandler(): Migrates to Safe 1.5.0 Singleton (L2) and sets the fallback handler to Safe 1.5.0 Compatibility Fallback Handler.
  8. migrateL2WithSetGuard(address guard): Migrates to Safe 1.5.0 Singleton (L2) and sets the guard to the specified address.
  9. migrateL2WithSetGuardAndFallbackHandler(address guard): Migrates to Safe 1.5.0 Singleton (L2), sets the guard to the specified address, and sets the fallback handler to Safe 1.5.0 Compatibility Fallback Handler.
  10. getGuard(): Gets the address of the current guard.
  11. isContract(address account): Checks whether an Ethereum address corresponds to a contract or an externally owned account (EOA).

@mmv08 mmv08 requested review from a team, rmeissner, Uxio0 and akshay-ap and removed request for a team September 4, 2023 14:47
@mmv08 mmv08 linked an issue Sep 4, 2023 that may be closed by this pull request
@mmv08
Copy link
Member Author

mmv08 commented Sep 4, 2023

As the 1.5.0 is not finalized I created a follow-up issue: #653

@coveralls
Copy link

coveralls commented Sep 20, 2023

Pull Request Test Coverage Report for Build 6274960393

  • 34 of 34 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+1.3%) to 92.846%

Totals Coverage Status
Change from base Build 6233603447: 1.3%
Covered Lines: 371
Relevant Lines: 391

💛 - Coveralls

* @param guard The address of the new guard contract.
*/
function migrateL2WithSetGuard(address guard) public {
require(address(this) != MIGRATION_SINGLETON, "Migration should only be called via delegatecall");
Copy link
Member

Choose a reason for hiding this comment

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

Do I assume correctly that you do not use migrateL2Singleton() to avoid the call to checkGuard();

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that's correct

import { AddressZero } from "@ethersproject/constants";
import { getSafeWithSingleton, migrationContractTo150, getSafeSingletonAt, getMock } from "../utils/setup";
import deploymentData from "../json/safeDeployment.json";
import safeRuntimeBytecode from "../json/safeRuntimeBytecode.json";
Copy link
Member

Choose a reason for hiding this comment

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

We use a separate file for the bytecode (instead of the build artefacts) to make the tests work also for future versions?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I was torn because there are no guarantees that the next version will be compatible with the upgrade contract

import safeRuntimeBytecode from "../json/safeRuntimeBytecode.json";
import { executeContractCallWithSigners } from "../../src/utils/execution";

const SAFE_SINGLETON_150_ADDRESS = "0x88627c8904eCd9DF96A572Ef32A7ff13b199Ed8D";
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we be able to calculate this based on the runtime bytecode?

Copy link
Member Author

Choose a reason for hiding this comment

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

We can, but I'm not sure why, since after 1.5.0 is released and the addresses known this doesn't have to be adjusted any more

const invalidGuardMock = await getMock();
await invalidGuardMock.givenCalldataReturnBool(guardEip165Calldata, false);

const migration = await (await migrationContractTo150()).deploy();
Copy link
Member

Choose a reason for hiding this comment

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

Why don't we deploy the migration contract as part of the deployment scripts and then reused the deterministic instance of it?

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought about doing that, but I couldn't come up with a neat way to do this because it depends on the singleton addresses, which either have to be computed depending on the runtime bytecode (doesn't work for the future contract updates well) or hardcoding the addresses (they're not known yet, because the 1.5.0 contract is not released). I plan on adding it to the deploy scripts with hardcoded addresses alongside the 1.5.0 release.


const migration = await (await migrationContractTo150()).deploy();
return {
safe130: await getSafeWithSingleton(singleton130, [user1.address]),
Copy link
Member

Choose a reason for hiding this comment

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

Should we have a test that uses a different type of proxy?

Copy link
Member Author

Choose a reason for hiding this comment

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

I added tests that use an eip-1967 proxy by openzeppelin and a modifier to every migrate function that verifies that the address stored at slot 0 is a contract. This also allowed the removal of an address check to require the library to be delegatecalled.

Copy link
Member

@rmeissner rmeissner left a comment

Choose a reason for hiding this comment

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

Would be good to align on some assumptions:

  • For which proxies should this work?
  • How do we handle other proxies?

@mmv08 mmv08 force-pushed the feature/1.5.0-migration branch 2 times, most recently from 88c0d8a to f84f71d Compare September 22, 2023 13:29
@mmv08 mmv08 self-assigned this Sep 26, 2023
@mmv08
Copy link
Member Author

mmv08 commented Sep 29, 2023

Question: Should we revisit the 1.4.1 migration contract and add the same checks as here that the address stored in the slot 0 is a contract?

@mmv08 mmv08 merged commit 8249dcb into main Oct 9, 2023
14 checks passed
@mmv08 mmv08 deleted the feature/1.5.0-migration branch October 9, 2023 11:42
@github-actions github-actions bot locked and limited conversation to collaborators Oct 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide migration contracts for 1.4.1 and 1.5.0
4 participants