Repository navigation
fix(paymaster): verify the signed hash is the expected outside execution - #1734
Merged
PhilippeR26 merged 1 commit intoOct 20, 2026
Merged
Conversation
The paymaster safety check read fields of the returned message, but the account signs a hash that covers only the fields the paymaster's own types list declares. A paymaster could therefore get one call array checked while a different, renamed array was the one actually signed, or get the user calls checked inside a message of another kind that was signed in their place. assertPaymasterTransactionSafety now rebuilds the outside execution it expects from the SDK's own SNIP-9 shape, the provider chain and the user calls, and refuses the transaction when the hash the account would sign differs from the one the paymaster returned. When the user passes timeBounds, the signed execution window must now stay inside the requested one. The check also moved into preparePaymasterTransaction, so a flow built on the lower-level methods is protected too: it now takes the user calls and the fee details, and executePaymasterTransaction delegates to it. This changes the signature of preparePaymasterTransaction; see the v10 to v11 migration guide, section 14.
Contributor
|
🎉 This PR is included in version 11.4.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
5 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and Resolution
When paying through a SNIP-29 paymaster, the SDK receives a SNIP-9 typed data to sign, checks it, then signs it. The check read fields of the returned message — but the account signs a hash, and that hash covers only the fields declared in the message's own
typeslist, which the paymaster controls. So a malicious or compromised paymaster could get one call array checked while a different, renamed array was the one actually signed, or get the user's calls checked inside a message of another kind (for example a session key) that was signed in their place. The caller and the time bounds were not checked either.assertPaymasterTransactionSafetynow rebuilds the outside execution it expects — from the SDK's own SNIP-9 shape, the provider chain and the user's own calls — and refuses the transaction when the hash the account would sign differs from the one the paymaster returned. When the user passestimeBounds, the signed execution window must stay inside the requested one. The check also moved intopreparePaymasterTransaction, so a flow built on the lower-level methods is protected too. This continues the hardening of the paymaster check started in 10.7.2 (#1694).RPC version (if applicable)
No change. RPC spec versions 0.9 and 0.10.x remain supported.
Usage related changes
timeBounds, the signed execution window must stay inside the requested one, or the transaction is refused. WithouttimeBoundsthe window is not constrained.preparePaymasterTransactionnow takes(preparedTransaction, calls, paymasterDetails, maxFeeInGasToken?)and runs the safety check itself. Only code calling this lower-level method directly is affected;executePaymasterTransactionis unchanged. Documented in the v10 to v11 migration guide, section 14.Development related changes
__tests__/accountPaymaster.test.ts: the base typed data is now a real SNIP-9 outside execution, and four cases were added — a renamed signed field, a foreign message, an execution window past the requested bounds, and a window inside them.Checklist: