Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/playground-web/src/app/data/pages-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
SquareIcon,
SquircleDashedIcon,
StampIcon,
TrendingUpIcon,
UserIcon,
WalletCardsIcon,
} from "lucide-react";
Expand Down Expand Up @@ -203,6 +204,13 @@ export const tokensFeatureCards: FeatureCardMetadata[] = [
link: "/tokens/nft-components",
description: "Headless UI components for rendering NFT Media and metadata",
},
{
icon: TrendingUpIcon,
title: "Price Tracker",
link: "/tokens/price-tracker",
description:
"Live token prices, market cap, and 24h volume across chains",
},
];

export const aiFeatureCards: FeatureCardMetadata[] = [
Expand Down
4 changes: 4 additions & 0 deletions apps/playground-web/src/app/navLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ const tokens: ShadcnSidebarLink = {
href: "/tokens/nft-components",
label: "NFT Components",
},
{
href: "/tokens/price-tracker",
label: "Price Tracker",
},
],
};

Expand Down
34 changes: 34 additions & 0 deletions apps/playground-web/src/app/tokens/price-tracker/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { TrendingUpIcon } from "lucide-react";
import { PageLayout } from "@/components/blocks/APIHeader";
import { TokenPriceTracker } from "@/components/token-price/token-price-tracker";
import ThirdwebProvider from "@/components/thirdweb-provider";
import { createMetadata } from "@/lib/metadata";

const title = "Price Tracker";
const description =
"Live token prices, market cap, and 24h volume across chains";

export const metadata = createMetadata({
title,
description,
image: {
icon: "wallets",
title,
},
});

export default function Page() {
return (
<ThirdwebProvider>
<PageLayout
containerClassName="space-y-12"
icon={TrendingUpIcon}
description={description}
docsLink="https://portal.thirdweb.com/typescript/v5/bridge/get-supported-tokens?utm_source=playground"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file excerpt =="
if [ -f apps/playground-web/src/app/tokens/price-tracker/page.tsx ]; then
  nl -ba apps/playground-web/src/app/tokens/price-tracker/page.tsx | sed -n '1,120p'
else
  echo "file not found"
fi

echo
echo "== related docs links in repository =="
rg -n "portal\.thirdweb\.com|get-supported-tokens|Bridge\.tokens|bridge/get-supported-tokens|docsLink" apps/playground-web/src apps -g '*.tsx' -g '*.ts' | head -n 80

Repository: thirdweb-dev/js

Length of output: 212


🌐 Web query:

Third.js bridge get supported tokens TypeScript docs portal.thirdweb.com

💡 Result:

To retrieve supported tokens for the thirdweb bridge in the TypeScript SDK, you use the Bridge.tokens utility [1][2]. This function allows you to query, filter, and paginate through supported bridge tokens [1][3]. You can access this functionality by importing Bridge from the thirdweb package [1][2]: import { Bridge } from "thirdweb"; The Bridge.tokens function accepts an options object to filter results, which acts as an AND operator when multiple filters are applied [1][4][2]. Common usage examples include: Filtering by chainId: const ethTokens = await Bridge.tokens({ chainId: 1, client: thirdwebClient, }); Searching by symbol or name: const usdcTokens = await Bridge.tokens({ symbol: "USDC", client: thirdwebClient, }); const tokensByName = await Bridge.tokens({ name: "Ethereum", client: thirdwebClient, }); Retrieving a specific token by address: const token = await Bridge.tokens({ tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", client: thirdwebClient, }); For large datasets, you can paginate the results using the limit and offset parameters: const tokens = await Bridge.tokens({ limit: 50, offset: 0, client: thirdwebClient, }); For complete details on the function signature and available options, refer to the official thirdweb TypeScript SDK reference documentation [1][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
git ls-files 'apps/playground-web/src/app/tokens/price-tracker/page.tsx' || true

echo
echo "== file excerpt =="
python3 - <<'PY'
from pathlib import Path
p = Path('apps/playground-web/src/app/tokens/price-tracker/page.tsx')
if p.exists():
    text = p.read_text()
    for i, line in enumerate(text.splitlines()[:140], 1):
        print(f'{i:4d}\t{line}')
else:
    print('file not found')
PY

echo
echo "== related docs links in repository =="
rg -n "portal\.thirdweb\.com|get-supported-tokens|Bridge\.tokens|bridge/get-supported-tokens|docsLink|supported-tokens" apps/playground-web/src apps -g '*.tsx' -g '*.ts' | head -n 120 || true

echo
echo "== curl docs redirect status =="
python3 - <<'PY'
import urllib.request
urls = [
    "https://portal.thirdweb.com/typescript/v5/bridge/get-supported-tokens?utm_source=playground",
    "https://portal.thirdweb.com/typescript/v5/bridge",
    "https://portal.thirdweb.com/typescript/v5/tokens",
    "https://portal.thirdweb.com/bridge/tokens",
]
for url in urls:
    try:
        req = urllib.request.Request(url, method='HEAD', headers={'User-Agent':'Mozilla/5.0'})
        with urllib.request.urlopen(req, timeout=15) as r:
            print(f"{r.status}\t<r={r.url.upper()}\n{url}")
    except Exception as e:
        print(f"ERROR {url}: {type(e).__name__}: {e}")
PY

Repository: thirdweb-dev/js

Length of output: 19042


🌐 Web query:

site:portal.thirdweb.com/references/typescript/v5 Bridge.tokens thirdweb

💡 Result:

In the thirdweb TypeScript SDK, Bridge.tokens is a function used to retrieve a list of supported tokens available for bridging operations [1]. Key details about Bridge.tokens include: Functionality: It returns an array of tokens that are supported for bridging, which can be filtered based on specific criteria such as chain ID, token symbol, token name, or token address [1]. Usage: It is part of the thirdweb Bridge module and requires a thirdweb client instance [1]. Filtering: When multiple filters are provided, they act as an AND operator, meaning a token must satisfy all specified conditions to be included in the results [1]. Pagination: The function supports pagination through limit and offset parameters to manage the size of the returned token list [1]. This function is one component of the broader thirdweb Bridge infrastructure, which also includes utilities for fetching supported chains, generating quotes for buy/sell intentions, and preparing transactions for cross-chain operations [1][2][3][4].

Citations:


Use the Bridge.tokens reference for the Docs link.

Price Tracker showcases Bridge.tokens, so this link should go to the current supported-tokens documentation instead of the generic SDK landing page. (portal.thirdweb.com/references/typescript/v5/tokens)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/playground-web/src/app/tokens/price-tracker/page.tsx` at line 27, Update
the docsLink prop in the Price Tracker page to use the Bridge.tokens reference
URL for supported tokens, replacing the current generic bridge documentation
link while leaving the surrounding configuration unchanged.

title={title}
>
<TokenPriceTracker />
</PageLayout>
</ThirdwebProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
"use client";

import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { Bridge } from "thirdweb";
import {
arbitrum,
base,
ethereum,
optimism,
polygon,
} from "thirdweb/chains";
import { THIRDWEB_CLIENT } from "@/lib/client";
import { CodeExample } from "../code/code-example";

const CHAINS = [
{ chain: ethereum, label: "Ethereum", id: 1 },
{ chain: base, label: "Base", id: 8453 },
{ chain: polygon, label: "Polygon", id: 137 },
{ chain: arbitrum, label: "Arbitrum", id: 42161 },
{ chain: optimism, label: "Optimism", id: 10 },
] as const;

function formatUsd(value: number): string {
if (value >= 1_000_000_000) {
return `$${(value / 1_000_000_000).toFixed(2)}B`;
}
if (value >= 1_000_000) {
return `$${(value / 1_000_000).toFixed(2)}M`;
}
if (value >= 1_000) {
return `$${(value / 1_000).toFixed(2)}K`;
}
return `$${value.toFixed(2)}`;
}

function formatPrice(value: number): string {
if (value >= 1) {
return `$${value.toFixed(2)}`;
}
if (value >= 0.01) {
return `$${value.toFixed(4)}`;
}
return `$${value.toFixed(6)}`;
}

function TokenPriceTrackerPreview() {
const [selectedChainId, setSelectedChainId] = useState(1);

const tokensQuery = useQuery({
queryKey: ["bridge-tokens-price", selectedChainId],
queryFn: () =>
Bridge.tokens({
client: THIRDWEB_CLIENT,
chainId: selectedChainId,
limit: 15,
sortBy: "market_cap",
includePrices: true,
}),
refetchInterval: 30_000,
});

return (
<div className="w-full max-w-3xl space-y-4 px-4">
{/* Chain Selector */}
<div className="flex items-center gap-2 flex-wrap">
{CHAINS.map((c) => (
<button
key={c.id}
type="button"
onClick={() => setSelectedChainId(c.id)}
className={`rounded-full border px-3 py-1 text-xs font-medium transition-colors ${
selectedChainId === c.id
? "border-primary bg-primary text-primary-foreground"
: "border-border bg-background text-muted-foreground hover:bg-accent"
}`}
>
{c.label}
</button>
))}
Comment on lines +68 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the design-system button and expose the selected chain.

Use Button with cn() and add aria-pressed so assistive technology can identify the active chain. Also expose className on TokenPriceTracker’s root element for consumer overrides.

As per coding guidelines, “Import UI component primitives from @/components/ui/*,” “Use cn(),” and “Expose className prop on root element for component overrides.”

Also applies to: 170-208

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/playground-web/src/components/token-price/token-price-tracker.tsx`
around lines 68 - 80, Update TokenPriceTracker to use the design-system Button
imported from the appropriate `@/components/ui` module, compose its conditional
styles with cn(), and add aria-pressed based on selectedChainId. Extend
TokenPriceTracker’s props with an optional className and apply it to the root
element through cn() so consumers can override its styles.

Source: Coding guidelines

</div>

{/* Token List */}
<div className="rounded-lg border bg-card">
{/* Header */}
<div className="grid grid-cols-[1fr_auto_auto_auto] gap-4 border-b px-4 py-2.5 text-xs font-medium text-muted-foreground">
<span>Token</span>
<span className="w-24 text-right">Price</span>
<span className="hidden w-24 text-right sm:block">Market Cap</span>
<span className="hidden w-24 text-right sm:block">24h Volume</span>
</div>

{/* Loading State */}
{tokensQuery.isLoading && (
<div className="space-y-0">
{Array.from({ length: 8 }).map((_, i) => (
<div
key={`skeleton-${i.toString()}`}
className="grid grid-cols-[1fr_auto_auto_auto] gap-4 border-b px-4 py-3 last:border-b-0"
>
<div className="flex items-center gap-3">
<div className="size-8 animate-pulse rounded-full bg-muted" />
<div className="space-y-1.5">
<div className="h-3.5 w-20 animate-pulse rounded bg-muted" />
<div className="h-3 w-12 animate-pulse rounded bg-muted" />
</div>
</div>
<div className="h-4 w-24 animate-pulse self-center rounded bg-muted" />
<div className="hidden h-4 w-24 animate-pulse self-center rounded bg-muted sm:block" />
<div className="hidden h-4 w-24 animate-pulse self-center rounded bg-muted sm:block" />
</div>
))}
</div>
)}

{/* Error State */}
{tokensQuery.isError && (
<div className="px-4 py-8 text-center text-sm text-muted-foreground">
Failed to load token data. Please try again.
</div>
)}

{/* Data */}
{tokensQuery.data?.map((token) => (
<div
key={`${token.chainId}-${token.address}`}
className="grid grid-cols-[1fr_auto_auto_auto] gap-4 border-b px-4 py-3 last:border-b-0 hover:bg-accent/50 transition-colors"
>
<div className="flex items-center gap-3">
{token.iconUri ? (
<img
src={token.iconUri}
alt={token.name}
className="size-8 rounded-full"
/>
) : (
<div className="size-8 rounded-full bg-muted" />
)}
<div>
<p className="text-sm font-medium leading-tight">
{token.name}
</p>
<p className="text-xs text-muted-foreground">{token.symbol}</p>
</div>
</div>
<span className="w-24 self-center text-right text-sm font-medium tabular-nums">
{token.prices?.usd ? formatPrice(token.prices.usd) : "—"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)token-price-tracker\.tsx$|/^package\.json$|pnpm-lock\.yaml$' || true

echo "== tracker outline =="
ast-grep outline apps/playground-web/src/components/token-price/token-price-tracker.tsx || true

echo "== tracker relevant lines =="
cat -n apps/playground-web/src/components/token-price/token-price-tracker.tsx | sed -n '1,230p'

echo "== search Bridge.tokens/price tracker usage =="
rg -n "Bridge\.tokens|prices\.(USD|usd)|token-price-tracker|price-tracker|from \"thirdweb\"" apps/playground-web/src . --glob '!node_modules' --glob '!dist' --glob '!build' | head -200

Repository: thirdweb-dev/js

Length of output: 28540


🌐 Web query:

thirdweb TypeScript Bridge.tokens prices USD currency prices documentation

💡 Result:

You can retrieve token metadata and pricing information using the thirdweb TypeScript SDK's Bridge.tokens utility [1][2]. The Bridge.tokens method allows you to query supported bridge tokens with various filters, including specific token addresses, chain IDs, symbols, and names [1][2]. The data returned by this utility includes a prices object, which provides the token's current value in multiple currencies, including USD [1][3]. Example usage to retrieve a specific token: import { Bridge } from "thirdweb"; // Retrieve USDC token information on Ethereum const token = await Bridge.tokens({ chainId: 1, tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", client, }); // Access the price in USD console.log(token.prices.USD); Additionally, if you specifically need to convert a crypto amount to a fiat value, you can use the convertCryptoToFiat function from the thirdweb/pay package [4][5]. Note that this specific function currently only supports USD as the target fiat currency [4][5]. Example usage for conversion: import { convertCryptoToFiat } from "thirdweb/pay"; import { NATIVE_TOKEN_ADDRESS } from "thirdweb"; // Get the price of 1 ETH in USD const result = await convertCryptoToFiat({ fromTokenAddress: NATIVE_TOKEN_ADDRESS, to: "USD", chain: ethereum, fromAmount: 1, }); console.log(result.result); // Returns the price in USD

Citations:


Read the documented uppercase USD price key.

Bridge.tokens() exposes USD pricing on prices.USD; lowercase prices.usd skips each row’s price value in both the preview and the copyable example.

  • apps/playground-web/src/components/token-price/token-price-tracker.tsx#L147: replace token.prices?.usd with token.prices?.USD.
  • apps/playground-web/src/components/token-price/token-price-tracker.tsx#L199: replace token.prices?.usd with token.prices?.USD.
📍 Affects 1 file
  • apps/playground-web/src/components/token-price/token-price-tracker.tsx#L147-L147 (this comment)
  • apps/playground-web/src/components/token-price/token-price-tracker.tsx#L199-L199
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/playground-web/src/components/token-price/token-price-tracker.tsx` at
line 147, Update both token price references in token-price-tracker.tsx at lines
147-147 and 199-199 to read the documented uppercase prices.USD key instead of
prices.usd, preserving the existing formatting and fallback behavior.

</span>
<span className="hidden w-24 self-center text-right text-xs text-muted-foreground tabular-nums sm:block">
{token.marketCapUsd ? formatUsd(token.marketCapUsd) : "—"}
</span>
<span className="hidden w-24 self-center text-right text-xs text-muted-foreground tabular-nums sm:block">
{token.volume24hUsd ? formatUsd(token.volume24hUsd) : "—"}
</span>
</div>
))}
</div>

{/* Auto-refresh indicator */}
<p className="text-center text-xs text-muted-foreground">
Auto-refreshes every 30s
{tokensQuery.isFetching && !tokensQuery.isLoading && (
<span className="ml-1.5 inline-block size-2 animate-pulse rounded-full bg-green-500" />
)}
</p>
</div>
);
}

export function TokenPriceTracker() {
return (
<CodeExample
header={{
title: "Token Price Tracker",
description:
"Fetch live token prices, market cap, and 24h volume using the Bridge.tokens() API with multi-chain support and auto-refresh.",
}}
code={`import { Bridge } from "thirdweb";
import { useQuery } from "@tanstack/react-query";

function App() {
const { data: tokens } = useQuery({
queryKey: ["tokens", chainId],
queryFn: () =>
Bridge.tokens({
client: THIRDWEB_CLIENT,
chainId: 1, // Ethereum
limit: 15,
sortBy: "market_cap",
includePrices: true,
}),
refetchInterval: 30_000, // auto-refresh every 30s
});

return tokens?.map((token) => (
<div key={token.address}>
<img src={token.iconUri} alt={token.name} />
<span>{token.name} ({token.symbol})</span>
<span>\${token.prices?.usd?.toFixed(2)}</span>
<span>MCap: {token.marketCapUsd}</span>
<span>Vol: {token.volume24hUsd}</span>
</div>
));
}`}
lang="tsx"
preview={<TokenPriceTrackerPreview />}
/>
);
}
Loading