Summary

Type: DeFi
Timeline: From 2026-02-02 → To 2026-02-03
Languages: Solidity

Findings
Total issues: 3 (3 resolved)
Critical: 0 (0 resolved) · High: 0 (0 resolved) · Medium: 0 (0 resolved) · Low: 2 (2 resolved)

Notes & Additional Information
0 notes raised (0 resolved)

Client Reported Issues
1 issue reported (1 resolved)

Scope

OpenZeppelin performed a differential audit of the across-protocol/contracts repository at commit 148d643 against base commit 041fcbf. The changes contained in pull request #1275 were the main subject of this audit.

Prior to the start of the audit, the Across team reported an issue in this scope. The fixes addressing this issue (contained in pull request #1287) were also reviewed as part of this audit.

In scope were the following files:

 contracts
├── SpokePoolPeriphery.sol
└── interfaces
    ├── SpokePoolPeripheryInterface.sol
    └── V3SpokePoolInterface.sol

System Overview

Across Protocol

The Across protocol is a cross-chain bridge designed for fast and cost-effective transfers of ERC-20 tokens and native assets across various networks. It allows users (depositors) to lock assets on an origin chain, which are then made available to them on a destination chain by relayers who front their own capital. The protocol refunds the relayers by sending funds available on the chain of relayers' choice or by tapping into the HubPool on Ethereum if a specific chain does not have enough funds.

SpokePoolPeriphery

The SpokePoolPeriphery contract acts as a user-facing entry point to the Across protocol, significantly expanding the options available for initiating cross-chain transfers. Its core functionalities include the following:

  • Swap and Bridge: A flagship feature allowing users to bridge assets even if they do not hold the specific token required by the SpokePool. The contract can take a user-specified swapToken, execute a trade on a designated external exchange to convert it into the inputToken accepted by Across, and then initiate the bridge deposit, all within a single, atomic transaction.
  • Versatile Token Authorization: The contract integrates multiple industry-standard mechanisms for authorizing token transfers from users, providing flexibility and enabling gas-efficient interactions, including standard ERC-20 transferFrom, native currency (e.g., ETH), EIP-2612 permit, Permit2, and EIP-3009 receiveWithAuthorization.
  • Isolated Swap Execution via SwapProxy: To enhance security and modularity, all swap operations are delegated to a dedicated SwapProxy contract. The SpokePoolPeriphery deploys this proxy, which then handles token approvals and executes the swap calldata on the target exchange before returning the funds.

Gasless Deposit and Nonce Changes

This audit focuses on changes to the SpokePoolPeriphery contract in pull request #1275 that alter how gasless deposits and their associated nonces are handled. The primary goal of these changes is to enable reliable pre-computation of deposit identifiers to support prefills.

  • Deterministic Deposit IDs: All gasless deposit methods that rely on user signatures for authorization have been updated to use deterministic depositIds. Previously, these deposits would use a standard incrementing nonce.
  • Unified Nonce Handling: The protocol now enforces a strict, unified nonce validation scheme to guarantee depositId uniqueness.
    • For Permit2 authorizations, the nonce used in the permit signature must exactly match the nonce passed in the witness data.
    • For EIP-3009 receiveWithAuthorization calls, the contract hashes the witness data to generate a nonce, which enables a single-signature authorization flow.
  • Strict Nonce Validation: The contract now requires user-submitted deposits (i.e., non-gasless transfers initiated via a direct transaction) to use a nonce of 0. This acts as a signal to use the traditional incrementing depositId counter, ensuring that each deposit type follows a predictable and non-conflicting depositId generation scheme.

Security Model and Trust Assumptions

The security of the SpokePoolPeriphery contract depends on its internal logic, the integrity of the external systems it composes, and the honest administration by its owner. The changes in scope introduce a new security model for gasless deposits, which creates a strong dependency on the nonce mechanisms of underlying authorization standards to prevent replay attacks.

Key trust assumptions for the system are as follows:

  • Privileged Role Honesty: The contract owner is trusted to configure and manage all critical contract addresses, including the core SpokePool, the SwapProxy, and approved swap targets. A malicious or compromised owner could misconfigure these addresses, leading to a loss of user funds or denial of service.
  • External System Integrity: The system's functionality and security are deeply reliant on external contracts. This includes:
    • Exchanges: The swap-and-bridge functionality fundamentally depends on the security and correct implementation of the external exchanges it interacts with. The system assumes these exchanges are not malicious and will execute swaps according to their advertised logic.
    • Authorization Contracts: The system trusts that the canonical Permit2 contract and the specific implementations of EIP-3009 tokens are secure and function as expected for handling signature-based token approvals.
  • Nonce Security and Uniqueness: The security of the new deterministic depositId generation for gasless deposits is critically dependent on the non-reuse of nonces from the underlying authorization mechanisms (e.g., Permit2). The system assumes that the guarantee of a unique nonce from these external systems is sufficient to prevent replay attacks and ensure the integrity of deposit prefills.
  • Relayer Network Integrity: While the SpokePoolPeriphery contract is not directly responsible for the relaying process, its utility depends on the liveness and honesty of the broader Across protocol's relayer network. Users implicitly trust that relayers will act to front liquidity on the destination chain.
 

Low Severity

Changes to view Function Visibility Not Backwards Compatible

In the reviewed pull request, the visibility of the SpokePoolPeriphery contract's permitNonces state variable has been changed from public to private. This is done because after the changes in the handling of nonces, the nonce returned from the permitNonces state variable will now only show the previous nonce and not the next valid nonce for a given user. A new getter function named permitNonce has been implemented for this purpose. The issue is that this change is not backwards compatible and will break existing integrations that rely on the previously public permitNonces variable to obtain the next valid nonce.

Consider changing the name of the newly added permitNonce function to permitNonces. This will retain backwards compatibility with no impact on integrators.

Update: Resolved in pull request #1301.

Incomplete Docstring

In SpokePoolPeriphery.sol, the _deposit function does not have a docstring for the newly added nonceIdentifier parameter.

Consider thoroughly documenting all functions/events (and their parameters or return values) that are part of a contract's public API. When writing docstrings, consider following the Ethereum Natural Specification Format (NatSpec).

Update: Resolved in pull request #1287.

Client Reported

depositId Collision Due to Missing Authorizer in Nonce

The SpokePoolPeriphery contract facilitates gasless deposits by using mechanisms like Permit2 or EIP-3009. In these flows, an authorizer signs a message that allows the contract to perform a deposit on behalf of a specified depositor. The contract generates a unique depositId for these deposits by hashing a nonce provided by the authorizer. The system correctly anticipates that authorizer and depositor may be different addresses.

A depositId collision can occur if two different authorizers create deposits with the same nonce while specifying the same third party as the depositor. Since the depositId is derived from a hash that includes the depositor and the nonce but not the authorizer, both deposits will resolve to the same depositId in the SpokePool. This can lead to one of the deposits failing or being overwritten, potentially resulting in a loss of funds for a user.

The Across team addressed this vulnerability by including the authorizer in the derivation of the nonce passed to the SpokePool. This modification ensures that the final depositId is unique to the combination of the authorizer, depositor, and nonce, thereby preventing collisions for gasless deposits.

Update: Resolved in pull request #1287.

 
 

Conclusion

The audit focused on a set of targeted changes made to the SpokePoolPeriphery contract within the Across V3 protocol. The scope of the review centered on the refactoring of gasless deposit flows to introduce deterministic depositIds and a stricter, unified nonce validation scheme. These modifications were intended to enhance the reliability of deposit pre-computation and improve the overall security of signature-based authorization mechanisms.

During the review period, the Across team reported a subtle, client-reported vulnerability related to potential depositId collisions in gasless deposit flows. The team provided a comprehensive fix for this issue, which was subsequently included in the audit scope.

 

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.