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:
- Base version:
7362cd0(master) - Head version:
b84dbfa
The scope included the following files:
contracts
├── external
│ └── interfaces
│ ├── IERC20Auth.sol
│ └── IPermit2.sol
├── handlers
│ └── MulticallHandler.sol
├── interfaces
│ └── SpokePoolPeripheryInterface.sol
├── libraries
│ └── PeripherySigningLib.sol
└── SpokePoolPeriphery.solThese 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:
- User specifies a
swapToken(e.g., DAI). - The contract executes a swap via an external exchange to convert it into an accepted
inputToken(e.g., USDC). - 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:
- Standard ERC-20
transferFrom - Native ETH deposits (auto-wrapped to WETH)
- EIP-2612
permitfor gasless approvals - Permit2’s
permitWitnessTransferFromfor batched, advanced permissions - EIP-3009
receiveWithAuthorizationfor compatible tokens
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:
- Token approvals to exchanges or Permit2
- Execution of router calldata
- Transfer of output tokens back to SpokePoolPeriphery
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:
- They must correctly specify token type and offset.
- Negative values cannot be encoded (limiting compatibility with certain DEXs).
- Incorrect offsets may lead to fund loss.
👉 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:
- EIP-712-compliant typed data hashing for
DepositData,Fees, andSwapAndDepositData - Signature deserialization utilities (
v, r, sparsing)
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:
- External exchange integrity: Users or frontends must select trusted DEXs; malicious calldata can result in fund loss.
- Parameter correctness: Inputs like router calldata, recipient addresses, and minimum output values must be accurate.
- Permit2 reliance: The system assumes the canonical Permit2 contract operates securely and is available on all deployed chains.
- Relayer responsibility: Relayers should simulate signed swap transactions off-chain to avoid gas exhaustion from malicious calls (e.g., infinite loops).
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:
- If fee recipient is zero, default to
msg.sender. - Otherwise, pay the specified address.
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:
- Clarified limitations of
makeCallWithBalance - Noted that native ETH cannot be swap output
- Added NatSpec comments to
PeripherySigningLib
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.