Skip to content

fix: only offer XVS bridge destinations whose OFT lane is open on-chain - #5795

Closed
cuzz-venus wants to merge 1 commit into
mainfrom
fix/bridge-closed-lanes
Closed

fix: only offer XVS bridge destinations whose OFT lane is open on-chain#5795
cuzz-venus wants to merge 1 commit into
mainfrom
fix/bridge-closed-lanes

Conversation

@cuzz-venus

Copy link
Copy Markdown
Contributor

What

The bridge "To" selector offered opBNB, ZKsync, Optimism and Unichain as XVS destinations from BSC while those OFT lanes are closed on-chain. BaseXVSProxyOFT reverts every non-whitelisted send to them with Single Transaction Limit Exceed, because chainIdToMaxSingleTransactionLimit is 0 for LayerZero ids 111 / 165 / 202 / 320.

Verified again while implementing (control side stays healthy):

cast call 0xf8F46791E3dB29a029Ec6c9d946226f3c613e854 \
  "chainIdToMaxSingleTransactionLimit(uint16)(uint256)" <lzId> \
  --rpc-url https://bsc-dataseed1.binance.org/
→ 101 ethereum 30000e18 · 110 arbitrum 20000e18 · 184 base 20000e18   (open)
→ 111 optimism 0 · 165 zksync 0 · 202 opbnb 0 · 320 unichain 0        (closed)

The destination list was built purely from the static featureFlags.bridgeRoute chain list (apps/evm/src/pages/Bridge/constants.ts); nothing consulted the OFT's lane limits.

How

Gate the destination options on the on-chain single transaction limit rather than hardcoding the removal of the four chains, so the list follows lane state in both directions and recovers by itself if a lane is reopened.

  • New query clients/api/queries/getXvsBridgeDestinationLimits reads chainIdToMaxSingleTransactionLimit(lzId) on the source chain's OFT once per candidate destination. The wagmi transport already sets batch.multicall, so the reads collapse into one multicall round trip.
  • New hook pages/Bridge/useBridgeDestinationChains narrows the configured destinations to the lanes whose limit is > 0.
  • pages/Bridge/index.tsx builds toChainIdOptions from that list, and reselects an open destination when the default one turns out to be closed (otherwise components/Select renders no selected option).

featureFlags.bridgeRoute is deliberately not touched — it also gates the /bridge route and the nav item, so removing those chains from it would strand users trying to bridge back out. The From selector is likewise left alone.

Two fail-open fallbacks, both covered by tests: while the limits are unknown (loading / RPC failure) and when no lane out of the source chain is open, the full configured list is kept. In both cases the existing amount validation still blocks the transfer with the exact on-chain limit, so nothing doomed can be submitted.

Files

File
clients/api/queries/getXvsBridgeDestinationLimits/index.ts added
clients/api/queries/getXvsBridgeDestinationLimits/useGetXvsBridgeDestinationLimits.ts added
clients/api/queries/getXvsBridgeDestinationLimits/__tests__/ added (3 tests + snapshot)
pages/Bridge/useBridgeDestinationChains.ts added
pages/Bridge/index.tsx options from the open-lane list + reselect effect
pages/Bridge/__tests__/index.spec.tsx 3 new cases
clients/api/index.ts, clients/api/__mocks__/index.ts, constants/functionKey.ts wiring

No new dependencies, no new i18n keys, no changes to featureFlags.bridgeRoute.

Gates

All four run end to end on this tree:

Gate Result
yarn tsc pass
yarn lint pass — 2180 files, 0 errors
yarn extract-translations pass — no translation-file changes (no new keys)
yarn test --coverage pass — evm 434/434 files, 1848/1848 tests; chains 9/9, 16/16

Coverage (evm): statements 80.71, branches 86.09, functions 74.47 — statements flat and branches up slightly against the pre-change baseline (80.71 / 86.04 / 74.52); functions is 0.05pp lower, which is the rounding effect of adding functions to the tree. src/pages/Bridge sits at 93.4% statements.

Notes / follow-ups

  • Send side only. The gate reads the source chain's send limit — the guard that actually reverts. It does not read each destination's chainIdToMaxSingleReceiveTransactionLimit, because that needs one PublicClient per destination chain and usePublicClient({ chainId }) can't be called in a loop. A lane open on the send side but zero on the receive side would still be offered, and would still be blocked by the existing getXvsBridgeStatus min() validation once selected — i.e. no worse than today.
  • Why a new query rather than extending getXvsBridgeStatus. That query is inherently single-destination: it also reads the destination chain's receive limits through a second public client and derives USD daily/single limits plus 24h transfer state. Widening it to N destinations would mean N public clients and N× the reads with no batching. The new query is a deliberately narrower single-chain batched read.
  • Open question for product. VPD-1850 records "the four chains are being retired" as an off-chain product claim (2026-08-16) that was never independently verified. This fix follows on-chain state, so the chains reappear automatically if the lanes are reopened. If retirement is meant to be permanent regardless of lane limits, someone should confirm whether they should also leave bridgeRoute — accepting that users holding XVS there could no longer bridge back.
  • Considered and skipped to avoid new copy across seven locale files: an explicit "no destinations available" notice for the no-open-lane case. Recommended as a follow-up.

Ticket: VENUS-178 (Multica) · VPD-1850 / TC-BRG-FE-014. Test case TC-BRG-FE-014 is unchanged, as required.

🤖 Generated with Claude Code

The bridge destination list was built purely from the static
featureFlags.bridgeRoute chain list, so opBNB, ZKsync, Optimism and
Unichain were offered as XVS destinations from BSC even though the OFT
reverts every non-whitelisted send to them ("Single Transaction Limit
Exceed") — their chainIdToMaxSingleTransactionLimit is 0.

Gate the "To" options on the on-chain limit instead of hardcoding the
removal, so the list follows lane state in both directions and recovers
automatically if a lane is reopened. bridgeRoute is left untouched: it
also gates the /bridge route and the nav item, so dropping those chains
from it would strand users bridging back out.

Falls back to the full list while the limits are unknown, and reselects
an open destination when the default one turns out to be closed.
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dapp-preview Ready Ready Preview Aug 27, 2026 5:24am
dapp-testnet Ready Ready Preview Aug 27, 2026 5:24am
venus.io Ready Ready Preview Aug 27, 2026 5:24am

Request Review

@changeset-bot

changeset-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a2eccc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown

Greptile Summary

The PR reads each candidate XVS destination’s source-side OFT limit and filters the bridge destination selector to lanes currently open on-chain.

  • Adds a batched destination-limit query and React Query hook.
  • Adds destination filtering and automatic reselection when the current destination is closed.
  • Adds query and bridge-page coverage for open, unknown, and entirely closed lane states.

Confidence Score: 4/5

The PR appears safe to merge from a behavioral perspective, with only a non-blocking repository-organization issue in the API mock wiring.

The lane reads, chain mapping, filtering, reselection, and conservative validation fallbacks form a coherent flow; the only accepted concern is that two new mock implementations were added to one shared file despite the repository’s required one-function-per-file structure.

Files Needing Attention: apps/evm/src/clients/api/mocks/index.ts

Important Files Changed

Filename Overview
apps/evm/src/clients/api/queries/getXvsBridgeDestinationLimits/index.ts Reads source-side per-destination OFT limits using the configured EVM-to-LayerZero chain mapping; no correctness issue was established.
apps/evm/src/clients/api/queries/getXvsBridgeDestinationLimits/useGetXvsBridgeDestinationLimits.ts Selects the active source or destination OFT contract and caches lane-limit reads by active chain and destination set.
apps/evm/src/pages/Bridge/useBridgeDestinationChains.ts Filters configured destinations to positive send limits while deliberately retaining the full list when data is unavailable or all lanes are closed.
apps/evm/src/pages/Bridge/index.tsx Builds destination options from live lane state and reselects the first open destination when necessary.
apps/evm/src/clients/api/mocks/index.ts Adds functional defaults for destination-limit tests, but colocates two new function implementations contrary to repository organization rules.
apps/evm/src/pages/Bridge/tests/index.spec.tsx Covers closed-lane filtering, unresolved-limit fallback, and the all-lanes-closed fallback.

Reviews (1): Last reviewed commit: "fix: only offer XVS bridge destinations ..." | Re-trigger Greptile

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for ./apps/evm

Status Category Percentage Covered / Total
🔵 Lines 80.71% 52080 / 64522
🔵 Statements 80.71% 52080 / 64522
🔵 Functions 74.47% 1599 / 2147
🔵 Branches 86.09% 7677 / 8917
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
apps/evm/src/clients/api/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getXvsBridgeDestinationLimits/index.ts 100% 100% 100% 100%
apps/evm/src/clients/api/queries/getXvsBridgeDestinationLimits/useGetXvsBridgeDestinationLimits.ts 25% 100% 0% 25% 36-68
apps/evm/src/constants/functionKey.ts 100% 100% 100% 100%
apps/evm/src/pages/Bridge/index.tsx 92.26% 86.11% 81.81% 92.26% 166-167, 174-186, 212-213, 240-243, 249-252, 291-292, 354-357
apps/evm/src/pages/Bridge/useBridgeDestinationChains.ts 100% 100% 100% 100%
Generated in workflow #14170 for commit 0a2eccc by the Vitest Coverage Report Action

@cuzz-venus cuzz-venus closed this Aug 27, 2026
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.

1 participant