Skip to content

Commit aaa796d

Browse files
authored
Merge branch 'main' into yash/stylus-docs
2 parents 5bb203e + db53847 commit aaa796d

File tree

6 files changed

+124
-139
lines changed

6 files changed

+124
-139
lines changed

apps/dashboard/src/app/nebula-app/(app)/components/AssetsSection/AssetsSection.tsx

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@ import { isProd } from "@/constants/env-utils";
33
import { useQuery } from "@tanstack/react-query";
44
import { XIcon } from "lucide-react";
55
import Link from "next/link";
6-
import { type ThirdwebClient, defineChain, toTokens } from "thirdweb";
6+
import {
7+
NATIVE_TOKEN_ADDRESS,
8+
type ThirdwebClient,
9+
defineChain,
10+
getAddress,
11+
toTokens,
12+
} from "thirdweb";
713
import {
814
Blobbie,
915
TokenIcon,
1016
TokenProvider,
1117
useActiveAccount,
1218
useActiveWalletChain,
1319
} from "thirdweb/react";
20+
import { getWalletBalance } from "thirdweb/wallets";
1421
import { ChainIconClient } from "../../../../../components/icons/ChainIcon";
1522
import { useAllChainsData } from "../../../../../hooks/chains/allChains";
1623
import { nebulaAppThirdwebClient } from "../../utils/nebulaThirdwebClient";
@@ -45,7 +52,7 @@ export function AssetsSectionUI(props: {
4552
{!props.isPending &&
4653
props.data.map((asset) => (
4754
<AssetItem
48-
key={asset.token_address}
55+
key={`${asset.chain_id}-${asset.token_address}`}
4956
asset={asset}
5057
client={props.client}
5158
/>
@@ -78,6 +85,8 @@ function AssetItem(props: {
7885
}) {
7986
const { idToChain } = useAllChainsData();
8087
const chainMeta = idToChain.get(props.asset.chain_id);
88+
const isNativeToken = props.asset.token_address === NATIVE_TOKEN_ADDRESS;
89+
8190
return (
8291
<TokenProvider
8392
address={props.asset.token_address}
@@ -88,7 +97,7 @@ function AssetItem(props: {
8897
<div className="relative flex h-[48px] items-center gap-2.5 rounded-lg px-2 py-1 hover:bg-accent">
8998
<div className="relative">
9099
<TokenIcon
91-
className="size-8 rounded-full"
100+
className="size-8 rounded-full border"
92101
loadingComponent={
93102
<Blobbie
94103
address={props.asset.token_address}
@@ -102,25 +111,31 @@ function AssetItem(props: {
102111
/>
103112
}
104113
/>
105-
<div className="-right-0.5 -bottom-0.5 absolute rounded-full border bg-background p-0.5">
106-
<ChainIconClient
107-
client={props.client}
108-
className="size-3.5"
109-
src={chainMeta?.icon?.url || ""}
110-
/>
111-
</div>
114+
{!isNativeToken && (
115+
<div className="-right-0.5 -bottom-0.5 absolute rounded-full border bg-background p-0.5">
116+
<ChainIconClient
117+
client={props.client}
118+
className="size-3.5"
119+
src={chainMeta?.icon?.url || ""}
120+
/>
121+
</div>
122+
)}
112123
</div>
113124

114125
<div className="flex min-w-0 flex-col text-sm">
115126
<Link
116-
href={`https://thirdweb.com/${props.asset.chain_id}/${props.asset.token_address}`}
127+
href={
128+
isNativeToken
129+
? `https://thirdweb.com/${props.asset.chain_id}`
130+
: `https://thirdweb.com/${props.asset.chain_id}/${props.asset.token_address}`
131+
}
117132
target="_blank"
118133
className="truncate font-medium before:absolute before:inset-0"
119134
>
120135
{props.asset.name}
121136
</Link>
122137

123-
<p className="text-muted-foreground text-sm">
138+
<p className="truncate text-muted-foreground text-sm">
124139
{`${toTokens(BigInt(props.asset.balance), props.asset.decimals)} ${props.asset.symbol}`}
125140
</p>
126141
</div>
@@ -158,16 +173,85 @@ export function AssetsSection(props: {
158173
data: AssetBalance[];
159174
};
160175

161-
return json.data;
176+
const tokensToShowOnTop = new Set(
177+
[
178+
// base
179+
"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", // usdc,
180+
"0x0555E30da8f98308EdB960aa94C0Db47230d2B9c", // wbtc
181+
"0x4200000000000000000000000000000000000006", // wrapped eth
182+
// ethereum
183+
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // usdc
184+
"0xdac17f958d2ee523a2206206994597c13d831ec7", // usdt
185+
"0xB8c77482e45F1F44dE1745F52C74426C631bDD52", // bnb
186+
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // weth
187+
"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", // wbtc
188+
// optimism
189+
"0x4200000000000000000000000000000000000042", // op token
190+
"0xdc6ff44d5d932cbd77b52e5612ba0529dc6226f1", // world coin
191+
"0x94b008aa00579c1307b0ef2c499ad98a8ce58e58", // usdt
192+
"0x0b2c639c533813f4aa9d7837caf62653d097ff85", // usdc
193+
"0x4200000000000000000000000000000000000006", // wrapped eth
194+
// polygon
195+
"0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", // weth
196+
"0xc2132d05d31c914a87c6611c10748aeb04b58e8f", // usdt
197+
"0x3BA4c387f786bFEE076A58914F5Bd38d668B42c3", // bnb
198+
"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", // usdc
199+
"0x2791bca1f2de4661ed88a30c99a7a9449aa84174", // usdc.e
200+
].map((x) => getAddress(x)),
201+
);
202+
203+
return json.data.sort((a, b) => {
204+
if (tokensToShowOnTop.has(getAddress(a.token_address))) {
205+
return -1;
206+
}
207+
if (tokensToShowOnTop.has(getAddress(b.token_address))) {
208+
return 1;
209+
}
210+
return 0;
211+
});
162212
},
163213
enabled: !!account && !!activeChain,
164214
});
165215

216+
const nativeBalances = useQuery({
217+
queryKey: ["getWalletBalance", account?.address, activeChain?.id],
218+
queryFn: async () => {
219+
if (!account || !activeChain) {
220+
return [];
221+
}
222+
223+
const chains = [...new Set([1, 8453, 10, 137, activeChain.id])];
224+
225+
const result = await Promise.allSettled(
226+
chains.map((chain) =>
227+
getWalletBalance({
228+
// eslint-disable-next-line no-restricted-syntax
229+
chain: defineChain(chain),
230+
client: props.client,
231+
address: account.address,
232+
}),
233+
),
234+
);
235+
236+
return result
237+
.filter((r) => r.status === "fulfilled")
238+
.map((r) => ({
239+
chain_id: r.value.chainId,
240+
token_address: r.value.tokenAddress,
241+
balance: r.value.value.toString(),
242+
name: r.value.name,
243+
symbol: r.value.symbol,
244+
decimals: r.value.decimals,
245+
}))
246+
.filter((x) => x.balance !== "0") as AssetBalance[];
247+
},
248+
});
249+
250+
const isPending = assetsQuery.isPending || nativeBalances.isPending;
251+
252+
const data = [...(nativeBalances.data ?? []), ...(assetsQuery.data ?? [])];
253+
166254
return (
167-
<AssetsSectionUI
168-
data={assetsQuery.data ?? []}
169-
isPending={assetsQuery.isPending}
170-
client={props.client}
171-
/>
255+
<AssetsSectionUI data={data} isPending={isPending} client={props.client} />
172256
);
173257
}

apps/dashboard/src/app/nebula-app/(app)/components/TransactionsSection/TransactionsSection.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function TransactionInfo(props: {
8888
<div className="relative flex h-[48px] items-center gap-2.5 rounded-lg px-2 py-1 hover:bg-accent">
8989
<ChainIconClient
9090
client={props.client}
91-
className="size-8"
91+
className="size-8 rounded-full border"
9292
src={chainMeta?.icon?.url || ""}
9393
/>
9494

@@ -167,20 +167,29 @@ export function TransactionsSection(props: {
167167
const url = new URL(
168168
`https://insight.${isProd ? "thirdweb" : "thirdweb-dev"}.com/v1/wallets/${account.address}/transactions`,
169169
);
170-
url.searchParams.set("limit", "10");
170+
url.searchParams.set("limit", "20");
171171
url.searchParams.set("decode", "true");
172172
url.searchParams.set("clientId", nebulaAppThirdwebClient.clientId);
173173

174+
const threeMonthsAgoUnixTime = Math.floor(
175+
(Date.now() - 3 * 30 * 24 * 60 * 60 * 1000) / 1000,
176+
);
177+
178+
url.searchParams.set(
179+
"filter_block_timestamp_gte",
180+
`${threeMonthsAgoUnixTime}`,
181+
);
182+
174183
for (const chain of chains) {
175184
url.searchParams.append("chain", chain.toString());
176185
}
177186

178187
const response = await fetch(url.toString());
179188
const json = (await response.json()) as {
180-
data: WalletTransaction[];
189+
data?: WalletTransaction[];
181190
};
182191

183-
return json.data;
192+
return json.data ?? [];
184193
},
185194
retry: false,
186195
enabled: !!account && !!activeChain,

apps/dashboard/src/components/pay/PayAnalytics/components/PayNewCustomers.tsx

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { SkeletonContainer } from "@/components/ui/skeleton";
33
import { useId, useMemo } from "react";
44
import { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis } from "recharts";
55
import type { UniversalBridgeWalletStats } from "types/analytics";
6-
import { CardHeading, ChangeBadge, NoDataOverlay, chartHeight } from "./common";
6+
import { CardHeading, NoDataOverlay, chartHeight } from "./common";
77

88
type GraphDataItem = {
99
date: string;
@@ -20,7 +20,7 @@ export function PayNewCustomers(props: {
2020
/**
2121
* For each date, compute the total number of wallets that have never existed before in the time series
2222
*/
23-
const { graphData, trend } = useMemo(() => {
23+
const { graphData } = useMemo(() => {
2424
const dates = new Set<string>();
2525
for (const item of props.data) {
2626
if (!dates.has(item.date)) {
@@ -47,19 +47,7 @@ export function PayNewCustomers(props: {
4747
value: newUsers,
4848
});
4949
}
50-
const lastPeriod = newUsersData[newUsersData.length - 3];
51-
const currentPeriod = newUsersData[newUsersData.length - 2];
52-
// Calculate the percent change from last period to current period
53-
const trend =
54-
lastPeriod &&
55-
currentPeriod &&
56-
lastPeriod.value > 0 &&
57-
currentPeriod.value > 0
58-
? (currentPeriod.value - lastPeriod.value) / lastPeriod.value
59-
: lastPeriod?.value === 0 && (currentPeriod?.value || 0) > 0
60-
? 100
61-
: undefined;
62-
return { graphData: newUsersData, trend };
50+
return { graphData: newUsersData };
6351
}, [props.data, props.dateFormat]);
6452
const isEmpty = useMemo(
6553
() => graphData.length === 0 || graphData.every((x) => x.value === 0),
@@ -89,17 +77,6 @@ export function PayNewCustomers(props: {
8977
);
9078
}}
9179
/>
92-
93-
{!isEmpty && typeof trend !== "undefined" && (
94-
<SkeletonContainer
95-
loadedData={trend}
96-
className="rounded-2xl"
97-
skeletonData={1}
98-
render={(v) => {
99-
return <ChangeBadge percent={v} />;
100-
}}
101-
/>
102-
)}
10380
</div>
10481
</div>
10582
</div>
@@ -125,7 +102,7 @@ export function PayNewCustomers(props: {
125102
{payload?.date}
126103
</p>
127104
<p className="text-base text-medium">
128-
Customers: {payload?.value}
105+
New Customers: {payload?.value}
129106
</p>
130107
</div>
131108
);

apps/dashboard/src/components/pay/PayAnalytics/components/Payouts.tsx

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useMemo } from "react";
44
import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis } from "recharts";
55
import type { UniversalBridgeStats } from "types/analytics";
66
import { toUSD } from "../../../../utils/number";
7-
import { CardHeading, ChangeBadge, NoDataOverlay, chartHeight } from "./common";
7+
import { CardHeading, NoDataOverlay, chartHeight } from "./common";
88

99
type GraphData = {
1010
date: string;
@@ -24,7 +24,7 @@ export function Payouts(props: {
2424
props.data.every((x) => x.developerFeeUsdCents === 0);
2525

2626
const barColor = isEmpty ? "hsl(var(--accent))" : "hsl(var(--chart-1))";
27-
const { graphData, totalPayoutsUSD, trend } = useMemo(() => {
27+
const { graphData, totalPayoutsUSD } = useMemo(() => {
2828
const dates = new Set<string>();
2929
for (const item of props.data) {
3030
if (!dates.has(item.date)) {
@@ -49,21 +49,9 @@ export function Payouts(props: {
4949
value: total / 100,
5050
});
5151
}
52-
const lastPeriod = cleanedData[cleanedData.length - 3];
53-
const currentPeriod = cleanedData[cleanedData.length - 2];
54-
const trend =
55-
lastPeriod &&
56-
currentPeriod &&
57-
lastPeriod.value > 0 &&
58-
currentPeriod.value > 0
59-
? (currentPeriod.value - lastPeriod.value) / lastPeriod.value
60-
: lastPeriod?.value === 0 && (currentPeriod?.value || 0) > 0
61-
? 100
62-
: undefined;
6352
return {
6453
graphData: cleanedData,
6554
totalPayoutsUSD: totalPayouts / 100,
66-
trend,
6755
};
6856
}, [props.data, props.dateFormat]);
6957

@@ -93,17 +81,6 @@ export function Payouts(props: {
9381
);
9482
}}
9583
/>
96-
97-
{!isEmpty && typeof trend !== "undefined" && (
98-
<SkeletonContainer
99-
className="rounded-2xl"
100-
loadedData={trend}
101-
skeletonData={1}
102-
render={(percent) => {
103-
return <ChangeBadge percent={percent} />;
104-
}}
105-
/>
106-
)}
10784
</div>
10885

10986
<div className="relative flex w-full justify-center">

0 commit comments

Comments
 (0)