Skip to content

Commit cbc3db7

Browse files
authored
Add custom ERC20 (#289)
* Add * Update (fmt) * Update * Update routeTree.gen.ts
1 parent 8947a37 commit cbc3db7

16 files changed

Lines changed: 1090 additions & 926 deletions

src/components/AddCustomToken.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { memo, useEffect, useState } from "react";
2+
import { type Address } from "viem";
3+
import { useTranslation } from "react-i18next";
4+
import { useErc20Metadata } from "@/hooks/use-erc20-metadata";
5+
import { Button } from "@/components/ui/button";
6+
import { Loader2, AlertCircle } from "lucide-react";
7+
import { TokenImage } from "./TokenImage";
8+
9+
interface AddCustomTokenProps {
10+
address: Address;
11+
onAdd: () => void;
12+
existsInList: boolean;
13+
}
14+
15+
export const AddCustomToken = memo(({ address, onAdd, existsInList }: AddCustomTokenProps) => {
16+
const { t } = useTranslation();
17+
const { symbol, decimals, name, isLoading } = useErc20Metadata({
18+
tokenAddress: address,
19+
});
20+
const [error, setError] = useState<string | null>(null);
21+
22+
const isValidErc20 = !isLoading && symbol && decimals !== undefined;
23+
24+
useEffect(() => {
25+
if (!isLoading && !isValidErc20) {
26+
setError(t("tokenSelector.not_erc20"));
27+
} else if (existsInList) {
28+
setError(t("tokenSelector.already_added"));
29+
} else {
30+
setError(null);
31+
}
32+
}, [isLoading, isValidErc20, existsInList, t]);
33+
34+
const handleAdd = () => {
35+
if (isValidErc20 && symbol && decimals !== undefined && name) {
36+
onAdd();
37+
}
38+
};
39+
40+
if (isLoading) {
41+
return (
42+
<div className="flex flex-col items-center gap-3 py-3">
43+
<Loader2 className="h-8 w-8 animate-spin text-primary" />
44+
<p className="text-sm text-muted-foreground">{t("tokenSelector.validating_token")}</p>
45+
</div>
46+
);
47+
}
48+
49+
if (error) {
50+
return (
51+
<div className="flex flex-col items-center gap-3 py-3">
52+
<AlertCircle className="h-8 w-8 text-destructive" />
53+
<p className="text-sm text-destructive">{error}</p>
54+
</div>
55+
);
56+
}
57+
58+
if (isValidErc20 && symbol && name) {
59+
return (
60+
<div className="flex flex-col items-center gap-3 py-3 px-4">
61+
<div className="flex items-center gap-3 p-4 bg-muted rounded-lg w-full">
62+
<TokenImage symbol={symbol} imageUrl={null} />
63+
<div className="flex flex-col min-w-0 flex-1">
64+
<span className="font-medium truncate">{symbol}</span>
65+
<span className="text-xs text-muted-foreground truncate">{name}</span>
66+
<span className="text-xs text-muted-foreground">
67+
{decimals} {t("common.decimals", { defaultValue: "decimals" })}
68+
</span>
69+
</div>
70+
</div>
71+
<Button onClick={handleAdd} className="w-full">
72+
{t("tokenSelector.add_custom_token")}
73+
</Button>
74+
<p className="text-xs text-muted-foreground text-center">{address}</p>
75+
</div>
76+
);
77+
}
78+
79+
return null;
80+
});
81+
82+
AddCustomToken.displayName = "AddCustomToken";

0 commit comments

Comments
 (0)