ERC-4337 Paymaster (EntryPoint v7)
Security Audit
June 12, 2025
Version 1.0.0
Presented by 0xMacro
This document includes the results of the security audit for SBC's smart contract code as found in the section titled ‘Source Code’. The security audit was performed by the Macro security team on May 28th to May 30th, 2025.
The purpose of this audit is to review the source code of certain SBC Solidity contracts, and provide feedback on the design, architecture, and quality of the source code with an emphasis on validating the correctness and security of the software in its entirety.
Disclaimer: While Macro’s review is comprehensive and has surfaced some changes that should be made to the source code, this audit should not solely be relied upon for security, as no single audit is guaranteed to catch all possible bugs.
The following is an aggregation of issues found by the Macro Audit team:
| Severity | Count | Acknowledged | Won't Do | Addressed |
|---|---|---|---|---|
| Critical | 1 | - | - | 1 |
| High | 1 | - | - | 1 |
| Medium | 1 | - | - | 1 |
| Low | 1 | - | - | 1 |
SBC was quick to respond to these issues.
Our understanding of the specification was based on the following sources:
The following source code was reviewed during the audit:
a0d722dbf1181e6aa95e2ae13bc36fe9e67e701b
| Source Code | SHA256 |
|---|---|
| contracts/SignatureVerifyingPaymasterV07.sol |
|
Note: This document contains an audit solely of the Solidity contracts listed above. Specifically, the audit pertains only to the contracts themselves, and does not pertain to any other programs or scripts, including deployment scripts.
Click on an issue to jump to it, or scroll down to see them all.
We quantify issues in three parts:
This third part – the severity level – is a summary of how much consideration the client should give to fixing the issue. We assign severity according to the table of guidelines below:
| Severity | Description |
|---|---|
|
(C-x) Critical |
We recommend the client must fix the issue, no matter what, because not fixing would mean significant funds/assets WILL be lost. |
|
(H-x) High |
We recommend the client must address the issue, no matter what, because not fixing would be very bad, or some funds/assets will be lost, or the code’s behavior is against the provided spec. |
|
(M-x) Medium |
We recommend the client to seriously consider fixing the issue, as the implications of not fixing the issue are severe enough to impact the project significantly, albiet not in an existential manner. |
|
(L-x) Low |
The risk is small, unlikely, or may not relevant to the project in a meaningful way. Whether or not the project wants to develop a fix is up to the goals and needs of the project. |
|
(Q-x) Code Quality |
The issue identified does not pose any obvious risk, but fixing could improve overall code quality, on-chain composability, developer ergonomics, or even certain aspects of protocol design. |
|
(I-x) Informational |
Warnings and things to keep in mind when operating the protocol. No immediate action required. |
|
(G-x) Gas Optimizations |
The presented optimization suggestion would save an amount of gas significant enough, in our opinion, to be worth the development cost of implementing it. |
The paymaster is intended to cover gas costs for users that meet specific requirements, like holding their SBC token for a sufficient duration. The flow is that a user makes a request for a transaction and the paymaster generates a signature and accompanying paymaster data. However, the signature only includes the sender address, and its validUntil and validAfter parameters. It does not contain a nonce, nor does it contain the intended transactions calldata.
function getHash(
uint48 validUntil,
uint48 validAfter,
address paymasterAddress,
address senderAddress
) public view returns (bytes32) {
return keccak256(abi.encode(
validUntil,
validAfter,
block.chainid,
paymasterAddress,
senderAddress
));
}
Reference: SignatureVerifyingPaymasterV07.sol#L125-138
Since nonce and calldata are not included, it allows for the sender to reuse this paymaster signature repeatedly on any transaction. This can allow for users to exploit or grief the ETH held in the paymaster, draining it with no cost to themselves.
Remediations to Consider
Add the operations nonce and calldata found in the PackedUserOperation struct to the signature to prevent replay attacks, and potentially limit the calls the paymaster will cover.
As mentioned in C-1, the paymaster signature is composed of the sender address, and validUntil and validAfter parameters. The intent of these validity parameters is to create a range of time the signature is valid for. However, after signature validation these values are adjusted to ensure they are always valid and thus the signature will never expire:
/**
* TIMESTAMP ADJUSTMENT MECHANISM
*
* This section implements automatic adjustments to the validity window timestamps
* to prevent common validation errors. These adjustments happen AFTER signature
* verification is complete, so they don't affect the cryptographic validation.
*
* The original timestamps from paymasterData were used to verify the signature.
* Now we may modify them before returning to the EntryPoint.
*/
// Convert current block timestamp to uint48 for comparison with our timestamps
uint48 now48 = uint48(block.timestamp);
// EXPIRED TIMESTAMP HANDLING:
// If validUntil is in the past or too close to now, extend it
// This prevents "AA32 paymaster expired" errors
if (validUntil <= now48 || validUntil < now48 + 60) {
validUntil = now48 + 3600; // Add 1 hour from now
}
// FUTURE ACTIVATION HANDLING:
// If validAfter is in the future, adjust it to be valid now
// This prevents "AA32 paymaster not due" errors
if (validAfter > now48) {
validAfter = now48 > 60 ? now48 - 60 : 0; // Set to 60 seconds in the past
}
Reference: SignatureVerifyingPaymasterV07.sol#L197-223
Without the ability for paymaster signatures to expire, once a signature has been given to a user, they are able to have the paymaster cover their gas costs indefinitely which may not be the intent. It is likely that some users could abuse this especially coupled with issue C-1.
Remediations to Consider
Remove the timestamp adjustment mechanism, the entrypoint should not return errors if valid timestamps are given. It is important for signatures to expire to prevent griefing.
Considering the paymaster signature currently does not consider the users calldata, any transaction from the user can be executed using the paymaster signature and the paymaster will cover the gas costs. This allows for potentially malicious high gas transactions to be accepted and drain the paymaster of its ETH.
Remediations to Consider
Consider using the maxGas value passed into the validateUserOp() function, and validate it against a set max gas value the paymaster is willing to execute on.
It is best practice to follow EIP712 signature pattern for smart contract. This pattern is used to ensure the signature is limited to only a specific contract, version, and chain id, so it cannot be used by any unintended ways. For the most part this is handled, however the version is missing from the signature which is important considering the paymaster is upgradeable, and you may not want signatures to be valid after an upgrade.
Remediations to Consider
Update the signature to follow EIP712, Openzepelin’s EIP712 is suggested to inherit to generate the proper domain separator.
Macro makes no warranties, either express, implied, statutory, or otherwise, with respect to the services or deliverables provided in this report, and Macro specifically disclaims all implied warranties of merchantability, fitness for a particular purpose, noninfringement and those arising from a course of dealing, usage or trade with respect thereto, and all such warranties are hereby excluded to the fullest extent permitted by law.
Macro will not be liable for any lost profits, business, contracts, revenue, goodwill, production, anticipated savings, loss of data, or costs of procurement of substitute goods or services or for any claim or demand by any other party. In no event will Macro be liable for consequential, incidental, special, indirect, or exemplary damages arising out of this agreement or any work statement, however caused and (to the fullest extent permitted by law) under any theory of liability (including negligence), even if Macro has been advised of the possibility of such damages.
The scope of this report and review is limited to a review of only the code presented by the SBC team and only the source code Macro notes as being within the scope of Macro’s review within this report. This report does not include an audit of the deployment scripts used to deploy the Solidity contracts in the repository corresponding to this audit. Specifically, for the avoidance of doubt, this report does not constitute investment advice, is not intended to be relied upon as investment advice, is not an endorsement of this project or team, and it is not a guarantee as to the absolute security of the project. In this report you may through hypertext or other computer links, gain access to websites operated by persons other than Macro. Such hyperlinks are provided for your reference and convenience only, and are the exclusive responsibility of such websites’ owners. You agree that Macro is not responsible for the content or operation of such websites, and that Macro shall have no liability to your or any other person or entity for the use of third party websites. Macro assumes no responsibility for the use of third party software and shall have no liability whatsoever to any person or entity for the accuracy or completeness of any outcome generated by such software.