Reach out for an audit or to learn more about Macro
or Message on Telegram

Stablecoin.xyz A-1

ERC-4337 Paymaster (EntryPoint v7)

Security Audit

June 12, 2025

Version 1.0.0

Presented by 0xMacro

Table of Contents

Introduction

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.

Overall Assessment

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.

Specification

Our understanding of the specification was based on the following sources:

Source Code

The following source code was reviewed during the audit:

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.

Issue Descriptions and Recommendations

Click on an issue to jump to it, or scroll down to see them all.

Security Level Reference

We quantify issues in three parts:

  1. The high/medium/low/spec-breaking impact of the issue:
    • How bad things can get (for a vulnerability)
    • The significance of an improvement (for a code quality issue)
    • The amount of gas saved (for a gas optimization)
  2. The high/medium/low likelihood of the issue:
    • How likely is the issue to occur (for a vulnerability)
  3. The overall critical/high/medium/low severity of the issue.

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.

Issue Details

C-1

Paymaster signatures can be reused

Topic
Signature Replay
Status
Impact
Critical
Likelihood
High

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.

H-1

Signatures never expire

Topic
Signature expiry
Status
Impact
High
Likelihood
High

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.

M-1

Paymaster accepts any gas cost

Topic
Griefing
Status
Impact
Medium
Likelihood
High

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.

L-1

Does not follow EIP712 signature pattern

Topic
Standards
Status
Impact
Low
Likelihood
Low

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.

Disclaimer

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.