-
Notifications
You must be signed in to change notification settings - Fork 25
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
Conversation
2070b2c
to
949c100
Compare
b7ebfe8
to
88714a5
Compare
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.
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)? |
{ | ||
"compilerOptions": { | ||
"target": "ES6", | ||
"module": "commonjs", |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me double check
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
help="disable ANSI escape codes for terminal manipulation", | ||
default=True, | ||
dest="use_ansi_esc", | ||
action="store_false") |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) |
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) |
7c883b4
to
9a0c218
Compare
@norswap okie made some changes, have also now tested it with L2 |
help="disable ANSI escape codes for terminal manipulation", | ||
default=True, | ||
dest="use_ansi_esc", | ||
action="store_false") |
There was a problem hiding this comment.
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?
help="disable ANSI escape codes for terminal manipulation", | ||
default=True, | ||
dest="use_ansi_esc", | ||
action="store_false") |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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.
There was a problem hiding this 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. |
There was a problem hiding this comment.
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:
- Users submit userOp to mempool (functionally equivalent, as its a RPC call, just that here we don't put it in the mempool)
- Batchers pick userOp, check if they can be sponsored (using the whole 4337 dance) and if so submit them.
- 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:
- Send to server that is both a paymaster & bundler. (Optionally drop to mempool, no need for now.)
- 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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes exactly!
} catch (error) { | ||
console.error('Error reading/parsing JSON file:', error); | ||
} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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}") | ||
|
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
); | ||
|
||
/// NOTE: Define additional transaction sponsor logic here if needed | ||
/// For example, it could be a list of whitelisted addresses |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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
log_file = open(log_file_path, "w") | ||
PROCESS_MGR.start( | ||
"start bundler", | ||
"stackup-bundler start --mode private", |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. | ||
""" |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
Oh and I think you need to run |
Work in progress for spinning up ERC4337 bundler and paymaster.