OpenZeppelin audited pull request #1043 and pull request #1045 of the across-protocol/contracts repository.
In scope were the following files:
programs
└── svm-spoke
└── src
├── error.rs
├── lib.rs
├── event.rs
├── common
│ └── relay_data.rs
├── instructions
│ └── deposit.rs
└── utils
└── delegate_utils.rs
On June 11th, 2025, the Across team informed OpenZeppelin about a vulnerability in the code that handles cross-chain transfers between Solana and EVM-based chains. It was identified that the u64
data type used by the Solana Token Program was insufficient to handle amounts specified from Ethereum that use uint256
, which would severely limit the capabilities of cross-chain interactions. OpenZeppelin had previously audited the code in this report. Subsequently, a review was conducted to validate the correctness and security of the fix that had been implemented to address this issue (pull request #1045). An additional unrelated update to the code disallowing deposits with output token set to 0x0
was also reviewed (pull request #1043).
Across relies on events to be fired during the deposit and fill actions so that the necessary actions can be taken by actors within the system. The issue is related to the amount fields in deposit and fill actions which are of insufficient size to store a large portion of token amounts corresponding to EVM chains. The Solana token program uses 64-bit integers to store token amounts, and so Solana tokens typically use 6-9 decimals in order to fit the total supply within this type. This is in contrast to EVM chains where many tokens are conventionally 18 decimals, stored in a 256-bit integer. Therefore, input and output amounts intended for EVM chains that exceed the 64-bit space will overflow and fail to be processed by the SVM spoke program. This discrepancy can cause the following impacts:
output_amount
: During a deposit to the SVM spoke, the user specifies the output_amount
they would like to receive on the destination chain. If this amount is 2**64
or larger (approximately 18.4e18
) they will not be able to submit the deposit request.input_amount
which needs to be submitted to the fill function on the SVM spoke. However, since the accepted type is u64
, if the deposit from the source chain includes an input_amount
of 2**64
or larger, the function will not be callable, and the request will be unfillable (and eventually refunded).As this issue prevents a large number of intended transfers from happening to/from Solana, the review team estimates this issue to be of critical severity.
The fix was to make the output_amount
in the deposit function a 32-byte array, i.e., [u8; 32]
, in order to align with output amounts on EVM chains. Similarly, the input_amount
of the RelayData
struct was changed to [u8; 32]
in order to process data coming from EVM chains on the Solana side. Note that the input_amount
in deposits is a Solana token (likewise, so is the output_amount
in RelayData
), and as such a u64
remains sufficient for these values.
An unrelated update (pull request #1043) disallows deposits with the address of the output token set to 0x0
. Historically, in Across, setting the output token address to zero implied that it is to be interpreted as the equivalent of the input token on the destination chain. However, this legacy functionality has been deprecated in the bridging logic for other tokens (e.g., LayerZero OFT) and is being removed for SPL tokens as well.
The slow fill leaf is computed from the SlowFill
struct which includes relay data for the corresponding fill. The leaf is then verified for inclusion in the Merkle tree with a slow relay root that is computed off-chain. However, the way that the relay data should be provided may be unclear to the slow fill executor due to the way that data is serialized before hashing.
The hashing function uses AnchorSerialize::serialize
to process the relay data before performing computation, and this function changes integer types to little-endian format in the resulting serialized bytes. This process may be unclear to the slow fill relayer when providing the data, resulting in a leaf that does not belong to the tree with the corresponding root.
Consider adding further documentation within the code to clarify the exact format that data should be provided to avoid confusion and failed slow fill attempts.
Update: Resolved in pull request #1053 at commit a90c895. The Across team stated:
Added a note to the
execute_slow_relay_leaf
function regarding the numerical encoding of token amounts when verifying the slow fill leaf.
Pull request #1045 addresses the type mismatch between Solana and EVM token amounts. It changes the data type of any amount used by the Solana program, that is either intended to be processed by or comes from an EVM chain, from u64
to [u8; 32]
(i.e., a 32-byte array). Now, it is possible to represent any EVM token amount on the Solana side. The review team determined that this change fixes the reported issue, and no additional issues were identified. However, it is recommended to clarify with additional code comments how relay data should be provided to the SVM spoke due to the way AnchorSerialize
handles endianness.
An unrelated update has also been proposed with pull request #1043. It disallows deposits with the address of the output token set to 0x0
. This removes legacy functionality that interpreted zero address tokens as the equivalent of the specified input token on the destination chain.