Periphery Changes Audit: Enhancing Across Protocol’s Cross-Chain Functionality

·

The Across Protocol continues to evolve, and with innovation comes the need for rigorous security validation. OpenZeppelin recently conducted a comprehensive differential audit of the protocol’s smart contract repository, focusing specifically on the SpokePoolPeriphery contract and its associated components. This audit scrutinized critical aspects such as swap logic, signature handling, EIP-712 encoding, and potential replay attacks—identifying vulnerabilities across high, medium, and low severity levels. Importantly, all reported issues have since been resolved, reinforcing the protocol's commitment to security and user trust.

This article dives deep into the technical scope, findings, and implications of the audit, offering clarity on how these upgrades enhance cross-chain interoperability while maintaining robust safety standards.


Audit Overview and Scope

The audit targeted the across-protocol/contracts GitHub repository, comparing two key commits:

The scope included the following files:

contracts
├── external
│   └── interfaces
│       ├── IERC20Auth.sol
│       └── IPermit2.sol
├── handlers
│   └── MulticallHandler.sol
├── interfaces
│   └── SpokePoolPeripheryInterface.sol
├── libraries
│   └── PeripherySigningLib.sol
└── SpokePoolPeriphery.sol

These components form the foundation of the new peripheral smart contracts, designed to improve user interaction with the Across V3 ecosystem through enhanced flexibility, gas efficiency, and cross-chain functionality.


System Overview: The Role of SpokePoolPeriphery

At the heart of this upgrade is the SpokePoolPeriphery contract—an intuitive entry point for users initiating cross-chain transfers. It introduces powerful new features that streamline asset movement across blockchains.

Key Features of SpokePoolPeriphery

Swap and Bridge (Atomic Cross-Chain Swaps)

This flagship feature enables users to bridge assets even if they don’t hold the exact token required by the destination SpokePool. Here’s how it works:

  1. User specifies a swapToken (e.g., DAI).
  2. The contract executes a swap via an external exchange to convert it into an accepted inputToken (e.g., USDC).
  3. The resulting tokens are deposited into the SpokePool—all within a single atomic transaction.

Additionally, output amounts can be proportionally adjusted if the swap yields more than the minimum expected amount, maximizing capital efficiency.

Flexible Token Authorization Methods

To support diverse wallet types and user preferences, SpokePoolPeriphery integrates multiple industry-standard authorization mechanisms:

This multi-path approach ensures broad compatibility and optimal user experience.

Isolated Swap Execution via SwapProxy

Security and modularity are strengthened through the use of a dedicated SwapProxy contract. All swap operations are delegated here, which handles:

This separation limits risk exposure and simplifies auditing.


MulticallHandler Updates

A new function, makeCallWithBalance, has been added to the MulticallHandler contract. It allows dynamic insertion of a contract’s token balance into arbitrary calldata before execution—useful when deposit amounts depend on prior swaps whose outputs are unknown at signing time.

However, users must exercise caution:

👉 Discover how advanced cross-chain strategies can optimize your asset transfers.


PeripherySigningLib: Secure Signature Handling

The PeripherySigningLib library supports secure, standardized signing workflows by providing:

This ensures consistent and verifiable off-chain signatures while minimizing attack surface in core logic.


Security Model and Trust Assumptions

With enhanced functionality comes expanded trust assumptions:

Users are expected to act prudently—submitting valid data and using trusted interfaces.


Critical Findings and Resolutions

Despite overall strong code quality, several vulnerabilities were identified and promptly fixed.

🔴 High-Risk: Incorrect Nonce Passed to Permit2.permit

The SwapProxy.performSwap function initially used a global nonce when calling Permit2.permit. However, Permit2 expects unique nonces per (owner, token, spender) tuple. Using a shared nonce caused subsequent swaps with different pairs to fail due to mismatched expectations.

Fix: Each (token, spender) pair now maintains its own nonce, ensuring compatibility with Permit2’s design.


🟠 Medium-Risk: Potential Replay Attacks on SpokePoolPeriphery

Functions like swapAndBridgeWithPermit and depositWithPermit accepted EIP-2612 signatures without including a nonce. This allowed attackers to replay old signatures within the depositQuoteTimeBuffer window, potentially forcing unintended deposits or swaps.

Fix: Introduced a permitNonces mapping and added a nonce field to relevant structs. Each successful operation now increments the user’s nonce, preventing reuse.

ERC-3009 paths remain nonce-managed at the token level; overlap risks are minimal due to strict timing and value alignment requirements.


🟠 Medium-Risk: DoS Attack via Permit2.invalidateNonces

Since performSwap allows arbitrary exchange addresses and calldata, attackers could target Permit2’s invalidateNonces function. By invalidating future nonces, they could permanently block specific token-exchange combinations.

Fix: Explicitly disallow exchange == Permit2 address in swap calls.


🟠 Medium-Risk: Incorrect EIP-712 Encoding

The SwapAndDepositData struct included an unsupported TransferType enum in its EIP-712 domain separator. Enums aren't natively supported in EIP-712 encoding.

Fix: Replaced enum usage with uint8, aligning with standard practices.


Low-Risk Issues and Improvements

🔻 Incompatibility with Non-EVM Chains

Original deposit function parameters (recipient, exclusiveRelayer) were typed as Ethereum address, then cast to bytes32. This prevented bridging to non-EVM chains where address formats differ.

Fix: Updated parameter types to support arbitrary chain addresses.


🔻 Integer Overflow in _swapAndBridge

In _swapAndBridge, calculating proportional output involved multiplying large values before division. If the intermediate product exceeded 2^256 - 1, the transaction reverted silently—without clear error messaging.

Fix: Added documentation highlighting overflow risks. Future implementations may adopt OpenZeppelin’s Math.mulDiv() for safer arithmetic.


🔻 Rigid Fee Recipient Field Limits Open Relaying

Every deposit required a hardcoded fee recipient, discouraging competitive relaying or fallback options during outages.

Fix: Implemented a "zero-address" convention:

This enables open relay competition while preserving transparency.


Code Quality & Maintainability Enhancements

Beyond security, several code health improvements were made:

Function Renaming for Clarity

The deposit function only accepted native currency, misleading developers expecting ERC-20 support. Now renamed to depositNative.

Documentation Upgrades

Previously sparse or misleading docs were updated:

Spelling Corrections

Fixed typos like “calldData” → “callData”, improving readability.

Removal of Unused Code

Deleted unused imports and the obsolete InvalidSignatureLength error.

Misleading Comment Fixes

Corrected outdated notes about immutable variables and clarified that try/catch blocks silently handle failed permit calls instead of reverting.

👉 Learn how top-tier security practices protect your cross-chain assets in real time.


Frequently Asked Questions (FAQ)

Q: What is the main purpose of the SpokePoolPeriphery contract?

A: It serves as a user-facing interface for Across Protocol, enabling advanced cross-chain actions like atomic swap-and-bridge operations and flexible token approvals using standards like EIP-2612 and Permit2.

Q: Why was the nonce handling in Permit2 integration considered high-risk?

A: Because Permit2 uses per-tuple nonces but the original implementation used a global nonce, leading to irreversible transaction failures after one successful swap—effectively locking users out of future swaps with different token pairs.

Q: How does the protocol prevent replay attacks now?

A: By introducing per-user nonces stored in a mapping and incrementing them after each permit-based action. This ensures one-time use of signed messages.

Q: Can I use this system to bridge assets to non-EVM chains?

A: Yes—after fixes, address fields now support arbitrary byte-encoded addresses, enabling interoperability with non-EVM blockchains like Solana or Cosmos zones.

Q: Does the system support gasless approvals?

A: Yes—via EIP-2612 permit, EIP-3009 receiveWithAuthorization, and Permit2 signatures, users can authorize transfers without paying gas upfront.

Q: Are there any remaining risks users should know about?

A: While all known vulnerabilities are patched, users should still verify exchange addresses, set realistic slippage tolerances, simulate transactions when possible, and only interact through trusted frontends to avoid phishing or malicious calldata injection.


Conclusion

The audit of Across Protocol’s peripheral contracts underscores a mature development process—one that embraces transparency, rapid response, and continuous improvement. Though vulnerabilities were uncovered in areas like nonce management, signature replay, and EIP-712 compliance, all have been decisively addressed.

These upgrades not only expand functionality—enabling seamless swap-and-bridge experiences and better cross-chain compatibility—but also reinforce security through modular design and precise trust assumptions.

As cross-chain ecosystems grow increasingly complex, audits like this one from OpenZeppelin play a vital role in safeguarding user assets and maintaining ecosystem integrity.

👉 Stay ahead in DeFi with secure, audited cross-chain solutions powered by cutting-edge technology.