Skip to content

Commit 5e4db2f

Browse files
committed
Add Solana wallet balance endpoint and UI integration
Introduces a new API endpoint and client method for fetching Solana wallet balances. Updates dashboard components to display Solana balances using the new endpoint, passing required authentication and client ID. Also updates generated API types and SDK to support the new endpoint. Closes BLD-472
1 parent 6c318f8 commit 5e4db2f

File tree

7 files changed

+210
-90
lines changed

7 files changed

+210
-90
lines changed

.changeset/quiet-streets-lie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/api": patch
3+
---
4+
5+
added solana token balances endpoint

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/components/server-wallets-table.client.tsx

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { useV5DashboardChain } from "@/hooks/chains/v5-adapter";
5757
import { WalletProductIcon } from "@/icons/WalletProductIcon";
5858
import { useDashboardRouter } from "@/lib/DashboardRouter";
5959
import { cn } from "@/lib/utils";
60+
import { fetchSolanaBalance } from "../lib/getSolanaBalance";
6061
import { updateDefaultProjectWallet } from "../lib/vault.client";
6162
import { CreateServerWallet } from "../server-wallets/components/create-server-wallet.client";
6263
import type { Wallet as EVMWallet } from "../server-wallets/wallet-table/types";
@@ -79,6 +80,7 @@ interface ServerWalletsTableProps {
7980
teamSlug: string;
8081
client: ThirdwebClient;
8182
solanaPermissionError?: boolean;
83+
authToken: string;
8284
}
8385

8486
export function ServerWalletsTable(props: ServerWalletsTableProps) {
@@ -95,6 +97,7 @@ export function ServerWalletsTable(props: ServerWalletsTableProps) {
9597
solanaTotalPages,
9698
client,
9799
solanaPermissionError,
100+
authToken,
98101
} = props;
99102

100103
const [activeChain, setActiveChain] = useState<WalletChain>("evm");
@@ -278,6 +281,7 @@ export function ServerWalletsTable(props: ServerWalletsTableProps) {
278281
project={project}
279282
teamSlug={teamSlug}
280283
client={client}
284+
authToken={authToken}
281285
/>
282286
))}
283287
</TableBody>
@@ -507,11 +511,13 @@ function SolanaWalletRow({
507511
project,
508512
teamSlug,
509513
client,
514+
authToken,
510515
}: {
511516
wallet: SolanaWallet;
512517
project: Project;
513518
teamSlug: string;
514519
client: ThirdwebClient;
520+
authToken: string;
515521
}) {
516522
const engineService = project.services.find(
517523
(s) => s.name === "engineCloud",
@@ -547,7 +553,11 @@ function SolanaWalletRow({
547553
</TableCell>
548554

549555
<TableCell>
550-
<SolanaWalletBalance publicKey={wallet.publicKey} />
556+
<SolanaWalletBalance
557+
publicKey={wallet.publicKey}
558+
authToken={authToken}
559+
clientId={project.publishableKey}
560+
/>
551561
</TableCell>
552562

553563
<TableCell>
@@ -739,14 +749,22 @@ function WalletBalance({
739749
);
740750
}
741751

742-
function SolanaWalletBalance({ publicKey }: { publicKey: string }) {
752+
function SolanaWalletBalance({
753+
publicKey,
754+
authToken,
755+
clientId,
756+
}: {
757+
publicKey: string;
758+
authToken: string;
759+
clientId: string;
760+
}) {
743761
const balance = useQuery({
744762
queryFn: async () => {
745-
// TODO: Implement actual Solana balance fetching
746-
return {
747-
displayValue: "0",
748-
symbol: "SOL",
749-
};
763+
return await fetchSolanaBalance({
764+
publicKey,
765+
authToken,
766+
clientId,
767+
});
750768
},
751769
queryKey: ["solanaWalletBalance", publicKey],
752770
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { configure, getSolanaWalletBalance } from "@thirdweb-dev/api";
2+
import { THIRDWEB_API_HOST } from "@/constants/urls";
3+
4+
// Configure the API client to use the correct base URL
5+
configure({
6+
override: {
7+
baseUrl: THIRDWEB_API_HOST,
8+
},
9+
});
10+
11+
export async function fetchSolanaBalance({
12+
publicKey,
13+
authToken,
14+
clientId,
15+
}: {
16+
publicKey: string;
17+
authToken: string;
18+
clientId: string;
19+
}): Promise<{
20+
displayValue: string;
21+
symbol: string;
22+
value: string;
23+
decimals: number;
24+
} | null> {
25+
try {
26+
const response = await getSolanaWalletBalance({
27+
path: {
28+
address: publicKey,
29+
},
30+
query: {
31+
chainId: "solana:mainnet",
32+
},
33+
headers: {
34+
Authorization: `Bearer ${authToken}`,
35+
"Content-Type": "application/json",
36+
"x-client-id": clientId,
37+
},
38+
});
39+
40+
if (response.error || !response.data) {
41+
console.error(
42+
"Error fetching Solana balance:",
43+
response.error || "No data returned",
44+
);
45+
return null;
46+
}
47+
48+
return {
49+
displayValue: response.data.result.displayValue,
50+
symbol: "SOL",
51+
value: response.data.result.value,
52+
decimals: response.data.result.decimals,
53+
};
54+
} catch (error) {
55+
console.error("Error fetching Solana balance:", error);
56+
return null;
57+
}
58+
}

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ export default async function TransactionsAnalyticsPage(props: {
215215
solanaWallets={solanaAccounts.data.items}
216216
teamSlug={params.team_slug}
217217
solanaPermissionError={isSolanaPermissionError}
218+
authToken={authToken}
218219
/>
219220
)}
220221
</div>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ export default async function Page(props: {
202202
solanaWallets={solanaAccounts.data.items}
203203
teamSlug={params.team_slug}
204204
solanaPermissionError={isSolanaPermissionError || false}
205+
authToken={authToken}
205206
/>
206207
)}
207208
</div>

packages/api/src/client/sdk.gen.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ import type {
6666
GetSolanaTransactionData,
6767
GetSolanaTransactionErrors,
6868
GetSolanaTransactionResponses,
69+
GetSolanaWalletBalanceData,
70+
GetSolanaWalletBalanceErrors,
71+
GetSolanaWalletBalanceResponses,
6972
GetTokenOwnersData,
7073
GetTokenOwnersErrors,
7174
GetTokenOwnersResponses,
@@ -1590,6 +1593,31 @@ export const createSolanaWallet = <ThrowOnError extends boolean = false>(
15901593
});
15911594
};
15921595

1596+
/**
1597+
* Get Solana Wallet Balance
1598+
* Get the SOL or SPL token balance for a Solana wallet on a specific Solana network.
1599+
*
1600+
* **Authentication**: Pass `x-client-id` for frontend usage from allowlisted origins or `x-secret-key` for backend usage.
1601+
*/
1602+
export const getSolanaWalletBalance = <ThrowOnError extends boolean = false>(
1603+
options: Options<GetSolanaWalletBalanceData, ThrowOnError>,
1604+
) => {
1605+
return (options.client ?? _heyApiClient).get<
1606+
GetSolanaWalletBalanceResponses,
1607+
GetSolanaWalletBalanceErrors,
1608+
ThrowOnError
1609+
>({
1610+
security: [
1611+
{
1612+
name: "x-client-id",
1613+
type: "apiKey",
1614+
},
1615+
],
1616+
url: "/v1/solana/wallets/{address}/balance",
1617+
...options,
1618+
});
1619+
};
1620+
15931621
/**
15941622
* Sign Solana Message
15951623
* Sign an arbitrary message with a Solana wallet. Supports both text and hexadecimal message formats with automatic format detection.

0 commit comments

Comments
 (0)