Skip to content

Commit 2cf03bc

Browse files
committed
Ensure spending params are in the signed hash
1 parent 7889a58 commit 2cf03bc

File tree

1 file changed

+16
-13
lines changed

1 file changed

+16
-13
lines changed

src/LimitingPaymaster.sol

+16-13
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ contract LimitingPaymaster is BasePaymaster {
3838
* note that this signature covers all fields of the UserOperation, except the "paymasterAndData",
3939
* which will carry the signature itself.
4040
*/
41-
function getHash(UserOperation calldata userOp, uint48 validUntil, uint48 validAfter)
41+
function getHash(UserOperation calldata userOp, uint48 validUntil, uint48 validAfter, uint32 spentKey, uint96 spentMax, bool allowAnyBundler)
4242
public view returns (bytes32) {
4343
// can't use userOp.hash(), since it contains also the paymasterAndData itself.
4444
return keccak256(
@@ -55,7 +55,10 @@ contract LimitingPaymaster is BasePaymaster {
5555
block.chainid,
5656
address(this),
5757
validUntil,
58-
validAfter
58+
validAfter,
59+
spentKey,
60+
spentMax,
61+
allowAnyBundler
5962
)
6063
);
6164
}
@@ -66,18 +69,18 @@ contract LimitingPaymaster is BasePaymaster {
6669
* paymasterAndData[:20] : address(this)
6770
* paymasterAndData[20:26] : validUntil
6871
* paymasterAndData[26:32] : validAfter
69-
* paymasterAndData[32:97] : signature
70-
* paymasterAndData[97:101] : spendKey
71-
* paymasterAndData[101:113] : spendMax
72-
* paymasterAndData[113] : allowAnyBundler
72+
* paymasterAndData[32:36] : spendKey
73+
* paymasterAndData[36:48] : spendMax
74+
* paymasterAndData[48] : allowAnyBundler
75+
* paymasterAndData[49:114] : signature
7376
*/
7477
function _validatePaymasterUserOp(UserOperation calldata userOp, bytes32 /*userOpHash*/, uint256 requiredPreFund)
7578
internal view override returns (bytes memory context, uint256 validationData) {
76-
(uint48 validUntil, uint48 validAfter, bytes calldata signature, uint32 spentKey, uint96 spentMax, bool allowAnyBundler) = parsePaymasterAndData(userOp.paymasterAndData);
79+
(uint48 validUntil, uint48 validAfter, uint32 spentKey, uint96 spentMax, bool allowAnyBundler, bytes calldata signature) = parsePaymasterAndData(userOp.paymasterAndData);
7780
require(spent[spentKey] + requiredPreFund <= spentMax, "Paymaster: spender funds are depleted");
7881
// Only support 65-byte signatures, to avoid potential replay attacks.
7982
require(signature.length == 65, "Paymaster: invalid signature length in paymasterAndData");
80-
bytes32 hash = ECDSA.toEthSignedMessageHash(getHash(userOp, validUntil, validAfter));
83+
bytes32 hash = ECDSA.toEthSignedMessageHash(getHash(userOp, validUntil, validAfter, spentKey, spentMax, allowAnyBundler));
8184

8285
// don't revert on signature failure: return SIG_VALIDATION_FAILED
8386
if (verifyingSigner != ECDSA.recover(hash, signature)) {
@@ -100,13 +103,13 @@ contract LimitingPaymaster is BasePaymaster {
100103
}
101104

102105
function parsePaymasterAndData(bytes calldata paymasterAndData)
103-
internal pure returns(uint48 validUntil, uint48 validAfter, bytes calldata signature, uint32 spentKey, uint96 spentMax, bool allowAnyBundler) {
106+
internal pure returns(uint48 validUntil, uint48 validAfter, uint32 spentKey, uint96 spentMax, bool allowAnyBundler, bytes calldata signature) {
104107
validUntil = uint48(bytes6(paymasterAndData[20:26]));
105108
validAfter = uint48(bytes6(paymasterAndData[26:32]));
106-
signature = paymasterAndData[32:97];
107-
spentKey = uint32(bytes4(paymasterAndData[97:101]));
108-
spentMax = uint96(bytes12(paymasterAndData[101:113]));
109-
allowAnyBundler = paymasterAndData.length > 113 && paymasterAndData[113] > 0;
109+
spentKey = uint32(bytes4(paymasterAndData[32:36]));
110+
spentMax = uint96(bytes12(paymasterAndData[36:48]));
111+
allowAnyBundler = paymasterAndData[48] > 0;
112+
signature = paymasterAndData[49:];
110113
}
111114

112115
function renounceOwnership() public override view onlyOwner {

0 commit comments

Comments
 (0)