Paymaster Web Service Capability
Abstract
With EIP-5792, apps can communicate with wallets about advanced features via capabilities. This proposal defines a capability that allows apps to request that ERC-4337 wallets communicate with a specified paymaster web service. To support this, we also define a standardized API for paymaster web services.
Motivation
App developers want to start sponsoring their users’ transactions using paymasters. Paymasters are commonly used via web services. However, there is currently no way for apps to tell wallets to communicate with a specific paymaster web service. Similarly, there is no standard for how wallets should communicate with these services. We need both a way for apps to tell wallets to communicate with a specific paymaster web service and a communication standard for wallets to do so.
Specification
One new EIP-5792 wallet capability is defined. We also define a standard interface for paymaster web services as a prerequisite.
Paymaster Web Service Interface
We define two JSON-RPC methods to be implemented by paymaster web services.
pm_getPaymasterStubData
Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. Accepts an unsigned user operation, entrypoint address, chain id, and a context object. Paymaster service providers can define fields that app developers should use in the context object.
This method MAY return paymaster-specific gas values if applicable to the provided EntryPoint version. For example, if provided with EntryPoint v0.7, this method MAY return paymasterVerificationGasLimit
. Furthermore, for EntryPoint v0.7, this method MUST return a value for paymasterPostOpGasLimit
. This is because it is the paymaster that pays the postOpGasLimit, so it cannot trust the wallet to estimate this amount.
The wallet SHOULD use these provided gas values when submitting the UserOperation
to a bundler for gas estimation.
This method MAY also return a sponsor
object with a name
field and an optional icon
field. The name
field is the name of the party sponsoring the transaction, and the icon
field is a URI pointing to an image. Wallet developers MAY choose to display sponsor information to users. The icon
string MUST be a data URI as defined in [RFC-2397]. The image SHOULD be a square with 96x96px minimum resolution. The image format is RECOMMENDED to be either lossless or vector based such as PNG, WebP or SVG to make the image easy to render on the wallet. Since SVG images can execute Javascript, wallets MUST render SVG images using the <img>
tag to ensure no untrusted Javascript execution can occur.
There are cases where a call to just pm_getPaymasterStubData
is sufficient to provide valid paymaster-related user operation fields, e.g. when the paymasterData
does not contain a signature. In such cases, the second RPC call to pm_getPaymasterData
(defined below) MAY be skipped, by returning isFinal: true
by this RPC call.
Paymaster web services SHOULD do validations on incoming user operations during pm_getPaymasterStubData
execution and reject the request at this stage if it would not sponsor the operation.
pm_getPaymasterStubData
RPC Specification
Note that the user operation parameter does not include a signature, as the user signs after all other fields are populated.
// [userOp, entryPoint, chainId, context]
type GetPaymasterStubDataParams = [
// Below is specific to Entrypoint v0.6 but this API can be used with other entrypoint versions too
{
sender: `0x${string}`;
nonce: `0x${string}`;
initCode: `0x${string}`;
callData: `0x${string}`;
callGasLimit: `0x${string}`;
verificationGasLimit: `0x${string}`;
preVerificationGas: `0x${string}`;
maxFeePerGas: `0x${string}`;
maxPriorityFeePerGas: `0x${string}`;
}, // userOp
`0x${string}`, // EntryPoint
`0x${string}`, // Chain ID
Record<string, any> // Context
];
type GetPaymasterStubDataResult = {
sponsor?: { name: string; icon?: string }; // Sponsor info
paymaster?: string; // Paymaster address (entrypoint v0.7)
paymasterData?: string; // Paymaster data (entrypoint v0.7)
paymasterVerificationGasLimit?: `0x${string}`; // Paymaster validation gas (entrypoint v0.7)
paymasterPostOpGasLimit?: `0x${string}`; // Paymaster post-op gas (entrypoint v0.7)
paymasterAndData?: string; // Paymaster and data (entrypoint v0.6)
isFinal?: boolean; // Indicates that the caller does not need to call pm_getPaymasterData
};
pm_getPaymasterStubData
Example Parameters
[
{
"sender": "0x...",
"nonce": "0x...",
"initCode": "0x",
"callData": "0x...",
"callGasLimit": "0x...",
"verificationGasLimit": "0x...",
"preVerificationGas": "0x...",
"maxFeePerGas": "0x...",
"maxPriorityFeePerGas": "0x..."
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"0x2105",
{
// Illustrative context field. These should be defined by service providers.
"policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
}
]
pm_getPaymasterStubData
Example Return Value
Paymaster services MUST detect which entrypoint version the account is using and return the correct fields.
For example, if using entrypoint v0.6:
{
"sponsor": {
"name": "My App",
"icon": "https://..."
},
"paymasterAndData": "0x..."
}
If using entrypoint v0.7:
{
"sponsor": {
"name": "My App",
"icon": "https://..."
},
"paymaster": "0x...",
"paymasterData": "0x..."
}
If using entrypoint v0.7, with paymaster gas estimates:
{
"sponsor": {
"name": "My App",
"icon": "https://..."
},
"paymaster": "0x...",
"paymasterData": "0x...",
"paymasterVerificationGasLimit": "0x...",
"paymasterPostOpGasLimit": "0x..."
}
Indicating that the caller does not need to make a pm_getPaymasterData
RPC call:
{
"sponsor": {
"name": "My App",
"icon": "https://..."
},
"paymaster": "0x...",
"paymasterData": "0x...",
"isFinal": true
}
pm_getPaymasterData
Returns values to be used in paymaster-related fields of a signed user operation. These are not stub values and will be used during user operation submission to a bundler. Similar to pm_getPaymasterStubData
, accepts an unsigned user operation, entrypoint address, chain id, and a context object.
pm_getPaymasterData
RPC Specification
Note that the user operation parameter does not include a signature, as the user signs after all other fields are populated.
// [userOp, entryPoint, chainId, context]
type GetPaymasterDataParams = [
// Below is specific to Entrypoint v0.6 but this API can be used with other entrypoint versions too
{
sender: `0x${string}`;
nonce: `0x${string}`;
initCode: `0x${string}`;
callData: `0x${string}`;
callGasLimit: `0x${string}`;
verificationGasLimit: `0x${string}`;
preVerificationGas: `0x${string}`;
maxFeePerGas: `0x${string}`;
maxPriorityFeePerGas: `0x${string}`;
}, // userOp
`0x${string}`, // Entrypoint
`0x${string}`, // Chain ID
Record<string, any> // Context
];
type GetPaymasterDataResult = {
paymaster?: string; // Paymaster address (entrypoint v0.7)
paymasterData?: string; // Paymaster data (entrypoint v0.7)
paymasterAndData?: string; // Paymaster and data (entrypoint v0.6)
};
pm_getPaymasterData
Example Parameters
[
{
"sender": "0x...",
"nonce": "0x...",
"initCode": "0x",
"callData": "0x...",
"callGasLimit": "0x...",
"verificationGasLimit": "0x...",
"preVerificationGas": "0x...",
"maxFeePerGas": "0x...",
"maxPriorityFeePerGas": "0x..."
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"0x2105",
{
// Illustrative context field. These should be defined by service providers.
"policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
}
]
pm_getPaymasterData
Example Return Value
Paymaster services MUST detect which entrypoint version the account is using and return the correct fields.
For example, if using entrypoint v0.6:
{
"paymasterAndData": "0x..."
}
If using entrypoint v0.7:
{
"paymaster": "0x...",
"paymasterData": "0x..."
}
paymasterService
Capability
The paymasterService
capability is implemented by both apps and wallets.
App Implementation
Apps need to give wallets paymaster service URLs they can make the above RPC calls to. They can do this using the paymasterService
capability as part of an EIP-5792 wallet_sendCalls
call.
wallet_sendCalls
Paymaster Capability Specification
type PaymasterCapabilityParams = Record<
`0x${string}`, // Chain ID
{
url: string; // Paymaster service URL for provided chain ID
context: Record<string, any>; // Additional data defined by paymaster service providers
}
>;
wallet_sendCalls
Example Parameters
[
{
"version": "1.0",
"from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"calls": [
{
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"value": "0x9184e72a",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675",
"chainId": "0x01"
},
{
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"value": "0x182183",
"data": "0xfbadbaf01",
"chainId": "0x2105"
}
],
"capabilities": {
"paymasterService": {
"0x01": {
"url": "https://...",
"context": {
"policyId": "962b252c-a726-4a37-8d86-333ce0a07299"
}
},
"0x2105": {
"url": "https://...",
"context": {
"policyId": "0a268db9-3243-4178-b1bd-d9b67a47d37b"
}
}
}
}
}
]
The wallet will then make the above paymaster RPC calls to the URLs specified in the paymasterService
capability field.
Wallet Implementation
To conform to this specification, smart wallets that wish to leverage app-sponsored transactions:
- MUST indicate to apps that they can communicate with paymaster web services via their response to an EIP-5792
wallet_getCapabilities
call. - SHOULD make calls to and use the values returned by the paymaster service specified in the capabilities field of an EIP-5792
wallet_sendCalls
call. An example of an exception is a wallet that allows users to select a paymaster provided by the wallet. Since there might be cases in which the provided paymaster is ultimately not used—either due to service failure or due to a user selecting a different, wallet-provided paymaster—applications MUST NOT assume that the paymaster it provides to a wallet is the entity that pays for transaction fees.
wallet_getCapabilities
Response Specification
type PaymasterServiceCapability = {
supported: boolean;
};
wallet_getCapabilities
Example Response
{
"0x2105": {
"paymasterService": {
"supported": true
}
},
"0x14A34": {
"paymasterService": {
"supported": true
}
}
}
Below is a diagram illustrating the full wallet_sendCalls
flow, including how a wallet might implement the interaction.
Rationale
Gas Estimation
The current loose standard for paymaster services is to implement pm_sponsorUserOperation
. This method returns values for paymaster-related user operation fields and updated gas values. The problem with this method is that paymaster service providers have different ways of estimating gas, which results in different estimated gas values. Sometimes these estimates can be insufficient. As a result we believe it’s better to leave gas estimation up to the wallet, as the wallet has more context on how user operations will get submitted (e.g. which bundler they will get submitted to). Then wallets can ask paymaster services to sponsor given the estimates defined by the wallet.
The above reason is also why we specify that the pm_getPaymasterStubData
method MAY also return paymaster-specific gas estimates. I.e., bundlers are prone to insufficiently estimating the paymaster-specific gas values, and the paymaster servies themselves are ultimately better equipped to provide them.
Chain ID Parameter
Currently, paymaster service providers typically provide developers with a URL per chain. That is, paymaster service URLs are not typically multichain. So why do we need a chain ID parameter? We recognize that we must specify some constraint so that wallets can communicate with paymaster services about which chain their requests are for. As we see it, there are two options:
- Formalize the current loose standard and require that paymaster service URLs are 1:1 with chains.
- Require a chain ID parameter as part of paymaster service requests.
We feel that option (2) is the better abstraction here. This allows service providers to offer multichain URLs if they wish at essentially no downside to providers who offer a URL per chain. Providers who offer a URL per chain would just need to accept an additional parameter that they can ignore. When an app developer who uses a URL-per-chain provider wants to submit a request to a different chain, they can just swap out the URL accordingly.
Challenges With Stub Data
Enabling a workflow with greater flexibility in gas estimations will nonetheless come with some known challenges that paymaster services must be aware of in order to ensure reliable gas estimates are generated during the process.
preVerificationGas
The preVerificationGas
value is largely influenced by the size of the user operation and it’s ratio of zero to non-zero bytes. This can cause a scenario where pm_getPaymasterStubData
returns values that results in upstream gas estimations to derive a lower preVerificationGas
compared to what pm_getPaymasterData
would require. If this occurs then bundlers will return an insufficient preVerificationGas
error during eth_sendUserOperation
.
To avoid this scenario, a paymaster service MUST return stub data that:
- Is of the same length as the final data.
- Has an amount of zero bytes (
0x00
) that is less than or equal to the final data.
Consistent Code Paths
In the naive case, a stub value of repeating non-zero bytes (e.g. 0x01
) that is of the same length as the final value will generate a usable preVerificationGas
. Although this would immediately result in a gas estimation error given that the simulation will likely revert due to an invalid paymaster data.
In a more realistic case, a valid stub can result in a successful simulation but still return insufficient gas limits. This can occur if the stub data causes validatePaymasterUserOp
or postOp
functions to simulate a different code path compared to the final value. For example, if the simulated code was to return early, the estimated gas limits would be less than expected which would cause upstream out of gas
errors once a user operation is submitted to the bundler.
Therefore, a paymaster service MUST also return a stub that can result in a simulation executing the same code path compared to what is expected of the final user operation.
Security Considerations
The URLs paymaster service providers give to app developers commonly have API keys in them. App developers might not want to pass these API keys along to wallets. To remedy this, we recommend that app developers provide a URL to their app’s backend, which can then proxy calls to paymaster services. Below is a modified diagram of what this flow might look like.
This flow would allow developers to keep their paymaster service API keys secret. Developers might also want to do additional simulation / validation in their backends to ensure they are sponsoring a transaction they want to sponsor.
Copyright
Copyright and related rights waived via CC0.