Skip to content
Merged
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: 1 addition & 7 deletions apps/explorer/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Index Supply API key (server-side, legacy)
INDEXER_API_KEY=""
NODE_ENV="development"

# TIDX basic auth (server-side), format: username:password
TIDX_BASIC_AUTH=""
Expand All @@ -24,15 +23,10 @@ VITE_BASE_URL=""
# --------------------------------------------------------
# VITE_TEMPO_ENV="testnet" # devnet | mainnet | testnet

# demo screens
# VITE_ENABLE_DEMO=false

# show react query + react router devtools
# VITE_ENABLE_DEVTOOLS=true

# (dev/preview) comma-separated list of allowed hosts to use in vite config. See
# https://vite.dev/config/server-options
# https://vite.dev/config/preview-options#preview-allowedhosts
# ALLOWED_HOSTS=""

TIDX_BASIC_AUTH=""
1 change: 0 additions & 1 deletion apps/explorer/env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
interface EnvironmentVariables {
readonly INDEXER_API_KEY: string | undefined
readonly TIDX_BASIC_AUTH: string | undefined
readonly SENTRY_AUTH_TOKEN: string | undefined
readonly SENTRY_ORG: string | undefined
Expand Down
50 changes: 44 additions & 6 deletions apps/explorer/src/lib/server/tempo-queries.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Address, Hex } from 'ox'
import * as OxHash from 'ox/Hash'
import * as OxHex from 'ox/Hex'
import { Tidx } from 'tidx.ts'
import { decodeAbiParameters, zeroAddress } from 'viem'
import * as ABIS from '#lib/abis'
import { tempoQueryBuilder } from '#lib/server/tempo-queries-provider'
Expand Down Expand Up @@ -140,18 +141,55 @@ export async function fetchTokenHoldersCountRows(
})
}

/**
* The tidx server silently caps query results at this many rows.
* Queries that would return more get truncated without any error or metadata.
*/
const TIDX_SERVER_ROW_LIMIT = 10_000

export async function fetchTokenHoldersCount(
address: Address.Address,
chainId: number,
countCap: number,
): Promise<{ count: number; capped: boolean }> {
const holders = await fetchTokenHolderBalances(address, chainId)
const rawCount = holders.length
const capped = rawCount >= countCap
try {
const qb = QB(chainId).withSignatures([TRANSFER_SIGNATURE])
const transfers = (await qb
.selectFrom('transfer')
.select((eb) => [
eb.ref('from').as('from'),
eb.ref('to').as('to'),
eb.fn.sum('tokens').as('tokens'),
])
.where('address', '=', address)
.groupBy(['from', 'to'])
.execute()) as TokenHolderAggregationRow[]

// The tidx server caps results at 10,000 rows. If we hit that ceiling,
// the data is truncated and the in-memory aggregation would be incorrect.
// Return a capped count instead of computing from incomplete data.
if (transfers.length >= TIDX_SERVER_ROW_LIMIT) {
return { count: countCap, capped: true }
}

return {
count: capped ? countCap : rawCount,
capped,
const holders = aggregateTokenHolderBalances(transfers)
const rawCount = holders.length
const capped = rawCount >= countCap

return {
count: capped ? countCap : rawCount,
capped,
}
} catch (error) {
// Only return a capped fallback for 422 (query too expensive, i.e. too many holders).
// For other errors (auth, network, 5xx), re-throw so callers handle them normally.
if (error instanceof Tidx.FetchRequestError && error.status === 422) {
console.error(
`[tidx] holders count query failed for ${address} (422), returning capped`,
)
return { count: countCap, capped: true }
}
throw error
}
}

Expand Down
11 changes: 10 additions & 1 deletion apps/explorer/src/lib/server/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,16 @@ export const fetchTokens = createServerFn({ method: 'POST' })
transferAggregate: await fetchTokenTransferAggregate(
address,
chainId,
),
).catch((error) => {
console.error(
`Failed to fetch transfer aggregate for ${address}:`,
error,
)
return {
oldestTimestamp: undefined,
latestTimestamp: undefined,
}
}),
holdersCount: await fetchTokenHoldersCount(
address,
chainId,
Expand Down
6 changes: 1 addition & 5 deletions apps/explorer/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ import { getVendorChunk } from './scripts/chunk-config.ts'

import wranglerJSON from '#wrangler.json' with { type: 'json' }

const enabledSchema = z.stringbool({
truthy: ['true', '1', 'yes', 'on', 'y', 'enabled'],
falsy: ['false', '0', 'no', 'off', 'n', 'disabled'],
})
const enabledSchema = z.stringbool()

const canonicalTempoEnvSchema = z.union([
z.literal('devnet'),
Expand All @@ -45,7 +42,6 @@ const tempoEnvSchema = z.prefault(

const envConfigSchema = z.object({
PORT: z.prefault(z.coerce.number(), 3_007),
VITE_ENABLE_DEMO: z.prefault(enabledSchema, 'true'),
CLOUDFLARE_ENV: tempoEnvSchema,
VITE_TEMPO_ENV: tempoEnvSchema,
VITE_ENABLE_DEVTOOLS: z.prefault(enabledSchema, 'false'),
Expand Down
216 changes: 108 additions & 108 deletions apps/tokenlist/data/4217/tokenlist.json
Original file line number Diff line number Diff line change
@@ -1,110 +1,110 @@
{
"$schema": "https://esm.sh/gh/uniswap/token-lists/src/tokenlist.schema.json",
"name": "Tempo Mainnet (Presto)",
"logoURI": "https://esm.sh/gh/tempoxyz/tokenlist/data/4217/icon.svg",
"timestamp": "2026-03-21T05:24:26Z",
"version": {
"major": 1,
"minor": 1,
"patch": 9
},
"tokens": [
{
"name": "PathUSD",
"symbol": "pathUSD",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000000000000000000000",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000000000000000000000.svg",
"extensions": {
"chain": "tempo",
"label": "PathUSD"
}
},
{
"name": "Bridged USDC (Stargate)",
"symbol": "USDC.e",
"decimals": 6,
"chainId": 4217,
"address": "0x20c000000000000000000000b9537d11c60e8b50",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg",
"extensions": {
"chain": "tempo",
"label": "USDC"
}
},
{
"name": "Bridged EURC (Stargate)",
"symbol": "EURC.e",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000001621e21f71cf12fb",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg",
"extensions": {
"chain": "tempo",
"label": "EURC"
}
},
{
"name": "USDT0",
"symbol": "USDT0",
"decimals": 6,
"chainId": 4217,
"address": "0x20c00000000000000000000014f22ca97301eb73",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg",
"extensions": {
"chain": "tempo"
}
},
{
"name": "Frax USD",
"symbol": "frxUSD",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000003554d28269e0f3c2",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg",
"extensions": {
"chain": "tempo"
}
},
{
"name": "Cap USD",
"symbol": "cUSD",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000000520792dcccccccc",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg",
"extensions": {
"chain": "tempo",
"label": "cUSD"
},
"dateAdded": "2026-03-11T04:44:38Z"
},
{
"name": "Staked Cap USD",
"symbol": "stcUSD",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000008ee4fcff88888888",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg",
"extensions": {
"chain": "tempo",
"label": "stcUSD"
},
"dateAdded": "2026-03-16T02:56:16Z"
},
{
"name": "Generic USD",
"symbol": "GUSD",
"address": "0x20c0000000000000000000005c0bac7cef389a11",
"decimals": 6,
"chainId": 4217,
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg",
"extensions": {
"chain": "tempo",
"label": "GUSD"
},
"dateAdded": "2026-03-21T05:24:26Z"
}
]
"$schema": "https://esm.sh/gh/uniswap/token-lists/src/tokenlist.schema.json",
"name": "Tempo Mainnet (Presto)",
"logoURI": "https://esm.sh/gh/tempoxyz/tokenlist/data/4217/icon.svg",
"timestamp": "2026-03-21T05:24:26Z",
"version": {
"major": 1,
"minor": 1,
"patch": 9
},
"tokens": [
{
"name": "PathUSD",
"symbol": "pathUSD",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000000000000000000000",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000000000000000000000.svg",
"extensions": {
"chain": "tempo",
"label": "PathUSD"
}
},
{
"name": "Bridged USDC (Stargate)",
"symbol": "USDC.e",
"decimals": 6,
"chainId": 4217,
"address": "0x20c000000000000000000000b9537d11c60e8b50",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg",
"extensions": {
"chain": "tempo",
"label": "USDC"
}
},
{
"name": "Bridged EURC (Stargate)",
"symbol": "EURC.e",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000001621e21f71cf12fb",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg",
"extensions": {
"chain": "tempo",
"label": "EURC"
}
},
{
"name": "USDT0",
"symbol": "USDT0",
"decimals": 6,
"chainId": 4217,
"address": "0x20c00000000000000000000014f22ca97301eb73",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg",
"extensions": {
"chain": "tempo"
}
},
{
"name": "Frax USD",
"symbol": "frxUSD",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000003554d28269e0f3c2",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg",
"extensions": {
"chain": "tempo"
}
},
{
"name": "Cap USD",
"symbol": "cUSD",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000000520792dcccccccc",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg",
"extensions": {
"chain": "tempo",
"label": "cUSD"
},
"dateAdded": "2026-03-11T04:44:38Z"
},
{
"name": "Staked Cap USD",
"symbol": "stcUSD",
"decimals": 6,
"chainId": 4217,
"address": "0x20c0000000000000000000008ee4fcff88888888",
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg",
"extensions": {
"chain": "tempo",
"label": "stcUSD"
},
"dateAdded": "2026-03-16T02:56:16Z"
},
{
"name": "Generic USD",
"symbol": "GUSD",
"address": "0x20c0000000000000000000005c0bac7cef389a11",
"decimals": 6,
"chainId": 4217,
"logoURI": "https://esm.sh/gh/tempoxyz/tempo-apps/apps/tokenlist/data/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg",
"extensions": {
"chain": "tempo",
"label": "GUSD"
},
"dateAdded": "2026-03-21T05:24:26Z"
}
]
}
Loading