Skip to content

Conversation

@markshenouda
Copy link
Member

@markshenouda markshenouda commented Dec 16, 2025

image image image image

@markshenouda markshenouda self-assigned this Dec 16, 2025
@markshenouda markshenouda linked an issue Dec 16, 2025 that may be closed by this pull request
2 tasks
@roomote
Copy link

roomote bot commented Dec 17, 2025

Rooviewer Claude Clock   See task on Roo Cloud

Reviewed latest commit (fix: typecheck). Clean fix that adds .ts extensions to imports and aligns ProcessedToken.rateLimits type with the return type of getAllRateLimitsForDirection() by changing from optional properties to explicit | null union types. No new issues found. The 2 previously flagged issues remain unresolved:

  • rate-limit-formatter.ts: formatRateLimit hardcodes 18 decimals - tokens with different decimals (USDC, USDT, WBTC) will display incorrect values
  • TokenDrawer.tsx: laneConfigs useMemo includes activeTab in dependencies, causing unnecessary API calls when switching to "verifiers" tab
Previous reviews

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

Comment on lines 229 to 235
const formatRateLimit = (value: string | null) => {
if (!value || value === "0") return "0"
// Convert from wei to tokens (divide by 1e18)
const numValue = BigInt(value)
const formatted = Number(numValue) / 1e18
return formatted.toLocaleString(undefined, { maximumFractionDigits: 2 })
}
Copy link

Choose a reason for hiding this comment

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

The formatRateLimit function hardcodes division by 1e18, assuming all tokens have 18 decimals. This will display incorrect values for tokens like USDC (6 decimals), USDT (6 decimals), or WBTC (8 decimals). For example, a USDC rate limit of 1000000 (1 USDC) would display as 0.000000000001 instead of 1. Consider passing the token's decimals and using 10 ** decimals for the conversion.

Fix it with Roo Code or mention @roomote and request a fix.

Comment on lines 83 to 103
useEffect(() => {
const fetchAllRateLimits = async () => {
const realtimeService = new RealtimeDataService()
const newRateLimits: Record<string, Record<string, TokenRateLimits>> = {}

for (const destinationChain of Object.keys(destinationLanes)) {
const source = activeTab === "outbound" ? network.key : destinationChain
const destination = activeTab === "outbound" ? destinationChain : network.key
const laneKey = `${source}-${destination}`

const response = await realtimeService.getLaneSupportedTokens(source, destination, environment)
if (response?.data) {
newRateLimits[laneKey] = response.data
}
}

setRateLimits(newRateLimits)
}

fetchAllRateLimits()
}, [network.key, destinationLanes, environment, activeTab])
Copy link

Choose a reason for hiding this comment

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

This useEffect runs when activeTab changes, including when it becomes "verifiers". Since activeTab === "outbound" is false for the verifiers tab, the logic treats it as "inbound" and makes unnecessary API calls. Consider adding an early return when activeTab === "verifiers" to avoid fetching rate limits data that won't be displayed.

Fix it with Roo Code or mention @roomote and request a fix.

Copy link

@Zelig880 Zelig880 left a comment

Choose a reason for hiding this comment

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

One more generic comment, it does not seem like we are handling Failure of the APi request well. We log the issue, but the UI does not seem to have a "error" and may default to a value like 0 while it is incorrect.

setIsLoadingRateLimits(false)
}

fetchRateLimits()

Choose a reason for hiding this comment

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

You are not cleaning up the Fetch Request. This is going to results in many fetch possibly being run at the same time.

// Get standard and FTF rate limits
const allLimits = tokenRateLimits
? realtimeService.getAllRateLimitsForDirection(tokenRateLimits, direction)
: { standard: null, ftf: null }

Choose a reason for hiding this comment

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

We are in control of the "getAllRateLimitsForDirection" so we should just return the default or whatever we want to simplify the code below. we should not have to know the "structure" to default it to null here.

Comment on lines 283 to 303
{isLoadingRateLimits ? (
"Loading..."
) : allLimits.standard ? (
allLimits.standard.isEnabled ? (
formatRateLimit(allLimits.standard.capacity)
) : (
"Disabled"
)
) : (
<span style={{ display: "inline-flex", alignItems: "center", gap: "4px" }}>
Unavailable
<Tooltip
label=""
tip="Rate limit data is currently unavailable. You can find the Token Pool rate limit by reading the Token Pool contract directly on the relevant blockchain."
style={{
display: "inline-block",
verticalAlign: "middle",
}}
/>
</span>
)}

Choose a reason for hiding this comment

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

All these conditions should not happen in the UI. Please abstruct it.. If you find yourself doign an if/ifsele/else, you should abstract. It is very hard to ready

Choose a reason for hiding this comment

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

Same apply to all the i, if else present in this PR

}) {
const [search, setSearch] = useState("")
const [inOutbound, setInOutbound] = useState<LaneFilter>(LaneFilter.Outbound)
const [activeTab, setActiveTab] = useState<"outbound" | "inbound" | "verifiers">("outbound")

Choose a reason for hiding this comment

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

Not sure why you went away from using a type, if required add a new one, but this does not seem correct

const destination = activeTab === "outbound" ? destinationChain : network.key
const laneKey = `${source}-${destination}`

const response = await realtimeService.getLaneSupportedTokens(source, destination, environment)

Choose a reason for hiding this comment

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

We are using "await" in a for loop. This may slow things down. I wouls suggest to run all this in parallel.

// Fetch rate limits for all lanes
useEffect(() => {
const fetchAllRateLimits = async () => {
const realtimeService = new RealtimeDataService()

Choose a reason for hiding this comment

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

Apply to all useEffect, const realtimeService = new RealtimeDataService(), should NOT be initiated within the useEffect, as it iw ill result in logs of memory usage and garbage collection work for no reason.

},
]}
onChange={(key) => setInOutbound(key as LaneFilter)}
onChange={(key) => setActiveTab(key as "outbound" | "inbound" | "verifiers")}

Choose a reason for hiding this comment

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

As mentioned above, please re-introduce the type

Comment on lines 213 to 223
verifiers
.filter((verifier) => {
if (!search) return true
const searchLower = search.toLowerCase()
return (
verifier.name.toLowerCase().includes(searchLower) ||
verifier.address.toLowerCase().includes(searchLower) ||
verifier.type.toLowerCase().includes(searchLower)
)
})
.map((verifier) => (

Choose a reason for hiding this comment

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

This operation should not happen here. Make a reusable function for this!

Comment on lines 317 to 340
// Get rate limit data for this lane
const source = activeTab === "outbound" ? network.key : destinationChain
const destination = activeTab === "outbound" ? destinationChain : network.key
const laneKey = `${source}-${destination}`
const laneRateLimits = rateLimits[laneKey]
const tokenRateLimits = laneRateLimits?.[token.id]

return (
<tr key={networkDetails.name} className={tokenPaused ? "ccip-table__row--paused" : ""}>
<td>
<button
type="button"
className={`ccip-table__network-name ${tokenPaused ? "ccip-table__network-name--paused" : ""}`}
onClick={() => {
drawerWidthStore.set(DrawerWidth.Wide)
drawerContentStore.set(() => (
<LaneDrawer
environment={environment}
lane={laneData}
sourceNetwork={network}
destinationNetwork={{
name: networkDetails?.name || "",
logo: networkDetails?.logo || "",
key: destinationChain,
}}
inOutbound={inOutbound}
explorer={network.explorer}
/>
))
}}
aria-label={`View lane details for ${networkDetails?.name}`}
>
<img
src={networkDetails?.logo}
alt={`${networkDetails?.name} blockchain logo`}
className="ccip-table__logo"
/>
{networkDetails?.name}
{tokenPaused && (
<span className="ccip-table__paused-badge" title="Transfers are currently paused">
⏸️
</span>
)}
</button>
</td>
<td>
{/* TODO: Fetch rate limits from API for both inbound and outbound
GET /api/ccip/v1/lanes/by-internal-id/{source}/{destination}/supported-tokens?environment={environment}
Response will contain both standard and custom rate limits per token */}
Disabled
</td>
<td>
{/* TODO: Fetch rate limits from API for both inbound and outbound
Display refill rate from standard.in/out or custom.in/out based on inOutbound filter */}
Disabled
</td>
<td>
{inOutbound === LaneFilter.Outbound
? determineTokenMechanism(network.tokenPoolType, destinationPoolType)
: determineTokenMechanism(destinationPoolType, network.tokenPoolType)}
</td>
{/* <td>
const realtimeService = new RealtimeDataService()
const direction = activeTab === "outbound" ? "out" : "in"

// Get standard and FTF rate limits
const allLimits = tokenRateLimits
? realtimeService.getAllRateLimitsForDirection(tokenRateLimits, direction)
: { standard: null, ftf: null }

// Token is paused if standard rate limit capacity is 0
const tokenPaused = allLimits.standard?.capacity === "0"

// Format rate limit values
const formatRateLimit = (value: string | null) => {
if (!value || value === "0") return "0"
const numValue = BigInt(value)
const formatted = Number(numValue) / 1e18
return formatted.toLocaleString(undefined, { maximumFractionDigits: 2 })

Choose a reason for hiding this comment

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

This logic should not live in a the UI

Choose a reason for hiding this comment

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

Also, lots of this logic is the same as the other file, please abstract

Comment on lines 384 to 390
{isLoading
? "Loading..."
: allLimits.standard
? allLimits.standard.isEnabled
? formatRateLimit(allLimits.standard.capacity)
: "Disabled"
: "N/A"}

Choose a reason for hiding this comment

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

Other occurances of If/elseif/else

markshenouda and others added 8 commits December 17, 2025 19:51
- Create custom hooks for data fetching:
  * useTokenRateLimits - single lane rate limits
  * useMultiLaneRateLimits - multiple lanes in parallel
  * useTokenFinality - token finality data

- Add RateLimitCell component to replace complex nested ternaries

- Create singleton realtimeDataService instance to avoid multiple instantiations

- Add rate-limit-formatter utilities for consistent formatting

- Update LaneDrawer, TokenDrawer, and TokenChainsTable to use new hooks

- Add proper cleanup in useEffect hooks to prevent memory leaks

Benefits:
- Reduced code duplication (~133 lines removed)
- Improved readability with <RateLimitCell> vs 7-level nested ternaries
- Better separation of concerns
- More testable code
- Proper cleanup handling
* feat: add verifiers to the search

* refactor: simplify verifiers mapping in Chain component

* refactor: simplify verifiers mapping in CCIP components

* refactor: optimize token mapping for verifiers in CCIP components
@Zelig880 Zelig880 merged commit 6a79b0b into ccip-1-7-updated Dec 19, 2025
12 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CCIP] Token Details Side pannel

3 participants