-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
Update EIP-5792: Propose changes to EIP-5792 #8826
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,33 +35,41 @@ We also define one capability expression for use with the fourth method, which f | |
|
||
### `wallet_sendCalls` | ||
|
||
Requests that a wallet submits a batch of calls. `from` and `chainId` are top-level properties rather than per-call properties because all calls should be sent from the same sender and on the same chain, identified by [EIP-155](./eip-155.md) integers expressed in hexadecimal notation. The items in the `calls` field are only those that are shared by all transaction types. Any other fields that a wallet may need to submit a transaction should be handled by the wallet. | ||
Requests that a wallet submits a batch of calls. `from` and `chainId` are top-level properties rather than per-call properties because all calls should be sent from the same sender and on the same chain, identified by [EIP-155](./eip-155.md) integers expressed in hexadecimal notation. | ||
The items in the `calls` field are simple `{to, data, value}` tuples. | ||
|
||
The capabilities field is how an app can communicate with a wallet about capabilities a wallet supports. For example, this is where an app can specify a paymaster service URL from which an [ERC-4337](./eip-4337.md) wallet can request a `paymasterAndData` input for a user operation. | ||
|
||
Each capability defined in the "capabilities" member can define global or call specific fields. | ||
These fields are set inside this capability's entry in the `capabilitiesData` object. | ||
Each entity in the `calls` field may contain an optional `capabilitiesData` object. | ||
This object allows the applications to attach a capability-specific metadata to individual calls. | ||
|
||
The wallet: | ||
|
||
* MUST send the calls in the order specified in the request | ||
* MUST send the calls on the same chain identified by the call's `chainId` property | ||
* MUST send the calls on the same chain identified by the request's `chainId` property | ||
* MUST NOT await for any calls to be finalized to complete the batch | ||
* MUST NOT send any calls from the request if the user rejects the request | ||
* MAY revert all calls if any call fails | ||
* MAY send all calls as part of one or more transactions, depending on wallet capability | ||
* SHOULD stop executing the calls if any call fails | ||
* MAY reject the request if the from address does not match the enabled account | ||
* MAY reject the request if one or more calls in the batch is expected to fail, when simulated sequentially | ||
* MUST reject the request if it contains a `capability` that is not supported by the wallet | ||
|
||
#### `wallet_sendCalls` RPC Specification | ||
|
||
```typescript | ||
type SendCallsParams = { | ||
version: string; | ||
from: `0x${string}`; | ||
chainId: `0x${string}` | undefined; // Hex chain id | ||
calls: { | ||
to?: `0x${string}` | undefined; | ||
data?: `0x${string}` | undefined; | ||
value?: `0x${string}` | undefined; // Hex value | ||
chainId?: `0x${string}` | undefined; // Hex chain id | ||
capabilitiesData?: Record<string, any> | undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, I don't love how this kind of muddles the simplicity of the calls struct. Is the complaint that capabilities makes it hard to specify info on a per call basis. Could we get a better example of when this would be needed 🤔
This should be possible with a general capability? Or you're saying one call in particular can revert? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't have to implement it, if our wallet doesn't have any per-call capability. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that pretty much sums it up. Of course, the
In my example, yes, I was talking about an "atomic" batch with a single "optional" call that should not revert the batch; Does this make sense? Or should dapps never need to go to the trouble of defining per-call parameters for a |
||
}[]; | ||
capabilities?: Record<string, any> | undefined; | ||
}; | ||
|
@@ -76,18 +84,17 @@ type SendCallsResult = string; | |
{ | ||
"version": "1.0", | ||
"from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", | ||
"chainId": "0x01", | ||
"calls": [ | ||
{ | ||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", | ||
"value": "0x9184e72a", | ||
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", | ||
"chainId": "0x01", | ||
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" | ||
}, | ||
{ | ||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", | ||
"value": "0x182183", | ||
"data": "0xfbadbaf01", | ||
"chainId": "0x01", | ||
"data": "0xfbadbaf01" | ||
} | ||
], | ||
"capabilities": { | ||
|
@@ -102,17 +109,24 @@ type SendCallsResult = string; | |
|
||
##### `wallet_sendCalls` Example Return Value | ||
|
||
The identifier can be any string. The only requirement is that for a given session, users should be able to call `wallet_getCallsStatus` with this value and expect a call-batch status to be returned. | ||
The identifier MUST be unique 64 bytes represented as a hex encoded string. | ||
For a given session, users should be able to call `wallet_getCallsStatus` with this value and expect a call-batch status | ||
to be returned. | ||
|
||
```json | ||
"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" | ||
"0x00000000000000000000000000000000000000000000000000000000000000000e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" | ||
``` | ||
|
||
### `wallet_getCallsStatus` | ||
|
||
Returns the status of a call batch that was sent via `wallet_sendCalls`. The identifier of the transaction is the value returned from the `wallet_sendCalls` RPC. Note that this method only returns a subset of the fields that `eth_getTransactionReceipt` returns, excluding any fields that may differ across wallet implementations. | ||
Returns the status of a call batch that was sent via `wallet_sendCalls`. | ||
The identifier of the batch is the value returned from the `wallet_sendCalls` RPC. | ||
Note that the `receipts` objects of this method's response is a strict subset of the object returned by `eth_getTransactionReceipt`. | ||
|
||
The `capabilitiesResultsData` object allows the wallets to attach a capability-specific metadata to the response. | ||
|
||
* If a wallet does not execute multiple calls atomically (i.e. in multiple transactions), the receipts in the `receipts` field MUST be in order of the calls sent. | ||
* The receipts in the `receipts` field MUST be in order they are included on-chain. | ||
* The `receipts` field MUST contain receipts for all calls in a batch that were included on-chain, including reverted ones. | ||
* If a wallet executes multiple calls atomically (i.e. in a single transaction), `wallet_getCallsStatus` MUST return a single receipt, corresponding to the transaction in which the calls were included. | ||
* The `logs` in the receipt objects MUST only include logs relevant to the calls submitted using `wallet_sendCalls`. For example, in the case of a transaction submitted onchain by an [ERC-4337](./eip-4337.md) bundler, the logs must only include those relevant to the user operation constructed using the calls submitted via `wallet_sendCalls`. I.e. the logs should not include those from other unrelated user operations submitted in the same bundle. | ||
|
||
|
@@ -122,38 +136,55 @@ Returns the status of a call batch that was sent via `wallet_sendCalls`. The ide | |
type GetCallsParams = string; | ||
|
||
type GetCallsResult = { | ||
status: 'PENDING' | 'CONFIRMED'; | ||
batchId: `0x${string}`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. callsId There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should keep the name the same |
||
chainId?: `0x${string}`; | ||
batchStatus: string; // See "Status Codes" | ||
receipts?: { | ||
logs: { | ||
address: `0x${string}`; | ||
data: `0x${string}`; | ||
topics: `0x${string}`[]; | ||
}[]; | ||
status: `0x${string}`; // Hex 1 or 0 for success or failure, respectively | ||
chainId: `0x${string}`; | ||
blockHash: `0x${string}`; | ||
blockNumber: `0x${string}`; | ||
gasUsed: `0x${string}`; | ||
transactionHash: `0x${string}`; | ||
}[]; | ||
capabilitiesResultsData?: Record<string, any> | undefined; | ||
}; | ||
``` | ||
|
||
##### Status Codes for `batchStatus` field | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should define what a batch is first? it's the first time we use it |
||
|
||
The purpose of the `batchStatus` field is to provide a short summary of the current status of the batch. | ||
It provides some off-chain context to the array of inner transaction `receipts`. | ||
|
||
| Name | Description | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this idea, thoughts @jxom @lukasrosario ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i think this makes sense if we're talking about single chain. would be tough if multichain. also not clear to me where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it sounds similar to "FAILURE" but the difference I had in mind was that a "FAILURE" means the action failed due to on-chain reverts, and "DISCARDED" means the wallet gave up on trying to send the batch to the network. One example could be a batch that specifies a The wallet cannot block the Another example is a UserOperation that has some condition in its validation function. Canonical ERC-4337 limits this use-case a lot, but does not completely prevent it. A wallet may want to accept a batch that is not yet valid, check if it has become valid during the next 2 hours, and mark the entire operation as "DISCARDED" after that. Does this makes sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yet another example of "discarded": assume you put a maxFeePerGas that is too low, and after sometime , because the network gas raised, the maxFee becomes too low below the baseFee, and the transaction is completely dropped. If we are sending a "sequence", it can happen to any transaction along the sequence. |
||
|-----------|----------------------------------------------------------------------------------------------------------------------| | ||
| PENDING | Batch has been received by the wallet but has not completed execution on-chain | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do you feel about possibly expanding For example
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @forshtat would be good to get your thoughts here. imo @ajhodges i'm not a huge fan of the ambiguity for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if we just left it as |
||
| SUCCESS | Batch has been included on-chain without reverts, receipts array contains info of all calls | | ||
| PARTIAL | Batch has been included on-chain only partially, the rest is either reverted or invalid. Receipts array contains info of all on-chain included calls | | ||
| FAILURE | Batch has been invalid or reverted completely and only changes related to gas charge may have been included on-chain | | ||
| DISCARDED | Batch has not been included on-chain and wallet will not retry | | ||
|
||
##### `wallet_getCallsStatus` Example Parameters | ||
|
||
As with the return value of `wallet_sendCalls`, the batch identifier may be any string. | ||
The batch identifier is a unique 64 bytes represented as a hex encoded string returned from `wallet_sendCalls`. | ||
|
||
```json | ||
[ | ||
"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" | ||
"0x00000000000000000000000000000000000000000000000000000000000000000e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" | ||
] | ||
``` | ||
|
||
##### `wallet_getCallsStatus` Example Return Value | ||
|
||
```json | ||
{ | ||
"status": "CONFIRMED", | ||
"chainId": "0x01", | ||
"batchId": "0x00000000000000000000000000000000000000000000000000000000000000000e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", | ||
"batchStatus": "SUCCESS", | ||
"receipts": [ | ||
{ | ||
"logs": [ | ||
|
@@ -166,7 +197,6 @@ As with the return value of `wallet_sendCalls`, the batch identifier may be any | |
} | ||
], | ||
"status": "0x1", | ||
"chainId": "0x01", | ||
"blockHash": "0xf19bbafd9fd0124ec110b848e8de4ab4f62bf60c189524e54213285e7f540d4a", | ||
"blockNumber": "0xabcd", | ||
"gasUsed": "0xdef", | ||
|
@@ -198,7 +228,7 @@ This method accepts a call bundle identifier returned by a `wallet_sendCalls` ca | |
|
||
### `wallet_getCapabilities` | ||
|
||
This RPC allows an application to request capabilities from a wallet (e.g. batch transactions, paymaster communication), without distinct discovery and permission requests. For more on the difference between requesting capabilities and discoverying features, see the ["Privacy Considerations" section](#privacy-considerations). | ||
This RPC allows an application to request capabilities from a wallet (e.g. batch transactions, paymaster communication), without distinct discovery and permission requests. For more on the difference between requesting capabilities and discovering features, see the ["Privacy Considerations" section](#privacy-considerations). | ||
|
||
This method SHOULD return an error if the user has not already authorized a connection between the application and the requested address. | ||
|
||
|
@@ -212,17 +242,18 @@ If any of these supplemental expressions of capabilities are contradicted by cap | |
|
||
Capabilities are returned in key/value pairs, with the key naming a capability and a value conforming to a shape defined for that name, in an object keyed to the relevant [EIP-155](./eip-155.md) `chainId` expressed in hexadecimal notation. | ||
Capabilities are nested in per-chain objects because wallets may support different capabilities across multiple chains authorized in a given session. | ||
All values for the "capability" fields MUST contain Semantic Version value `version`. | ||
|
||
```typescript | ||
type GetCapabilitiesParams = [`0x${string}`]; // Wallet address | ||
type GetCapabilitiesParams = [`0x${string}`, [`0x${string}`]]; // Wallet address, array of queried chain ids (optional) | ||
|
||
type GetCapabilitiesResult = Record<`0x${string}`, <Record<string, any>>; // Hex chain id | ||
``` | ||
|
||
##### `wallet_getCapabilities` Example Parameters | ||
|
||
```json | ||
["0xd46e8dd67c5d32be8058bb8eb970870f07244567"] | ||
["0xd46e8dd67c5d32be8058bb8eb970870f07244567", ["0x2105", "0x14A34"]] | ||
``` | ||
|
||
##### `wallet_getCapabilities` Example Return Value | ||
|
@@ -233,15 +264,15 @@ The capabilities below are for illustrative purposes. | |
{ | ||
"0x2105": { | ||
"paymasterService": { | ||
"supported": true | ||
"version": "1.0.0" | ||
}, | ||
"sessionKeys": { | ||
"supported": true | ||
"version": "1.0.0" | ||
} | ||
}, | ||
"0x14A34": { | ||
"paymasterService": { | ||
"supported": true | ||
"version": "1.0.0" | ||
} | ||
} | ||
} | ||
|
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.
Hmm I wonder if this is best 🤔 . E.g. why not allow specifying a paymaster which the app would like to be used but is not a necessity.
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.
How a wallet can tell if the cap (e.g. paymaster) is required by this app or optional?
The app can always query the wallet caps first, to decide how to continue.
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.
capability specs should specify whether they're required or not / how optional ones are handled. paymaster, for example, notes that the app must not assume the provided paymaster service is the one that ends up being used.
so if an app wants the capability to be required, the capability spec should include a
required
field, and that is how a wallet can tell if the capability is required by the app. i dont think we should blanket all capabilities to be required by default.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.
There are two issues raised here:
There is a
wallet_getCapabilities
method that provides the application with a list of supported "capabilities".Shouldn't the app that ignores that list and submits a
wallet_sendCalls
with a "capability" that is not supported be considered to behave incorrectly? I think it should reject the call, which would prevent dapps from yoloing theirsendCalls
requests.As the API defined here deals with real value and assets, making capabilities
required
by default makes a lot of sense to me. If using a paymaster for a transaction is indeed optional, it is trivial to specifyoptional: true
, but forgetting to setrequired: true
for a security-oriented capability may be catastrophic.What do you think?
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.
So @forshtat is the conclusion here that each capability could have an
optional:
flag? But passing a capability the wallet doesn't have would be hard failure?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.
In my personal opinion, yes, this seems pretty reasonable.