Skip to content

Commit 9ea75a9

Browse files
authored
Merge pull request #834 from bentechnology03-ops/feat/805-stellar-testnet-faucet-widget
feat(dashboard): add Stellar testnet faucet widget to WalletInfoCard …
2 parents 989f82c + 527761f commit 9ea75a9

1 file changed

Lines changed: 95 additions & 1 deletion

File tree

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,97 @@
1+
"use client";
2+
3+
import React, { useState, useCallback } from "react";
4+
import { Wallet, Loader2, Droplets } from "lucide-react";
5+
import { useAutoStellarWallet } from "@/app/hooks/useAutoStellarWallet";
6+
import { useBalance } from "@/app/hooks/useBalance";
7+
import { fundWithFriendbot } from "@/app/lib/stellar";
8+
import { isTestnet, getFreighterNetwork, getStellarNetwork } from "@/app/lib/networkConfig";
9+
import { useToast } from "@/hooks/useToast";
10+
11+
// IS_TESTNET guard — evaluated once at module load; safe because the network
12+
// env var is baked in at build time and never changes at runtime.
13+
const IS_TESTNET = isTestnet();
14+
115
export default function WalletInfoCard() {
2-
return null;
16+
const { publicKey, networkLabel } = useAutoStellarWallet();
17+
const { showToast, ToastEl } = useToast();
18+
const [isFunding, setIsFunding] = useState(false);
19+
20+
const freighterNetwork = getFreighterNetwork(getStellarNetwork());
21+
const { refresh } = useBalance({
22+
publicKey,
23+
network: freighterNetwork,
24+
autoRefresh: false,
25+
});
26+
27+
const handleFundWallet = useCallback(async () => {
28+
if (!publicKey || isFunding) return;
29+
30+
setIsFunding(true);
31+
try {
32+
await fundWithFriendbot(publicKey);
33+
showToast("Testnet wallet funded with 10,000 XLM!", "success");
34+
35+
// Give the ledger a moment to confirm before refreshing
36+
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
37+
refresh();
38+
} catch (err) {
39+
const message =
40+
err instanceof Error ? err.message : "Failed to fund wallet. Please try again.";
41+
showToast(message, "error");
42+
} finally {
43+
setIsFunding(false);
44+
}
45+
}, [publicKey, isFunding, refresh, showToast]);
46+
47+
// Only render on testnet — the IS_TESTNET guard ensures this card never
48+
// appears in production mainnet builds.
49+
if (!IS_TESTNET) return null;
50+
51+
// No public key yet — nothing to show
52+
if (!publicKey) return null;
53+
54+
return (
55+
<>
56+
<div className="bg-surface border border-border rounded-2xl p-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
57+
<div className="flex items-center gap-3">
58+
<div className="w-10 h-10 rounded-xl bg-brand/10 flex items-center justify-center shrink-0">
59+
<Wallet className="w-5 h-5 text-brand" aria-hidden="true" />
60+
</div>
61+
<div>
62+
<p className="text-sm font-bold text-white">Testnet Wallet</p>
63+
<p className="text-xs text-muted-foreground mt-0.5">
64+
{networkLabel} &mdash; need XLM to test transactions?
65+
</p>
66+
</div>
67+
</div>
68+
69+
<button
70+
type="button"
71+
onClick={handleFundWallet}
72+
disabled={isFunding}
73+
aria-busy={isFunding}
74+
aria-label="Fund testnet wallet with Friendbot"
75+
className="flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-semibold
76+
bg-brand/10 hover:bg-brand/20 text-brand border border-brand/20
77+
transition-colors disabled:opacity-50 disabled:cursor-not-allowed
78+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand"
79+
>
80+
{isFunding ? (
81+
<>
82+
<Loader2 className="w-4 h-4 animate-spin" aria-hidden="true" />
83+
Funding…
84+
</>
85+
) : (
86+
<>
87+
<Droplets className="w-4 h-4" aria-hidden="true" />
88+
Fund Testnet Wallet
89+
</>
90+
)}
91+
</button>
92+
</div>
93+
94+
{ToastEl}
95+
</>
96+
);
397
}

0 commit comments

Comments
 (0)