Skip to content

Commit 33a9301

Browse files
authored
feat: Enhance airdrop error handling in useAirdrop hook with user-friendly messages and wallet connection check (#4)
1 parent b126042 commit 33a9301

File tree

1 file changed

+59
-3
lines changed

1 file changed

+59
-3
lines changed

client/src/components/Wallet/hooks/useAirdrop.tsx

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useEffect, useState } from "react";
2+
import type { PublicKey } from "@solana/web3.js";
23

34
import { Emoji } from "../../../constants";
45
import {
@@ -24,12 +25,16 @@ export const useAirdrop = () => {
2425
await PgTerminal.process(async () => {
2526
if (!airdropAmount) return;
2627

28+
if (!PgWallet.current) {
29+
throw new Error("Wallet is not connected.");
30+
}
31+
2732
let msg;
2833
try {
2934
PgTerminal.println(PgTerminal.info("Sending an airdrop request..."));
3035

3136
const conn = PgConnection.current;
32-
const walletPk = PgWallet.current!.publicKey;
37+
const walletPk = PgWallet.current.publicKey;
3338

3439
// Airdrop tx is sometimes successful even when the balance hasn't
3540
// changed. To solve this, we check before and after balance instead
@@ -55,8 +60,11 @@ export const useAirdrop = () => {
5560
"Error receiving airdrop."
5661
)}`;
5762
}
58-
} catch (e: any) {
59-
const convertedError = PgTerminal.convertErrorMessage(e.message);
63+
} catch (error: unknown) {
64+
const convertedError = convertAirdropErrorMessage(error, {
65+
walletPublicKey: PgWallet.current.publicKey,
66+
airdropAmount: airdropAmount,
67+
});
6068
msg = `${Emoji.CROSS} ${PgTerminal.error(
6169
"Error receiving airdrop:"
6270
)}: ${convertedError}`;
@@ -68,3 +76,51 @@ export const useAirdrop = () => {
6876

6977
return { airdrop, airdropCondition: !!airdropAmount };
7078
};
79+
80+
function convertAirdropErrorMessage(
81+
error: unknown,
82+
params: { walletPublicKey: PublicKey; airdropAmount: number }
83+
): string {
84+
// Case 0: Not expected, return something debuggable.
85+
if (!(error instanceof Error)) return String(error);
86+
87+
const { message } = error;
88+
const { walletPublicKey, airdropAmount } = params;
89+
90+
// Case 1: Internal error is clean, but not user-friendly;
91+
// so enhance the message with instructions, keeping the original message.
92+
if (message.toLowerCase().includes("internal error")) {
93+
return createUserFriendlyErrorMessage(message, {
94+
walletPublicKey,
95+
airdropAmount,
96+
});
97+
}
98+
99+
// Case 2: User friendly message is presented, but the format is ugly and contains technical details.
100+
// Use the cleaned message instead of the original message, enhanced with instructions.
101+
const helpfulErrorMessages = [
102+
"You've either reached your airdrop limit today or the airdrop faucet has run dry.",
103+
];
104+
const helpfulMessage = helpfulErrorMessages.find((helpfulMessage) =>
105+
message.includes(helpfulMessage)
106+
);
107+
if (helpfulMessage) {
108+
return createUserFriendlyErrorMessage(helpfulMessage, {
109+
walletPublicKey,
110+
airdropAmount,
111+
});
112+
}
113+
114+
// Case 3: Use more generic message converter
115+
return PgTerminal.convertErrorMessage(message);
116+
}
117+
118+
function createUserFriendlyErrorMessage(
119+
originalMessage: string,
120+
params: { walletPublicKey: PublicKey; airdropAmount: number }
121+
) {
122+
const { walletPublicKey, airdropAmount } = params;
123+
const link = `https://faucet.solana.com/?walletAddress=${walletPublicKey.toBase58()}&amount=${airdropAmount}`;
124+
const instruction = `Try requesting devnet SOL from Faucet (pre-filled with your wallet):\n${link}`;
125+
return `${originalMessage}\n${instruction}`;
126+
}

0 commit comments

Comments
 (0)