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

Supporting ERC4337 #15

Merged
merged 20 commits into from
Sep 18, 2023
Merged

Supporting ERC4337 #15

merged 20 commits into from
Sep 18, 2023

Conversation

eerkaijun
Copy link
Contributor

Work in progress for spinning up ERC4337 bundler and paymaster.

bundler.py Outdated Show resolved Hide resolved
@eerkaijun eerkaijun force-pushed the kai/account-abstraction branch 2 times, most recently from 2070b2c to 949c100 Compare August 26, 2023 14:50
@eerkaijun
Copy link
Contributor Author

Managed to spin up a bundler and verifying paymaster, tested them as well (currently tested on L1 devnet, when L2 is ready, we should test those too, as I believe the gas estimation will be slightly different).

An open ended question is private key management: there are multiple instances where devs will need to use a signer.

  • Account abstraction contract deployments
  • Bundler signer
  • Paymaster signer

Should we assume that people will use the same account for all three of these instances (thus only ask for the private key once), or should we ask three times (so that devs can put in different private keys if they want to)?

@eerkaijun eerkaijun marked this pull request as ready for review August 31, 2023 13:33
paymaster/package-lock.json Outdated Show resolved Hide resolved
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
Copy link
Member

Choose a reason for hiding this comment

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

Is there a way we can use ESM or do we pull in stuff that won't work with it?
(Also a huge nit, not urgent, and not a blocker for merging this.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let me double check

Copy link
Member

Choose a reason for hiding this comment

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

Could you add an issue for it if you don't end up looking into it? If you do and it's bothersome, I think it's good to add a comment about it.

We should probably add a "non-priority" tag for stuff like that, I just want to keep track of it, makes for good batch spring cleanup.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#41

help="disable ANSI escape codes for terminal manipulation",
default=True,
dest="use_ansi_esc",
action="store_false")
Copy link
Member

Choose a reason for hiding this comment

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

Should this be folded into roll.py?
Not really important rn.

Copy link
Member

Choose a reason for hiding this comment

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

Can you add an issue for this & link it here?

Copy link
Member

Choose a reason for hiding this comment

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

Can you add an issue for this and link it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#38

bundler.py Outdated Show resolved Hide resolved
bundler.py Show resolved Hide resolved
bundler.py Show resolved Hide resolved
bundler.py Show resolved Hide resolved
paymaster/src/abis.ts Show resolved Hide resolved
paymaster/src/server.ts Outdated Show resolved Hide resolved
paymaster/test/tests.ts Show resolved Hide resolved
@norswap
Copy link
Member

norswap commented Sep 1, 2023

Should we assume that people will use the same account for all three of these instances (thus only ask for the private key once), or should we ask three times (so that devs can put in different private keys if they want to)?

Yes, we should support using different ones. See my comment above, probably want to include these as different config fields. If the user does not provide them, prompt. What we can do is give an easy alias for reusing a key. (e.g. you can type "deployer" to reuse to the deployment key)

@norswap
Copy link
Member

norswap commented Sep 1, 2023

Also, L2 should be working now! (I need to push the change that uses HTTP instead of WebSocket on the op-node, but that's the only fix necessary)

@eerkaijun
Copy link
Contributor Author

@norswap okie made some changes, have also now tested it with L2

bundler.py Outdated Show resolved Hide resolved
help="disable ANSI escape codes for terminal manipulation",
default=True,
dest="use_ansi_esc",
action="store_false")
Copy link
Member

Choose a reason for hiding this comment

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

Can you add an issue for this & link it here?

bundler.py Show resolved Hide resolved
bundler.py Outdated Show resolved Hide resolved
help="disable ANSI escape codes for terminal manipulation",
default=True,
dest="use_ansi_esc",
action="store_false")
Copy link
Member

Choose a reason for hiding this comment

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

Can you add an issue for this and link it here?

{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
Copy link
Member

Choose a reason for hiding this comment

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

Could you add an issue for it if you don't end up looking into it? If you do and it's bothersome, I think it's good to add a comment about it.

We should probably add a "non-priority" tag for stuff like that, I just want to keep track of it, makes for good batch spring cleanup.

Copy link
Member

@norswap norswap left a comment

Choose a reason for hiding this comment

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

This is shaping up great.

My suggestion here would be to run through the review comments, change whatever can be done quickly, and collect a list of what can't (I can compile it if you want), then merge this and do the rest in a subsequent PR.

I want to try to decrease time-to-merge, esp. when there's ongoing parallel work (not a problem atm).

Sorry for taking so long to review!

1. Users send a JSON RPC request to the paymaster server with the corresponding UserOp.
2. The paymaster server signs the hash of the UserOp if it decides to sponsor the transaction. The signature is encoded in the `paymasterAndData` field of the userOp.
3. The paymaster server sends the updated UserOp back to the user.
4. The user can now send the UserOp to the bundler and the transaction will be sponsored by the paymaster.
Copy link
Member

Choose a reason for hiding this comment

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

Oh I see, there is no mempool involved!

This is good, below is just me spitballing on what how I thought this would be + how it could be:

Is there a way in which the paymaster could submit the transaction to the bundler directly? I guess the updated userop needs to be signed by the user right?

Before seeing this, my very naive flow assumption was:

  1. Users submit userOp to mempool (functionally equivalent, as its a RPC call, just that here we don't put it in the mempool)
  2. Batchers pick userOp, check if they can be sponsored (using the whole 4337 dance) and if so submit them.
  3. The sponsorship check is on-chain, the paymaster contract basically always sponsors non-reverting tx (is it even possible to check this in 4337???) to whitelisted contracts. Could also involve rate limiting (though update usage would have to live in the sponsored tx, NOT in the validation logic which can't write state).

I think the ideal flow for what we're doing would be to:

  1. Send to server that is both a paymaster & bundler. (Optionally drop to mempool, no need for now.)
  2. Sign userOp, then wrap in a tx that
    • Writes to the paymaster contract, authorizing userOp
    • Submits the userOp to the entrypoint contract (paymaster validation then checks the authorization we just wrote)

This would be an ideal one-request flow from the user's perspective.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean users send the signed UserOp to paymaster, and the paymaster submit to the entrypoint contract directly?

Copy link
Member

Choose a reason for hiding this comment

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

Yes exactly!

paymaster/README.md Show resolved Hide resolved
} catch (error) {
console.error('Error reading/parsing JSON file:', error);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Why not reuse the read_json_file from libroll.py here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This file is in Typescript.

Copy link
Member

Choose a reason for hiding this comment

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

omg haha

"""
run(descr, f"git clone {url}")
print(f"Succeeded: {descr}")

Copy link
Member

Choose a reason for hiding this comment

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

Could you extend the usage of this to other parts of the codebase where we clone? Or add an issue for it is equally good!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already done :)

@@ -0,0 +1,54 @@
import { BigNumberish, ethers } from 'ethers';
Copy link
Member

Choose a reason for hiding this comment

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

I could switch to viem later if I wanted to, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that shouldn't be a problem

paymaster/src/rpcMethods.ts Outdated Show resolved Hide resolved
);

/// NOTE: Define additional transaction sponsor logic here if needed
/// For example, it could be a list of whitelisted addresses
Copy link
Member

Choose a reason for hiding this comment

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

Do you think you could implement this as a configuration value that is either None/undefined (in which case there is not check) or a list (in which case only whitelisted contracts are allowed, nothing allowed if list is empty)?

Probably good for next PR.

Copy link
Member

Choose a reason for hiding this comment

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

^ for this one though, .env won't cut it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I think this would be a good enhancement. Added a new issue: #39

bundler.py Outdated Show resolved Hide resolved
log_file = open(log_file_path, "w")
PROCESS_MGR.start(
"start bundler",
"stackup-bundler start --mode private",
Copy link
Member

Choose a reason for hiding this comment

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

Just curious, what does --mode private do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Means it's a private mempool for the bundler (i.e. only our own bundler will be able to see the transactions sent to this RPC endpoints as they are not broadcasted)

"""
Private key to use for deploying 4337 contracts and paymaster signature (None by default).
Will be used if set, otherwise will prompt users to enter private key.
"""
Copy link
Member

Choose a reason for hiding this comment

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

Is it good to always make the deployer the same as the paymaster signature address? Haven't really thought about this in earnest.

I know people like to talk about "secure signers" as option for holding live keys (e.g. paymaster signer) and it might be a bit of a pain to use such a signer for deployment? Is that a good reason to separate them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a good question, let's revisit it: #40

@norswap
Copy link
Member

norswap commented Sep 18, 2023

Oh and I think you need to run make check to reformat the python code (and maybe make sure it actually runs on python files inside paymaster!

@eerkaijun eerkaijun merged commit 26c2c67 into master Sep 18, 2023
2 checks passed
@eerkaijun eerkaijun deleted the kai/account-abstraction branch September 24, 2023 11:07
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

Successfully merging this pull request may close these issues.

2 participants