Skip to content

Commit f8c075e

Browse files
committed
update: Token details page
1 parent a9a1bdf commit f8c075e

File tree

2 files changed

+239
-8
lines changed

2 files changed

+239
-8
lines changed

src/components/CCIP/Tables/TokenChainsTable.tsx

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import { Environment, SupportedTokenConfig, tokenPoolDisplay, PoolType } from "~
55
import { areAllLanesPaused } from "~/config/data/ccip/utils.ts"
66
import { ChainType, ExplorerInfo } from "~/config/types.ts"
77
import TableSearchInput from "./TableSearchInput.tsx"
8-
import { useState } from "react"
8+
import { useState, useEffect } from "react"
99
import { getExplorerAddressUrl, fallbackTokenIconUrl } from "~/features/utils/index.ts"
1010
import TokenDrawer from "../Drawer/TokenDrawer.tsx"
11+
import { Tooltip } from "~/features/common/Tooltip/Tooltip.tsx"
12+
import { RealtimeDataService } from "~/lib/ccip/services/realtime-data.ts"
13+
import type { TokenFinalityData } from "~/lib/ccip/types/index.ts"
1114

1215
interface TableProps {
1316
networks: {
@@ -42,6 +45,36 @@ interface TableProps {
4245

4346
function TokenChainsTable({ networks, token, lanes, environment }: TableProps) {
4447
const [search, setSearch] = useState("")
48+
const [finalityData, setFinalityData] = useState<Record<string, TokenFinalityData>>({})
49+
const [loading, setLoading] = useState(true)
50+
51+
console.log("[TokenChainsTable] Render - loading:", loading, "finalityData keys:", Object.keys(finalityData))
52+
53+
useEffect(() => {
54+
const fetchFinalityData = async () => {
55+
try {
56+
console.log("[TokenChainsTable] Starting fetch for token:", token.id, "env:", environment)
57+
const realtimeService = new RealtimeDataService()
58+
const result = await realtimeService.getTokenFinality(token.id, environment, "internal_id")
59+
console.log("[TokenChainsTable] Received result:", result)
60+
61+
if (result && result.data) {
62+
console.log("[TokenChainsTable] Setting finality data:", result.data)
63+
setFinalityData(result.data)
64+
} else {
65+
console.warn("[TokenChainsTable] No data received")
66+
}
67+
} catch (error) {
68+
console.error("Failed to fetch token finality data:", error)
69+
} finally {
70+
console.log("[TokenChainsTable] Setting loading to false")
71+
setLoading(false)
72+
}
73+
}
74+
75+
fetchFinalityData()
76+
}, [token.id, environment])
77+
4578
return (
4679
<>
4780
<div className="ccip-table__filters">
@@ -145,15 +178,47 @@ function TokenChainsTable({ networks, token, lanes, environment }: TableProps) {
145178
</td>
146179
<td>{network.tokenPoolVersion}</td>
147180
<td>
148-
{/* TODO: Fetch from API - GET /api/ccip/v1/tokens/{tokenCanonicalSymbol}/finality?environment={environment}
149-
Custom finality is derived from minBlockConfirmation > 0
150-
Display: "Yes" | "No" | "N/A" (with tooltip for unavailable) */}
151-
-
181+
{(() => {
182+
console.log(
183+
`[TokenChainsTable] Checking finality for network: ${network.name}, key: "${network.key}", hasData:`,
184+
!!finalityData[network.key],
185+
"finality:",
186+
finalityData[network.key]
187+
)
188+
return null
189+
})()}
190+
{loading ? (
191+
"-"
192+
) : finalityData[network.key] ? (
193+
finalityData[network.key].hasCustomFinality === null ? (
194+
<Tooltip
195+
label="N/A"
196+
tip="Custom finality data is currently unavailable. You can find the custom finality settings by reading the Token Pool contract directly on the relevant blockchain."
197+
labelStyle={{ marginRight: "5px" }}
198+
style={{ display: "inline-block", verticalAlign: "middle" }}
199+
/>
200+
) : finalityData[network.key].hasCustomFinality ? (
201+
"Yes"
202+
) : (
203+
"No"
204+
)
205+
) : (
206+
<Tooltip
207+
label="N/A"
208+
tip="Custom finality data is currently unavailable. You can find the custom finality settings by reading the Token Pool contract directly on the relevant blockchain."
209+
labelStyle={{ marginRight: "5px" }}
210+
style={{ display: "inline-block", verticalAlign: "middle" }}
211+
/>
212+
)}
152213
</td>
153214
<td>
154-
{/* TODO: Fetch from API - GET /api/ccip/v1/tokens/{tokenCanonicalSymbol}/finality?environment={environment}
155-
Display minBlockConfirmation value or "-" if custom finality is disabled/unavailable */}
156-
-
215+
{loading
216+
? "-"
217+
: finalityData[network.key]
218+
? finalityData[network.key].minBlockConfirmation === null
219+
? "-"
220+
: finalityData[network.key].minBlockConfirmation
221+
: "-"}
157222
</td>
158223
</tr>
159224
)
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { Environment } from "~/lib/ccip/types/index.ts"
2+
import type {
3+
TokenRateLimits,
4+
RateLimiterEntry,
5+
RateLimiterConfig,
6+
TokenFinalityData,
7+
OutputKeyType,
8+
} from "~/lib/ccip/types/index.ts"
9+
10+
export const prerender = false
11+
12+
/**
13+
* Base URL for CCIP realtime API
14+
* For client-side calls, use relative URLs to hit the local API endpoints
15+
*/
16+
const getApiBaseUrl = () => {
17+
// In browser context, use relative URLs
18+
if (typeof window !== "undefined") {
19+
return ""
20+
}
21+
// In server context, use environment variable or default
22+
return process.env.CCIP_REALTIME_API_BASE_URL || "https://api.ccip.chainlink.com"
23+
}
24+
25+
/**
26+
* Response structure for lane supported tokens endpoint
27+
*/
28+
export interface LaneSupportedTokensResponse {
29+
metadata: {
30+
environment: Environment
31+
timestamp: string
32+
requestId: string
33+
sourceChain: string
34+
destinationChain: string
35+
tokenCount: number
36+
}
37+
supportedTokens: Record<string, TokenRateLimits>
38+
}
39+
40+
/**
41+
* Response structure for token finality endpoint
42+
*/
43+
export interface TokenFinalityResponse {
44+
metadata: {
45+
environment: Environment
46+
timestamp: string
47+
requestId: string
48+
tokenSymbol: string
49+
chainCount: number
50+
}
51+
data: Record<string, TokenFinalityData>
52+
}
53+
54+
/**
55+
* Service class for handling CCIP realtime data operations
56+
* Provides functionality to fetch live data from the CCIP API
57+
*/
58+
export class RealtimeDataService {
59+
private readonly requestId: string
60+
61+
/**
62+
* Creates a new instance of RealtimeDataService
63+
*/
64+
constructor() {
65+
// Generate UUID - handle both browser and server environments
66+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
67+
this.requestId = crypto.randomUUID()
68+
} else {
69+
this.requestId = `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`
70+
}
71+
}
72+
73+
/**
74+
* Fetches supported tokens with rate limits for a specific lane
75+
*
76+
* @param sourceInternalId - Source chain internal ID
77+
* @param destinationInternalId - Destination chain internal ID
78+
* @param environment - Network environment (mainnet/testnet)
79+
* @returns Supported tokens with rate limits
80+
*/
81+
async getLaneSupportedTokens(
82+
sourceInternalId: string,
83+
destinationInternalId: string,
84+
environment: Environment
85+
): Promise<LaneSupportedTokensResponse | null> {
86+
try {
87+
const baseUrl = getApiBaseUrl()
88+
const url = `${baseUrl}/api/ccip/v1/lanes/by-internal-id/${sourceInternalId}/${destinationInternalId}/supported-tokens?environment=${environment}`
89+
90+
const response = await fetch(url)
91+
92+
if (!response.ok) {
93+
return null
94+
}
95+
96+
const data = await response.json()
97+
return data
98+
} catch (error) {
99+
console.error("Error fetching lane supported tokens:", error)
100+
return null
101+
}
102+
}
103+
104+
/**
105+
* Fetches token finality details across all chains
106+
*
107+
* @param tokenCanonicalSymbol - Token canonical symbol (e.g., "BETS", "LINK")
108+
* @param environment - Network environment (mainnet/testnet)
109+
* @param outputKey - Format to use for displaying chain keys (optional)
110+
* @returns Token finality data for all chains
111+
*/
112+
async getTokenFinality(
113+
tokenCanonicalSymbol: string,
114+
environment: Environment,
115+
outputKey?: OutputKeyType
116+
): Promise<TokenFinalityResponse | null> {
117+
try {
118+
const baseUrl = getApiBaseUrl()
119+
let url = `${baseUrl}/api/ccip/v1/tokens/${tokenCanonicalSymbol}/finality?environment=${environment}`
120+
121+
if (outputKey) {
122+
url += `&output_key=${outputKey}`
123+
}
124+
125+
const response = await fetch(url)
126+
127+
if (!response.ok) {
128+
console.error("Failed to fetch token finality:", response.status)
129+
return null
130+
}
131+
132+
const data = await response.json()
133+
return data
134+
} catch (error) {
135+
console.error("Error fetching token finality:", error)
136+
return null
137+
}
138+
}
139+
140+
/**
141+
* Checks if rate limiter data is unavailable (null)
142+
*
143+
* @param entry - Rate limiter entry to check
144+
* @returns True if unavailable (null)
145+
*/
146+
isRateLimiterUnavailable(entry: RateLimiterEntry): entry is null {
147+
return entry === null
148+
}
149+
150+
/**
151+
* Checks if rate limiter is enabled
152+
*
153+
* @param config - Rate limiter configuration
154+
* @returns True if enabled
155+
*/
156+
isRateLimiterEnabled(config: RateLimiterConfig): boolean {
157+
return config.isEnabled
158+
}
159+
160+
/**
161+
* Gets the request ID for this service instance
162+
*/
163+
getRequestId(): string {
164+
return this.requestId
165+
}
166+
}

0 commit comments

Comments
 (0)