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

Launchpad Contract for ERC-4337-Compatible Initialisation #182

Closed
wants to merge 10 commits into from

Conversation

nlordell
Copy link
Collaborator

@nlordell nlordell commented Nov 30, 2023

Related to #149

ERC-4337 limits the op-codes that can be used in account creation with initCode and validateUserOp. In particular, it limits CREATE2 calls to only a single call that results in the user operations account. This is a problem if you want to account setup to deploy more than one contract.

In particular this is interesting in the context of P-256 signature verification (elliptic curve used by Passkeys and WebAuthn). It is typically implemented as:

P256Signer
contract P256Signer {
    // The X and Y coordinates of the public key
    uint256 immutable public X;
    uint256 immutable public Y;
    
    constructor(uint256 x, uint256 y) {
        X = x;
        Y = y;
    }
    
    function isValidSignature(bytes32 message, bytes calldata signature) external view returns (bytes4) {
        // check that the signature for the message matches the public key
    }
}

This means that, when deploying a Safe with a single P-256 signer that has never been used before, we would need the initialisation to deploy two contracts:

  • The SafeProxy for the new account
  • The P256Signer contract for the specified public key

ERC-4337 does not allow this, even for staked factories (see #147). In order to work around this, we use a “deferred setup” strategy, where the init code deploys what we call a “launchpad” contract and is tied to specific UserOperation parameters, and the user operation execution does the actual setup:

sequenceDiagram
    actor B as Bundler
    participant E as EntryPoint
    participant F as SafeProxyFactory
    participant P as SafeProxy (Account)
    participant L as SafeOpLaunchpad
    participant S as Safe (Singleton)
    participant M as Safe4337Module
    actor T as Target

    B  ->> +E: handleOps([userOp])
    E  ->> +F: CALL(userOp.initCode[:20])
    F  ->>  P: CREATE2
    F  ->>  P: setup(initHash)
    P  ->>  L: setup(initHash)
    note over L: SSTORE(initHash)
    F -->> -E: ok

    E  ->> +P: validateUserOp(userOp)
    P  ->>  L: validateUserOp(userOp)
    note over L: require(getInitHash(userOp) == initHash)
    L -->>  P: validationData
    P -->> -E: validationData

    E  ->> +P: initializeThenUserOp<br>(singleton,setup,callData,signatures)
    P  ->>  L: initializeThenUserOp(...)
    note over L: SafeStorage.singleton = singleton
    L  ->>  S: DELEGATECALL(setup)
    note over S: standard Safe setup
    note over P: At ths point, the Safe is deployed
    L  ->>  P: DELEGATECALL(callData)
    P  ->>  S: DELEGATECALL(callData)
    S  ->>  M: execUserOp(...)
    M  ->>  P: executeFromModule(...)
    P  ->>  S: executeFromModule(...)
    S  ->>  T: Ether
    P -->> -E: ok
Loading

An E2E test was added to verify this strategy works with the reference bundler (and, to no surprise, it does).

Implementation Notes

The _getInitHash function is strange - but a work around to ensure that the initHash is not self-referential.

Downsides

The way it was currently implemented the Safe’s address is tied to the first user operation that the Safe executes. This means that given an owner (for example a P-256 signer) there is no way of knowing what the final address of the Safe account will be as it depends on the user operation’s calldata, gas parameters, etc.

@coveralls
Copy link

coveralls commented Dec 4, 2023

Pull Request Test Coverage Report for Build 7114753184

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 100.0%

Totals Coverage Status
Change from base Build 7062952438: 0.0%
Covered Lines: 33
Relevant Lines: 33

💛 - Coveralls

@nlordell nlordell changed the title Launchpad Contract for ERC-4337-Compatible Complex Initialisation Launchpad Contract for ERC-4337-Compatible Initialisation Dec 6, 2023
@nlordell nlordell marked this pull request as ready for review December 6, 2023 14:42
@nlordell nlordell requested a review from a team as a code owner December 6, 2023 14:42
@nlordell nlordell requested review from rmeissner, akshay-ap, mmv08 and remedcu and removed request for a team December 6, 2023 14:42
@mmv08
Copy link
Member

mmv08 commented Dec 11, 2023

This means that given an owner (for example a P-256 signer) there is no way of knowing what the final address of the Safe account will be as it depends on the user operation’s calldata, gas parameters, etc.

While I like the overall solution, in my opinion, this is a huge downside, in my experience, many projects care about deterministic addresses and the "deploy as you go" approach. Also, this prevents cross-chain replays because the hash includes the chain ID, and even without it, it may be tricky because of the fluctuations in gas parameters.

@nlordell
Copy link
Collaborator Author

this is a huge downside

I agree - I don't think this is a great approach (just wanted to create the PR for completeness/documentation purposes). In the context of ERC-4337 + WebAuthn signer deployment, I do much prefer #184. If you are OK with it, I will close this PR in favour of the latter.

@mmv08
Copy link
Member

mmv08 commented Dec 12, 2023

this is a huge downside

I agree - I don't think this is a great approach (just wanted to create the PR for completeness/documentation purposes). In the context of ERC-4337 + WebAuthn signer deployment, I do much prefer #184. If you are OK with it, I will close this PR in favour of the latter.

Yes I'm very ok with it :)

@nlordell
Copy link
Collaborator Author

Closing in favour of #184

@nlordell nlordell closed this Dec 13, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Dec 13, 2023
@nlordell nlordell deleted the launchpad-contract branch March 5, 2024 07:28
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.

3 participants