Skip to content
Merged
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
105 changes: 86 additions & 19 deletions components/toolbox/console/primary-network/DevnetFaucet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useState, useEffect, useCallback } from 'react';
import { useSession } from 'next-auth/react';
import { formatEther, defineChain } from 'viem';
import { formatEther, defineChain, isAddress } from 'viem';
import { makePublicClientForChain } from '@/components/toolbox/hooks/usePublicClientForChain';
import { Copy, Check, AlertTriangle, Droplets, ExternalLink, RefreshCw, Wallet } from 'lucide-react';
import {
Expand Down Expand Up @@ -64,7 +64,15 @@ function DevnetFaucet({ onSuccess: _onSuccess }: BaseConsoleToolProps) {
const { data: session } = useSession();
const { walletEVMAddress } = useWalletStore();
const [isDripping, setIsDripping] = useState(false);
const [result, setResult] = useState<{ success: boolean; message: string; txHash?: string } | null>(null);
const [result, setResult] = useState<{
success: boolean;
message: string;
txHash?: string;
destinationAddress?: string;
} | null>(null);
const [recipientAddress, setRecipientAddress] = useState('');
const [recipientError, setRecipientError] = useState<string | null>(null);
const [hasEditedRecipientAddress, setHasEditedRecipientAddress] = useState(false);

const [faucetBalance, setFaucetBalance] = useState<string | null>(null);
const [faucetAddress, setFaucetAddress] = useState<string | null>(null);
Expand Down Expand Up @@ -119,6 +127,12 @@ function DevnetFaucet({ onSuccess: _onSuccess }: BaseConsoleToolProps) {
}
}, [isAvaLabs, walletEVMAddress, fetchUserBalance]);

useEffect(() => {
if (walletEVMAddress && !hasEditedRecipientAddress) {
setRecipientAddress(walletEVMAddress);
}
}, [walletEVMAddress, hasEditedRecipientAddress]);

const handleAddNetwork = async () => {
if (!window.ethereum) return;
try {
Expand Down Expand Up @@ -156,13 +170,20 @@ function DevnetFaucet({ onSuccess: _onSuccess }: BaseConsoleToolProps) {
};

const handleDrip = async () => {
if (!walletEVMAddress || isDripping) return;
if (isDripping) return;

const destinationAddress = recipientAddress.trim();
if (!isAddress(destinationAddress)) {
setRecipientError('Enter a valid EVM address');
return;
}

setIsDripping(true);
setResult(null);
setRecipientError(null);

try {
const response = await fetch(`/api/devnet-faucet?address=${walletEVMAddress}`);
const response = await fetch(`/api/devnet-faucet?address=${encodeURIComponent(destinationAddress)}`);
const data = await response.json();

if (!response.ok) {
Expand All @@ -174,11 +195,14 @@ function DevnetFaucet({ onSuccess: _onSuccess }: BaseConsoleToolProps) {
success: true,
message: `Sent ${data.amount} AVAX`,
txHash: data.txHash,
destinationAddress: data.destinationAddress,
});
// Refresh balances after drip
setTimeout(() => {
fetchBalance();
fetchUserBalance();
if (walletEVMAddress && destinationAddress.toLowerCase() === walletEVMAddress.toLowerCase()) {
fetchUserBalance();
}
}, 2000);
} catch {
setResult({ success: false, message: 'Network error. Please try again.' });
Expand Down Expand Up @@ -312,10 +336,8 @@ function DevnetFaucet({ onSuccess: _onSuccess }: BaseConsoleToolProps) {
</div>
</div>

{!walletEVMAddress ? (
<p className="text-sm text-zinc-500 text-center py-2">Connect your wallet to drip devnet AVAX</p>
) : (
<div className="space-y-3">
<div className="space-y-3">
{walletEVMAddress && (
<div className="flex items-center justify-between bg-zinc-100 dark:bg-zinc-800 rounded px-3 py-2">
<div className="flex items-center gap-1.5 text-sm text-zinc-600 dark:text-zinc-400">
<Wallet className="w-3.5 h-3.5" />
Expand All @@ -341,17 +363,59 @@ function DevnetFaucet({ onSuccess: _onSuccess }: BaseConsoleToolProps) {
</button>
</div>
</div>
<p className="text-xs text-zinc-500 font-mono truncate">{walletEVMAddress}</p>
<button
onClick={handleDrip}
disabled={isDripping}
className="w-full px-4 py-2 text-sm font-medium bg-blue-600 text-white hover:bg-blue-700 transition-colors disabled:bg-zinc-400 disabled:cursor-not-allowed rounded flex items-center justify-center gap-2"
>
<Droplets className="w-4 h-4" />
{isDripping ? 'Dripping...' : 'Drip 2 AVAX'}
</button>
)}
<div className="space-y-1.5">
<div className="flex items-center justify-between gap-2">
<label
htmlFor="devnet-faucet-recipient"
className="text-sm font-medium text-zinc-700 dark:text-zinc-300"
>
Recipient Address
</label>
{walletEVMAddress && (
<button
type="button"
onClick={() => {
setRecipientAddress(walletEVMAddress);
setRecipientError(null);
setResult(null);
setHasEditedRecipientAddress(false);
}}
className="text-xs text-zinc-500 hover:text-zinc-900 dark:hover:text-white transition-colors"
>
Use connected wallet
</button>
)}
</div>
<input
id="devnet-faucet-recipient"
type="text"
value={recipientAddress}
onChange={(event) => {
setRecipientAddress(event.target.value);
setRecipientError(null);
setResult(null);
setHasEditedRecipientAddress(true);
}}
placeholder="0x..."
className="w-full px-3 py-2 text-sm font-mono bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded text-zinc-900 dark:text-white placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500"
/>
{recipientError && <p className="text-xs text-red-500">{recipientError}</p>}
</div>
)}
{walletEVMAddress && (
<p className="text-xs text-zinc-500 truncate">
Connected wallet: <span className="font-mono">{walletEVMAddress}</span>
</p>
)}
<button
onClick={handleDrip}
disabled={isDripping || !recipientAddress.trim()}
className="w-full px-4 py-2 text-sm font-medium bg-blue-600 text-white hover:bg-blue-700 transition-colors disabled:bg-zinc-400 disabled:cursor-not-allowed rounded flex items-center justify-center gap-2"
>
<Droplets className="w-4 h-4" />
{isDripping ? 'Dripping...' : 'Drip 2 AVAX'}
</button>
</div>

{result && (
<div
Expand All @@ -362,6 +426,9 @@ function DevnetFaucet({ onSuccess: _onSuccess }: BaseConsoleToolProps) {
}`}
>
<p>{result.message}</p>
{result.destinationAddress && (
<p className="mt-1 text-xs font-mono break-all text-zinc-500">to: {result.destinationAddress}</p>
)}
{result.txHash && <p className="mt-1 text-xs font-mono break-all text-zinc-500">tx: {result.txHash}</p>}
</div>
)}
Expand Down
Loading