News | OpenZeppelin

EVM SVM Sponsored Mint Burn Audit

Written by OpenZeppelin Security | March 2, 2026

Summary

Type: Cross Chain
Timeline: From 2026-01-07 → To 2026-01-20
Languages: Solidity, Rust

Findings
Total issues: 11 (9 resolved)
Critical: 0 (0 resolved) · High: 0 (0 resolved) · Medium: 2 (2 resolved) · Low: 6 (4 resolved)

Notes & Additional Information
3 notes raised (3 resolved)

Scope

OpenZeppelin performed an audit of the changes made to the across-protocol/contracts repository. The scope consisted of the SVM and EVM parts.

The SVM part included a diff audit of the changes introduced to the following files in pull request #1225 at commit e3187f9:

 programs/
└── sponsored-cctp-src-periphery/
    └── src/
        ├── instructions/
        │   └── deposit.rs
        ├── utils/
        │   └── sponsored_cctp_quote.rs
        └── event.rs

The EVM part included a diff audit of the changes introduced to the following files in pull request #1218 at commit c4dd51e:

 contracts/
├── external/
│   ├── interfaces/
│   │   └── ICoreDepositWallet.sol
│   └── libraries/
│       └── BytesLib.sol
├── handlers/
│   └── HyperliquidDepositHandler.sol
├── interfaces/
│   └── SponsoredCCTPInterface.sol
├── libraries/
│   ├── HyperCoreLib.sol
│   └── SponsoredCCTPQuoteLib.sol
└── periphery/
    └── mintburn/
        ├── ArbitraryEVMFlowExecutor.sol
        ├── HyperCoreFlowExecutor.sol
        ├── Structs.sol
        ├── SwapHandler.sol
        ├── sponsored-cctp/
        │   ├── SponsoredCCTPDstPeriphery.sol
        │   └── SponsoredCCTPSrcPeriphery.sol
        └── sponsored-oft/
            ├── ComposeMsgCodec.sol
            ├── DstOFTHandler.sol
            ├── QuoteSignLib.sol
            ├── SponsoredOFTSrcPeriphery.sol
            └── Structs.sol

The changes introduced to the following files in pull request #1227 at commit 92f9e84 were also diff-audited:

 contracts/
├── external/
│   └── libraries/
│       └── OFTCoreMath.sol
├── interfaces/
│   └── IOFT.sol
└── periphery/
    └── mintburn/
        └── sponsored-oft/
            ├── ComposeMsgCodec.sol
            ├── DstOFTHandler.sol
            ├── QuoteSignLib.sol
            ├── SponsoredOFTSrcPeriphery.sol
            └── Structs.sol

During the review, several issues and code improvements were also identified within the broader cross-chain CCTP and OFT mechanisms. These findings apply to out-of-scope code and are labeled accordingly.

System Overview

SVM Scope

The changes under review include the introduction of the destination_dex and account_creation_mode parameters to the SponsoredCCTPQuote struct, which is passed to the deposit_for_burn instruction that is responsible for initiating the process of bridging tokens through the CCTP v2 protocol. This change increases the size of:

  • the data needed to encode the deposit_for_burn instruction call by 5 bytes (4 for destination_dex and 1 for account_creation_mode)
  • the hook_data passed to the TokenMessengerMinterV2 program, which in turn increases the size of the message_sent_event_data account. This also increases the amount of rent that must be deposited by the rent_fund account for the message_sent_event_data account creation

EVM Scope

Pull request #1218 notably enables using in-flight funds to sponsor account creation for previously non-existent users on HyperCore during standard HyperEVM → HyperCore transfer flows (i.e. executeFlow and executeSimpleTransferFlow) or the two-step finalizeSwapFlows. The user will receive the expected amount on HyperCore, minus the quote token debited by HyperLiquid for account creation. The pull request also expands functionality to allow transfers to HyperCore venues beyond the spot DEX and adds explicit support for USDC bridging via the ICoreDepositWallet.

Pull request #1227 introduces support for OFT tokens with non-zero bridge fees. This is achieved by specifying a minimum amount of tokens to receive on destination chain, calculated based on the fee imposed on a given token. On the destination chain, in order to obtain the fee which has actually been taken, an additional amountSentSD parameter has been added to the OFT message, which represents the amount of a token which has been deposited before taking the fees. The token amount actually received by the destination contract is then subtracted from this parameter, after scaling the parameter by the relevant decimals number. The result of this operation is subsequently used to calculate the amount of tokens to sponsor.

Security Model and Trust Assumptions

In addition to the trust assumptions listed in the previous audit report of this codebase, the following trust assumptions were identified:

  • Only quotes with correct destination DEX and correct account creation mode value will be signed by the signer entity, before depositing assets on the source blockchain. Failure to do so may result in loss of funds due to:
    • HyperFlow → HyperCore transfers to unsupported DEXes succeeding in the HyperEVM but failing to transfer the funds to HyperCore
    • the conversion to AccountCreationMode will revert if accountCreationMode is outside of the allowed enum values
  • Only ERC-20 tokens which implement the decimals function (which is optional in this standard) will be used when transferring through OFT.
  • The OftMessengers for tokens with bridge fees are correctly implemented (i.e., they subtract the bridging fee and dust (where applicable) and enforce the minimum amount to be respected).

Privileged Roles

The in-scope changes do not introduce any new privileged roles.

 

Medium Severity

[Out of Scope] Sponsorship and Custom EVM Actions May Be Skipped

The receiveMessage function of the SponsoredOFTSrcPeriphery contract is called whenever a CCTP message is received on HyperEVM. It receives tokens from the Message Transmitter, validates it based on the provided signature, and executes custom EVM actions depending on the execution mode.

However, the _isQuoteValid function used for validation of the provided quote does not revert when the provided quote is invalid. This allows for a griefing attack where an attacker calls receiveMessage on behalf of another user but deliberately provides an incorrect signature. As a result, the quote will be treated as invalid, which causes the sponsored amount to be set to 0 and any custom EVM actions to be skipped for that user's message.

Consider reverting in case the signature provided for a given quote is invalid and the deadline of the quote has not yet been exceeded.

Update: Resolved in pull request #1255.

[Out of Scope] Unauthenticated Slippage Parameter Can Force DonationBox Subsidies

In the sponsored OFT flow, the quote validation only authenticates SignedQuoteParams, while the caller-controlled maxUserSlippageBps remains unsigned. Although this value is embedded in the cross-chain composeMsg and used on the destination chain during swap execution, it can be modified after quote signing without invalidating the signature.

On the destination, maxUserSlippageBps directly affects the computed minAmountToSend for non-sponsored swaps. By lowering this value, a caller can inflate the minimum required output, increasing the likelihood that additionalToSend is required during settlement. The protocol then unconditionally withdraws funds from DonationBox to cover the shortfall. This allows callers to bypass relayer or signer slippage policies, potentially draining DonationBox liquidity or rendering swaps unfinalizable if subsidies are unavailable or refused.

Consider enforcing maxUserSlippageBps on the destination chain by including it in the signed quote parameters. If leaving it unsigned is intentional (to allow recovery from extreme slippage), consider enforcing a bounded range within which maxUserSlippageBps may be adjusted instead of allowing it to be arbitrarily modified.

Update: Resolved in pull request #1288. The team stated:

To mitigate the likelihood of an attack involving price manipulation, we decided to include maxUserSlippageBps as a signed param.
The user can still choose the param, but the API will make sure that the param makes sense for the current market conditions.

Low Severity

Using BytesLib for ABI-Encoded Data

The codebase uses the BytesLib library to read uint8, uint16, and uint32 values from arbitrary byte offsets. However, BytesLib was designed for tightly packed data (i.e., created through abi.encodePacked), while most messages in the codebase are encoded using abi.encode. To compensate, decoding functions such as _getAccountCreationMode rely on calling BytesLib with magic offsets that account for the 0-byte paddings. This makes the logic harder to follow and increases the risk of future decoding errors if message layouts change.

To improve readability and reduce maintenance risk, consider calling the BytesLib library with solely the offset to the 32-byte word that the target value should be extracted from, and updating BytesLib to extract the relevant low-order bytes from that word.

Update: Resolved in pull request #1271.

Implicit Revert on Insufficient Funds for Account Activation

The transferERC20CoreToCore function is used to forward ERC-20 tokens from the current contract to the to address, on HyperCore. In the USDC case, the function will directly call the CoreDepositWallet, which will handle the account activation fee within its logic. In case of other ERC-20 tokens, the function will only transfer _amountCoreToReceive - accountActivationFeeCore, taking into account the activation fee. Note that if the transfer amount is not enough to handle the activation fee, the execution will revert through an implicit underflow. While not currently a security risk, such hidden revert assumptions can later lead to errors upon changes to the code.

Consider adding an explicit require check to fail early when the amount does not cover the activation fee, improving code readability and safety.

Update: Resolved in pull request #1268.

Cross-Chain Message Finalization Can Succeed While Funds Remain Untransferred

The transferERC20EVMToCore function has been updated to account for the accountActivationFeeCore fee, which is charged when transferring assets to a new HyperCore account. The function transfers _amountCoreToReceive - accountActivationFeeCore only when _amountCoreToReceive is sufficient to cover the fee. However, when _amountCoreToReceive == accountActivationFeeCore, the fee check fails and no transfer occurs, yet in cross-chain delivery flows the message may still be marked as processed, leaving funds stranded within a periphery contract on HyperCore.

Consider explicitly reverting when the transferred amount equals the activation fee, ensuring that the message can be retried under more favorable network or DepositBox conditions.

Update: Resolved in pull request #1268.

[Out of Scope] Decimal Conversion Rounds Down Bridged Amount Without Refunding the Remainder

The depositToHypercore function of the HyperliquidDepositHandler contract allows a caller to bridge tokens from HyperEVM to HyperCore and sends them to the end user's account on HyperCore. However, it does not refund any dust amount provided. It first transfers the entire specified amount, but afterward, it removes any dust amount before bridging the tokens in case the token has more decimals on EVM than on Core. Such dust amount is not refunded to the user and remains in the HyperliquidDepositHandler contract.

Consider refunding users with any dust token amount that has not been bridged.

Update: Acknowledged, not resolved. The client team stated:

The value of such refunds is extremely low. We feel like the additional gas cost / complexity that this would require is not worth it

[Out of Scope] Destination Does Not Enforce OFT Quote Deadlines

The OFT quote format includes a deadline that is enforced on the source chain yet not on the destination chain. While destination execution is expected to be timely, this is not guaranteed. Late execution can occur under materially different market or operational conditions, leading to unexpected swap behavior and potentially unintended donation-box sponsorship.

Consider enforcing the quote expiry deadline on the destination chain to ensure end-to-end validity. If destination-side enforcement is intentionally left out, clearly document the deadline as source-only (or remove it from composeMsg) to avoid implying time-bound guarantees across chains.

Update: Resolved in pull request #1218 at commit 2c87316 by documenting the current design choice of not enforcing deadlines on the destination chain for the OFT flow and not sending the deadline parameter in that flow to the destination chain anymore. The team stated:

We decided that the UX pain of “not executing the flow as expected” in the case of LZ executor being slow for whatever reason is greater then the potential harm caused by lumped executions that are past deadline.

We considered adding a buffer and checking the deadline + buffer against block time. However, in the OFT case, the execution time is heavily dependent on the origin / dst chains and it not easily configurable as a catch-all.

[Out of Scope] Possibility to Drain Assets from Donation Box

Whenever custom EVM actions are specified for a message, the maxBpsToSponsor parameter is greater than zero, and token bridging incurs any fees, some amount of final token may be sponsored from the funds taken from the Donation Box. This amount is calculated in proportion to the final token amount received from custom EVM actions. These custom EVM actions could involve swapping an input token on a decentralized exchange (DEX).

However, even though it is assumed that the actionData used to encode these actions is trusted and fully generated by trusted off-chain Across component (in particular, that calls are only allowed to trusted contracts and that users are not able to manipulate calldata in a way that allows them to execute any other contract), an attacker could inflate the final token amount by temporarily manipulating final token price on a DEX and then delivering message which performs the swap. As a result, funds taken from the Donation Box in final token can be arbitrarily inflated, potentially draining the entire final token amount from it. Although temporary price manipulation can be costly, it could still be viable for an attacker assuming that there are enough assets in the Donation Box.

Consider closely monitoring the token amounts in the Donation Box in order to ensure that they are enough for sponsoring legitimate transactions and to make the aforementioned attack unprofitable.

Update: Acknowledged, not resolved. The client team stated:

Arbitrary actions were added to the Phase0(current) version of the contracts to potentially support a fully sponsored flow with a PMM (PMM is trusted, swaps 1:1 and then bridge fees are sponsored from DonationBox)

With the PMM, this problem goes away.

For regular DEX swaps, the way the contracts are written right now, we wouldn’t be able to sponsor the DEX slippage, only the bridge fees. However, that wouldn’t create a 1:1 UX we’re going for.

All in all, we’re not planning to enable DEX swaps + sponsorship with the Phase0 contracts, but the next iteration of the contracts(coming in the future) will have a different setup to support destination-chain swaps + sponsorship

Notes & Additional Information

Documentation Improvement Suggestion

This comment from the ICoreDepositWallet.sol file suggests that all the functions declared in the ICoreDepositWallet interface from that file can be found at the provided link to the interface with the same name in the circlefin/hyperevm-circle-contracts repository. However, that second interface does not declare the depositFor function directly, but instead inherits it from the IForwardDepositReceiver interface.

Consider modifying the comment to also include the link to the IForwardDepositReceiver.sol file from the circlefin/hyperevm-circle-contracts repository in order to improve clarity of the codebase.

Update: Resolved in pull request #1269.

Typographical Error

This comment in the ArbitraryEVMFlowExecutor.sol file does not contain " " before "(".

Consider fixing this typographical error in order to improve the clarity of the codebase.

Update: Resolved in pull request #1269.

sweepOnCore Improvement Suggestion

The sweepOnCore function of the HyperCoreFlowExecutor contract is an administrative function that allows authorized users with the FUNDS_SWEEPER_ROLE to transfer tokens from this contract to them on HyperCore. However, this function always assumes that assets are transferred from the same DEX that they are sent from.

Consider allowing the callers of the sweepOnCore function to specify the source DEX for the transfer in order to increase its flexibility.

Update: Resolved in pull request #1270. The pull request also modifies the transferERC20CoreToCore such that it uses SPOT_SEND_HEADER only when both source and destination dexes are the spot dex. 

Conclusion

This audit covered three scopes:

  • Pull request #1225, which introduces two extra parameters for bridging tokens to HyperCore for transactions originating from the Solana blockchain.
  • Pull request #1218, which brings the same functionality to EVM blockchains and handles these parameters on the Hyperliquid side.
  • Pull request #1227, which adds support for OFT tokens with bridging fees.

Following a thorough review of the codebase and accompanying documentation, several low- and note-severity issues were identified, along with multiple out-of-scope issues in the existing code.

Thanks are extended to the Across team for their responsiveness and collaboration throughout the audit, including promptly addressing questions and providing helpful context.

Appendix

Issue Classification

OpenZeppelin classifies smart contract vulnerabilities on a 5-level scale:

  • Critical
  • High
  • Medium
  • Low
  • Note/Information

Critical Severity

This classification is applied when the issue’s impact is catastrophic, threatening extensive damage to the client's reputation and/or causing severe financial loss to the client or users. The likelihood of exploitation can be high, warranting a swift response. Critical issues typically involve significant risks such as the permanent loss or locking of a large volume of users' sensitive assets or the failure of core system functionalities without viable mitigations. These issues demand immediate attention due to their potential to compromise system integrity or user trust significantly.

High Severity

These issues are characterized by the potential to substantially impact the client’s reputation and/or result in considerable financial losses. The likelihood of exploitation is significant, warranting a swift response. Such issues might include temporary loss or locking of a significant number of users' sensitive assets or disruptions to critical system functionalities, albeit with potential, yet limited, mitigations available. The emphasis is on the significant but not always catastrophic effects on system operation or asset security, necessitating prompt and effective remediation.

Medium Severity

Issues classified as being of medium severity can lead to a noticeable negative impact on the client's reputation and/or moderate financial losses. Such issues, if left unattended, have a moderate likelihood of being exploited or may cause unwanted side effects in the system. These issues are typically confined to a smaller subset of users' sensitive assets or might involve deviations from the specified system design that, while not directly financial in nature, compromise system integrity or user experience. The focus here is on issues that pose a real but contained risk, warranting timely attention to prevent escalation.

Low Severity

Low-severity issues are those that have a low impact on the client's operations and/or reputation. These issues may represent minor risks or inefficiencies to the client's specific business model. They are identified as areas for improvement that, while not urgent, could enhance the security and quality of the codebase if addressed.

Notes & Additional Information Severity

This category is reserved for issues that, despite having a minimal impact, are still important to resolve. Addressing these issues contributes to the overall security posture and code quality improvement but does not require immediate action. It reflects a commitment to maintaining high standards and continuous improvement, even in areas that do not pose immediate risks.