Skip to content

feat: Checking LZ OFTLimits on transferTokenLayerZero (DEV-1140)#235

Open
TheMj0ln1r wants to merge 4 commits intofix/rate-limit-for-transferTokenLayerZerofrom
dev-1140-lz-oft-limit-checks
Open

feat: Checking LZ OFTLimits on transferTokenLayerZero (DEV-1140)#235
TheMj0ln1r wants to merge 4 commits intofix/rate-limit-for-transferTokenLayerZerofrom
dev-1140-lz-oft-limit-checks

Conversation

@TheMj0ln1r
Copy link
Copy Markdown
Contributor

@TheMj0ln1r TheMj0ln1r commented Jan 29, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Cross-chain token transfers now validate and enforce minimum and maximum amount limits before execution, preventing invalid transfers.
  • Tests

    • Added comprehensive boundary condition tests for cross-chain transfer amount limits across multiple networks.

✏️ Tip: You can customize this high-level summary in your review settings.

@notion-workspace
Copy link
Copy Markdown

@octane-security-app
Copy link
Copy Markdown

Summary by Octane

New Contracts

No new contracts were added.

Updated Contracts

  • LayerZeroLib.sol: The smart contract now includes OFT limits to enforce minimum and maximum transaction amounts per message.

🔗 Commit Hash: b324168

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 29, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review

Walkthrough

The changes enforce OFT (Omnichain Fungible Token) limit boundaries in the LayerZero library's transfer function by decoding and validating minimum and maximum amount limits before token transfer execution. Comprehensive test coverage is added to verify boundary-case scenarios across multiple test suites.

Changes

Cohort / File(s) Summary
LayerZero OFT Limit Enforcement
src/libraries/LayerZeroLib.sol
Introduces per-message amount bounds validation by decoding OFTLimit and OFTReceipt from quote results. Enforces amount >= limit.minAmountLD and amount <= limit.maxAmountLD (if set) with corresponding error messages. Updates sendParams.minAmountLD to use receipt.amountReceivedLD.
Boundary Condition Tests
test/mainnet-fork/LayerZero.t.sol
Adds three new test_transferTokenLayerZero_OFTLimitBoundary() functions across MainnetControllerTransferLayerZeroFailureTests and ForeignControllerTransferLayerZeroFailureTests, verifying amount-above-max reverts and min/max boundary conditions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • lucas-manuel
  • deluca-mike
  • supercontracts

Poem

🐰 Bounds we hop, not past the fence,
Min and max make perfect sense,
LayerZero tokens safe at last,
Crossing chains with limits fast!
Tests ensure no bounds are breached,
Limits checked, the goal's now reached!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: Checking LZ OFTLimits on transferTokenLayerZero (DEV-1140)' follows the required format with a concise prefix and clearly describes the main change: adding OFT limit checks to the LayerZero transfer function.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev-1140-lz-oft-limit-checks

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • DEV-1140: Entity not found: Issue - Could not find referenced Issue.

Tip

🧪 Unit Test Generation v2 is now available!

We have significantly improved our unit test generation capabilities.

To enable: Add this to your .coderabbit.yaml configuration:

reviews:
  finishing_touches:
    unit_tests:
      enabled: true

Try it out by using the @coderabbitai generate unit tests command on your code files or under ✨ Finishing Touches on the walkthrough!

Have feedback? Share your thoughts on our Discord thread!


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/mainnet-fork/LayerZero.t.sol`:
- Around line 647-741: The test is operating against mainnet fixtures but runs
on the Arbitrum fork; replace references to mainnetController, rateLimits,
almProxy, usdt (and SPARK_PROXY when impersonating) with the corresponding
foreign-controller fixtures (e.g., foreignController, foreignRateLimits,
foreignAlmProxy, foreignUsdt, and foreign SPARK_PROXY variable) so the setup,
rate-limit mutation, deals, and transferTokenLayerZero calls target the Arbitrum
deployment; update calls to ILayerZero(USDT_OFT) to use the foreign token
variable and ensure setRateLimitData, setLayerZeroRecipient, deal() and
transferTokenLayerZero invocations reference the foreign fixtures and relayer as
in the other forked tests.

Comment on lines +647 to +741
function test_transferTokenLayerZero_OFTLimitBoundary() external {
bytes32 target = bytes32(uint256(uint160(makeAddr("layerZeroRecipient"))));

vm.startPrank(SPARK_PROXY);

rateLimits.setRateLimitData(
keccak256(abi.encode(
mainnetController.LIMIT_LAYERZERO_TRANSFER(),
USDT_OFT,
destinationEndpointId
)),
type(uint256).max,
0
);

mainnetController.setLayerZeroRecipient(destinationEndpointId, target);

vm.stopPrank();

// Setup token balances
deal(address(usdt), address(almProxy), type(uint256).max);
deal(relayer, 1 ether); // Gas cost for LayerZero

bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200_000, 0);

// NOTE: amount under OFTLimit minAmountLD boundary test can't be done with USDT_OFT
// USDT_OFT OFTLimit.minAmountLD = 0, So, we can't pass amount less than 0

// amount over OFTLimit maxAmountLD boundary

SendParam memory sendParams = SendParam({
dstEid : destinationEndpointId,
to : target,
amountLD : 18446744073709551616, // type(uint64) + 1
minAmountLD : 18446744073709551616, // type(uint64) + 1
extraOptions : options,
composeMsg : "",
oftCmd : ""
});

MessagingFee memory fee = ILayerZero(USDT_OFT).quoteSend(sendParams, false);

// OFTLimits remains same
(OFTLimit memory limits, ,) = ILayerZero(USDT_OFT).quoteOFT(sendParams);

vm.prank(relayer);
vm.expectRevert("LayerZeroLib/amount-above-max");
mainnetController.transferTokenLayerZero{value: fee.nativeFee}(
USDT_OFT,
limits.maxAmountLD + 1,
destinationEndpointId
);

// amount at OFTLimit minAmountLD boundary

sendParams = SendParam({
dstEid : destinationEndpointId,
to : target,
amountLD : 0,
minAmountLD : 0,
extraOptions : options,
composeMsg : "",
oftCmd : ""
});

fee = ILayerZero(USDT_OFT).quoteSend(sendParams, false);

vm.prank(relayer);
mainnetController.transferTokenLayerZero{value: fee.nativeFee}(
USDT_OFT,
limits.minAmountLD,
destinationEndpointId
);

// amount at OFTLimit maxAmountLD boundary

sendParams = SendParam({
dstEid : destinationEndpointId,
to : target,
amountLD : type(uint64).max,
minAmountLD : type(uint64).max,
extraOptions : options,
composeMsg : "",
oftCmd : ""
});

fee = ILayerZero(USDT_OFT).quoteSend(sendParams, false);

vm.prank(relayer);
mainnetController.transferTokenLayerZero{value: fee.nativeFee}(
USDT_OFT,
limits.maxAmountLD,
destinationEndpointId
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix Arbitrum fork boundary test to use foreign controller fixtures.

This block configures and calls mainnetController/rateLimits/almProxy/usdt on the Arbitrum fork, so it targets the wrong deployment and will likely fail or become a no-op. Use the foreign controller fixtures instead.

Proposed fix
-        vm.startPrank(SPARK_PROXY);
+        vm.startPrank(SPARK_EXECUTOR);

-        rateLimits.setRateLimitData(
+        foreignRateLimits.setRateLimitData(
             keccak256(abi.encode(
-                mainnetController.LIMIT_LAYERZERO_TRANSFER(),
+                foreignController.LIMIT_LAYERZERO_TRANSFER(),
                 USDT_OFT,
                 destinationEndpointId
             )),
             type(uint256).max,
             0
         );

-        mainnetController.setLayerZeroRecipient(destinationEndpointId, target);
+        foreignController.setLayerZeroRecipient(destinationEndpointId, target);

-        deal(address(usdt), address(almProxy), type(uint256).max);
+        deal(USDT0, address(foreignAlmProxy), type(uint256).max);

-        mainnetController.transferTokenLayerZero{value: fee.nativeFee}(
+        foreignController.transferTokenLayerZero{value: fee.nativeFee}(
             USDT_OFT,
             limits.maxAmountLD + 1, 
             destinationEndpointId
         );

-        mainnetController.transferTokenLayerZero{value: fee.nativeFee}(
+        foreignController.transferTokenLayerZero{value: fee.nativeFee}(
             USDT_OFT,
             limits.minAmountLD,
             destinationEndpointId
         );

-        mainnetController.transferTokenLayerZero{value: fee.nativeFee}(
+        foreignController.transferTokenLayerZero{value: fee.nativeFee}(
             USDT_OFT,
             limits.maxAmountLD,
             destinationEndpointId
         );
🤖 Prompt for AI Agents
In `@test/mainnet-fork/LayerZero.t.sol` around lines 647 - 741, The test is
operating against mainnet fixtures but runs on the Arbitrum fork; replace
references to mainnetController, rateLimits, almProxy, usdt (and SPARK_PROXY
when impersonating) with the corresponding foreign-controller fixtures (e.g.,
foreignController, foreignRateLimits, foreignAlmProxy, foreignUsdt, and foreign
SPARK_PROXY variable) so the setup, rate-limit mutation, deals, and
transferTokenLayerZero calls target the Arbitrum deployment; update calls to
ILayerZero(USDT_OFT) to use the foreign token variable and ensure
setRateLimitData, setLayerZeroRecipient, deal() and transferTokenLayerZero
invocations reference the foreign fixtures and relayer as in the other forked
tests.

@TheMj0ln1r TheMj0ln1r changed the base branch from dev to fix/rate-limit-for-transferTokenLayerZero January 29, 2026 18:48
@TheMj0ln1r TheMj0ln1r force-pushed the dev-1140-lz-oft-limit-checks branch from b324168 to 5aebe7e Compare January 29, 2026 18:49
@octane-security-app
Copy link
Copy Markdown

Overview

Vulnerabilities found: 5                                                                                

🔗 Commit Hash: b324168
🛡️ Octane Dashboard: All vulnerabilities

@github-actions
Copy link
Copy Markdown

Coverage after merging dev-1140-lz-oft-limit-checks into fix/rate-limit-for-transferTokenLayerZero will be

99.30%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
deploy
   ControllerDeploy.sol100%100%100%100%
   ForeignControllerInit.sol100%100%100%100%
   MainnetControllerInit.sol97.37%93.33%100%100%152, 90
src
   ALMProxy.sol100%100%100%100%
   ALMProxyFreezable.sol100%100%100%100%
   ForeignController.sol96.10%89.66%95.65%98.04%130–131, 131, 131, 556
   MainnetController.sol99.18%100%98.36%99.24%596–597
   OTCBuffer.sol92.31%100%83.33%93.75%55
   RateLimitHelpers.sol100%100%100%100%
   RateLimits.sol100%100%100%100%
   WEETHModule.sol93.18%90%85.71%96.30%65, 72
src/libraries
   AaveLib.sol100%100%100%100%
   ApproveLib.sol100%100%100%100%
   CCTPLib.sol100%100%100%100%
   CurveLib.sol100%100%100%100%
   ERC4626Lib.sol97.06%90%100%100%117
   LayerZeroLib.sol96.55%85.71%100%100%109
   PSMLib.sol100%100%100%100%
   UniswapV4Lib.sol99.32%95.65%100%100%282
   WEETHLib.sol100%100%100%100%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants