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

[4337] Launchpad Account Deployer to Work Around 4337 Limitations #149

Closed
nlordell opened this issue Nov 13, 2023 · 3 comments
Closed

[4337] Launchpad Account Deployer to Work Around 4337 Limitations #149

nlordell opened this issue Nov 13, 2023 · 3 comments
Assignees

Comments

@nlordell
Copy link
Collaborator

nlordell commented Nov 13, 2023

Context / issue

The 4337 module currently allows creating a new account (using UserOperation.initCode) and executing an action atomically.

Furthermore, support for additional signing schemes (such as WebAuthn using P256 curve signatures) is already supported by the Safe by creating a dedicated signer contract per owner (in the case of WebAuthn, one contract per public key) implementing the signature validation logic using ERC-1271.

However, ERC-4337 limits the number of CREATE2 calls to 1 when using initCode meaning that for example, it is currently not possible to deploy a Safe using account abstraction with nothing but a WebAuthn signer.

Proposed solution

The proposed solution would be to split validation into two phases:

  • The first phase in validateUserOp would:
    1. deploy a "transitional Safe" for the account account
    2. tie the Safe creation with a specific setup including a public key, to prevent it from being misused
    3. verify the P256 ECDSA signature
  • The second phase would:
    1. deploy the P256 signer contract for the associate public key
    2. transition the Safe to the new setup
    3. execute the Safe's first action

Alternatives

Outcome

A script similar to runOp where an owner can be deployed with the account as part of the single bundle. An E2E test using the reference bundler where an account can execute a user operation and be deployed as part of the single bundle while using a singleton signer.

The focus is not to have a useful signer contract, just a process that is supported under 4337.

@nlordell nlordell changed the title Launchpad Account Deployer to Work Around 4337 Limitations [4337] Launchpad Account Deployer to Work Around 4337 Limitations Nov 13, 2023
nlordell added a commit that referenced this issue Nov 24, 2023
Fixes #148 

This PR introduces an E2E test using the official bundler where we
deploy a Safe that uses a custom (and silly 🤪) signing scheme over
ERC-1271. Specifically, the signing scheme is deployed as a singleton
contract that can be shared by multiple Safes using a `mapping(safe =>
data)` to differentiate between signers. In theory, this could be
adapted to, for example, checking a signature using P256 curve in order
to integrate with WebAuthn, and is meant to “prove” that the Safe with
the ERC-4337 module would work with existing WebAuthn signer
implementations.

The actual implementation is a little convoluted for a few reasons.
Namely, ERC-4337 only allows a **single** `CREATE2` per user operation
that **MUST** result in the `userOp.sender` account. Because of this,
deploying ad-hoc signers in the `Safe.setup` call is not possible, and
we have instead opted for a “singleton signer” pattern, where the logic
for verifying signatures is shared and a mapping is used to store
per-Safe context relevant to signature verification.

Additionally, In order for this to work with ERC-4337, a staked factory
is needed (as accessing associated storage for user operations with
`initCode` require a factory). Unfortunately, staked factories add all
kinds of complexities:
- Currently, the staked factory contract is quite dumb (can only stake,
but can’t unstake). A full staked factory contract requires us to iron
out some requirements and make a proper implementation with
documentation and tests (out of scope for this PR and issue, and a
non-trivial amount of work)
- Staking is determined per bundler, so while some amount of stake may
be sufficient for a bundler, it may not be sufficient for all (which
adds additional off-chain management dependencies).

**For these reasons, I would recommend exploring #149**.
@nlordell nlordell self-assigned this Nov 27, 2023
@nlordell
Copy link
Collaborator Author

nlordell commented Dec 6, 2023

Two different approaches, each with their pros/cons, were proposed in:

@mmv08
Copy link
Member

mmv08 commented Dec 11, 2023

Out of curiosity, have you looked into how other projects solve this?

Also, another (dirty but wcyd) approach to consider is getting bundlers to whitelist our factory or similar. I know some bundlers do that for some ERC20 tokens to enable ERC20 payments.

nlordell added a commit that referenced this issue Dec 13, 2023
Related to #149, follow-up to #182

This PR provides another approach to Safe initialization to accommodate
for custom signers that are deployed along with the Safe. See #182 for
more context.

This launchpad implementation is very specific to custom signers and
introduces a new `IUniqueSignerFactory` interface that must be
implemented to work with it. The name “unique” signer is not great, but
named this way since for a given factory and some “signer data”, a
`ISignatureVerifier` implementation will correspond to a **unique**
address. In the example of P-256 and WebAuthn signers, this means a
P-256 public key will correspond to a unique on-chain address with an
`ISignatureVerifier` implementation.

This implementation works by tying the Safe address to the
`signerFactory`, `signerData` and Safe `setup` parameters. This means a
couple of things:
1. The Safe address is unique for a specific initial configuration
(owner, singleton, fallback, modules, etc.); the same property holds for
the existing `SafeProxyFactory`
2. Unlike #182, the Safe address **is not** tied to the first user
operation (in fact, the user operation is signed with the same owner as
the Safe will use!)

```mermaid
sequenceDiagram
    actor B as Bundler
    participant E as EntryPoint
    participant F as SafeProxyFactory
    participant P as SafeProxy (Account)
    participant L as SafeSignerLaunchpad
    participant S as Safe (Singleton)
    participant M as Safe4337Module
    participant U as IUniqueSignerFactory
    participant A as Signer
    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  ->> +U: isValidSignatureForSigner(operationData, signature, signerData)
    U -->> -L: magic
    L -->> -P: validationData
    P -->> -E: validationData

    E  ->> +P: initializeThenUserOp<br>(singleton, signerFactory, signerData, ...setupParams, callData)
    P  ->>  L: initializeThenUserOp(…)
    note over L: SafeStorage.singleton = singleton
    L  ->> +U: createSigner(signerData)
    U  ->>  A: CREATE2
    U -->> -L: owner
    L  ->>  S: setup([owner], 1, ...setupParams)
    note over S: standard Safe setup
    L  ->>  P: DELEGATECALL(callData)
    P  ->>  S: DELEGATECALL(callData)
    S  ->>  M: execUserOp(…)
    M  ->>  P: executeFromModule(…)
    P  ->>  S: executeFromModule(…)
    S  ->>  T: Ether
    P -->> -E: ok
```

### Implementation Notes

Currently, the implementation only supports a single signer, but can be
changed to support multiple (by turning `signerFactory` and `signerData`
into arrays).

### Downsides

The downsides of this implementation over #182 is that it is a solution
_very_ specific to deploying Safes with custom signing schemes that
require additional contract deployments and not a general solution to
complex Safe setups in the context of ERC-4337.
@nlordell
Copy link
Collaborator Author

Out of curiosity, have you looked into how other projects solve this?

Not yet.

@nlordell nlordell closed this as completed Feb 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants