diff --git a/apps/dashboard/src/@/lib/search.ts b/apps/dashboard/src/@/lib/search.ts index b32f7697a00..fe87403c265 100644 --- a/apps/dashboard/src/@/lib/search.ts +++ b/apps/dashboard/src/@/lib/search.ts @@ -184,7 +184,7 @@ async function fetchContractName(chainId: number, contractAddress: string) { return name; } -const formatNumber = (num: number) => { +export const formatNumber = (num: number) => { if (num >= 1000000) { return `${(num / 1000000).toLocaleString(undefined, { maximumFractionDigits: 1, diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx index 15891f1705c..12d933d8a04 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/ContractAnalyticsPage.tsx @@ -10,6 +10,7 @@ import { } from "@/components/analytics/date-range-selector"; import { ThirdwebBarChart } from "@/components/blocks/charts/bar-chart"; import { SkeletonContainer } from "@/components/ui/skeleton"; +import { formatNumber } from "@/lib/search"; import { type AnalyticsQueryParams, type TotalQueryResult, @@ -376,7 +377,7 @@ function AnalyticsStatUI(props: { label: string; data: number | undefined }) { { - return
{v.toLocaleString()}
; + return
{formatNumber(v)}
; }} skeletonData={10000} /> diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts index 3de0de3d811..0010673002f 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-events.ts @@ -61,7 +61,7 @@ export async function getContractEventAnalytics(params: { if (!res.ok) { const errorText = await res.text(); - throw new Error(`Failed to fetch analytics data: ${errorText}`); + throw new Error(`Failed to fetch events analytics data: ${errorText}`); } const json = (await res.json()) as InsightResponse; diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.ts index a8d6faac200..2f2297705d6 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-function-breakdown.ts @@ -26,12 +26,18 @@ export async function getContractFunctionBreakdown(params: { startDate?: Date; endDate?: Date; }): Promise { + const daysDifference = + params.startDate && params.endDate + ? Math.ceil( + (params.endDate.getTime() - params.startDate.getTime()) / + (1000 * 60 * 60 * 24), + ) + : 30; const queryParams = [ `chain=${params.chainId}`, - "group_by=time", + "group_by=day", "group_by=function_selector", - "aggregate=toStartOfDay(toDate(block_timestamp)) as time", - "aggregate=count(*) as count", + `limit=${daysDifference * 10}`, // at most 10 functions per day params.startDate ? `filter_block_timestamp_gte=${getUnixTime(params.startDate)}` : "", @@ -68,17 +74,16 @@ export async function getContractFunctionBreakdown(params: { if ( typeof value === "object" && value !== null && - "time" in value && + "day" in value && "count" in value && "function_selector" in value && typeof value.function_selector === "string" && - typeof value.time === "string" && - typeof value.count === "number" + typeof value.day === "string" ) { collectedAggregations.push({ - count: value.count, + count: Number(value.count), function_selector: value.function_selector, - time: value.time, + time: value.day, }); } } diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.ts index 8195715dbae..b2da2f68617 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-transactions.ts @@ -30,11 +30,17 @@ export async function getContractTransactionAnalytics(params: { startDate?: Date; endDate?: Date; }): Promise { + const daysDifference = + params.startDate && params.endDate + ? Math.ceil( + (params.endDate.getTime() - params.startDate.getTime()) / + (1000 * 60 * 60 * 24), + ) + : 30; const queryParams = [ `chain=${params.chainId}`, - "group_by=time", - "aggregate=toStartOfDay(toDate(block_timestamp)) as time", - "aggregate=count(block_timestamp) as count", + "group_by=day", + `limit=${daysDifference}`, params.startDate ? `filter_block_timestamp_gte=${getUnixTime(params.startDate)}` : "", @@ -55,7 +61,8 @@ export async function getContractTransactionAnalytics(params: { ); if (!res.ok) { - throw new Error("Failed to fetch analytics data"); + const errorText = await res.text(); + throw new Error(`Failed to fetch transaction analytics data: ${errorText}`); } const json = (await res.json()) as InsightResponse; @@ -67,14 +74,13 @@ export async function getContractTransactionAnalytics(params: { if ( typeof tx === "object" && tx !== null && - "time" in tx && + "day" in tx && "count" in tx && - typeof tx.time === "string" && - typeof tx.count === "number" + typeof tx.day === "string" ) { returnValue.push({ - count: tx.count, - time: new Date(tx.time), + count: Number(tx.count), + time: new Date(tx.day), }); } } diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts index 403fd526985..2599273befc 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/contract-wallet-analytics.ts @@ -30,11 +30,18 @@ export async function getContractUniqueWalletAnalytics(params: { startDate?: Date; endDate?: Date; }): Promise { + const daysDifference = + params.startDate && params.endDate + ? Math.ceil( + (params.endDate.getTime() - params.startDate.getTime()) / + (1000 * 60 * 60 * 24), + ) + : 30; const queryParams = [ `chain=${params.chainId}`, - "group_by=time", - "aggregate=toStartOfDay(toDate(block_timestamp)) as time", - "aggregate=count(distinct from_address) as count", + "group_by=day", + "aggregate=count(distinct from_address)", + `limit=${daysDifference}`, params.startDate ? `filter_block_timestamp_gte=${getUnixTime(params.startDate)}` : "", @@ -55,10 +62,12 @@ export async function getContractUniqueWalletAnalytics(params: { ); if (!res.ok) { - throw new Error("Failed to fetch analytics data"); + const errorText = await res.text(); + throw new Error(`Failed to fetch wallet analytics data: ${errorText}`); } const json = (await res.json()) as InsightResponse; + console.log("wallet analytics json", json); const aggregations = Object.values(json.aggregations[0]); const returnValue: TransactionAnalyticsEntry[] = []; @@ -67,14 +76,13 @@ export async function getContractUniqueWalletAnalytics(params: { if ( typeof tx === "object" && tx !== null && - "time" in tx && + "day" in tx && "count" in tx && - typeof tx.time === "string" && - typeof tx.count === "number" + typeof tx.day === "string" ) { returnValue.push({ - count: tx.count, - time: new Date(tx.time), + count: Number(tx.count), + time: new Date(tx.day), }); } } diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.ts index 026821181a2..34d684ab210 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-events.ts @@ -33,7 +33,8 @@ export async function getTotalContractEvents(params: { ); if (!res.ok) { - throw new Error("Failed to fetch analytics data"); + const errorText = await res.text(); + throw new Error(`Failed to fetch events analytics data: ${errorText}`); } const json = (await res.json()) as InsightResponse; diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.ts index a9165ea53b9..21450412ebb 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-contract-transactions.ts @@ -6,7 +6,7 @@ type InsightResponse = { aggregations: [ { 0: { - total: number; + count: number; }; }, ]; @@ -19,10 +19,9 @@ export async function getTotalContractTransactions(params: { contractAddress: string; chainId: number; }): Promise<{ count: number }> { - const queryParams = [ - `chain=${params.chainId}`, - "aggregate=count(block_number) as total", - ].join("&"); + const queryParams = [`chain=${params.chainId}`, "aggregate=count()"].join( + "&", + ); const res = await fetch( `https://insight.${thirdwebDomain}.com/v1/transactions/${params.contractAddress}?${queryParams}`, @@ -34,12 +33,15 @@ export async function getTotalContractTransactions(params: { ); if (!res.ok) { - throw new Error("Failed to fetch analytics data"); + const errorText = await res.text(); + throw new Error( + `Failed to fetch transactions analytics data: ${errorText}`, + ); } const json = (await res.json()) as InsightResponse; return { - count: json.aggregations[0][0].total, + count: json.aggregations[0][0].count, }; } diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.ts index f2650aba63b..3fa155a9772 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/analytics/utils/total-unique-wallets.ts @@ -6,7 +6,7 @@ type InsightResponse = { aggregations: [ { 0: { - total: number; + count: number; }; }, ]; @@ -21,7 +21,7 @@ export async function getTotalContractUniqueWallets(params: { }): Promise<{ count: number }> { const queryParams = [ `chain=${params.chainId}`, - "aggregate=count(distinct from_address) as total", + "aggregate=count(distinct from_address)", ].join("&"); const res = await fetch( @@ -34,12 +34,15 @@ export async function getTotalContractUniqueWallets(params: { ); if (!res.ok) { - throw new Error("Failed to fetch analytics data"); + const errorText = await res.text(); + throw new Error( + `Failed to fetch unique wallets analytics data: ${errorText}`, + ); } const json = (await res.json()) as InsightResponse; return { - count: json.aggregations[0][0].total, + count: json.aggregations[0][0].count, }; } diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsx index 446f63fddcf..b4a9797747f 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/TransactionsSection/TransactionsSection.tsx @@ -190,6 +190,13 @@ export function TransactionsSection(props: { client: ThirdwebClient }) { } const response = await fetch(url.toString()); + + if (!response.ok) { + throw new Error( + `Failed to fetch transactions: ${response.status} - ${await response.text()}`, + ); + } + const json = (await response.json()) as { data?: WalletTransaction[]; };