Skip to content

Commit

Permalink
Fix typescript props type issue, remove duplicate custom tokens, add …
Browse files Browse the repository at this point in the history
…native token balance validation
  • Loading branch information
MrKampla committed Jul 24, 2024
1 parent 5eabddf commit 11a63f6
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 56 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const Wallet = () => {
name: 'USD Coin',
symbol: 'USDC',
img: 'https://etherscan.io/token/images/centre-usdc_28.png',
chainId: 137,
},
]
: [
Expand All @@ -99,6 +100,7 @@ const Wallet = () => {
name: 'USD Coin',
symbol: 'USDC',
img: 'https://etherscan.io/token/images/centre-usdc_28.png',
chainId: 8453,
},
];

Expand All @@ -122,6 +124,15 @@ const Wallet = () => {
// TOKEN: 'Token',
// TOKENS: 'Tokeny',
// ENTER_VALID_WALLET: 'Wprowadź poprawny adres portfela',
// ADD: 'Dodaj',
// DECIMALS: 'Miejsca dziesiętne',
// IMPORT_TOKENS: 'Importuj tokeny',
// PRECISION: 'Precyzja',
// SYMBOL: 'Symbol',
// REFRESH_LIST: 'Odśwież listę',
// TOKEN_ADDRESS: 'Adres tokena',
// UNKNOWN_CHAIN: 'Nieznana sieć',
// AMOUNT_EXCEEDS_BALANCE: 'Kwota przekracza saldo',
// }}
withNativeToken
onSendErc20Token={(token, _txRequest) => {
Expand Down
19 changes: 10 additions & 9 deletions lib/components/TokenIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ const TokenIcon = ({
img: React.ReactNode | string;
symbol?: string;
}) => {
return typeof img === 'string' ? (
img ? (
if (img) {
return typeof img === 'string' ? (
<img src={img} className="ww-w-8 ww-h-8 ww-rounded-full" />
) : (
<div className="ww-w-fit">
<div className="ww-bg-primary ww-text-sm ww-text-primary-foreground ww-flex ww-h-8 ww-w-8 ww-items-center ww-justify-center ww-rounded-full">
{symbol?.slice(0, 3) ?? '?'}
</div>
img
);
}
return (
<div className="ww-w-fit">
<div className="ww-bg-primary ww-text-sm ww-text-primary-foreground ww-flex ww-h-8 ww-w-8 ww-items-center ww-justify-center ww-rounded-full">
{symbol?.slice(0, 3) ?? '?'}
</div>
)
) : (
img
</div>
);
};

Expand Down
16 changes: 14 additions & 2 deletions lib/helpers/useCustomTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { WagmiWalletUiStore } from '@/store';
import { Token } from '@/types';
import { useLocalStorage } from '@uidotdev/usehooks';
import { useContext } from 'react';
import { useChainId } from 'wagmi';

export const useCustomTokens = () => {
const chainId = useChainId();
const { customTokensStorageId } = useContext(WagmiWalletUiStore);
const [customTokens, saveCustomTokens] = useLocalStorage<Token[]>(
customTokensStorageId!,
Expand All @@ -16,8 +18,18 @@ export const useCustomTokens = () => {
);
};
const addCustomToken = (token: Token) => {
saveCustomTokens([...customTokens, token]);
saveCustomTokens(
[...customTokens, token].filter(
(token, index, allTokens) =>
allTokens.findIndex(t => t.address === token.address) === index,
),
);
};

return { customTokens, saveCustomTokens, removeCustomToken, addCustomToken };
return {
customTokens: customTokens.filter(token => token.chainId === chainId),
saveCustomTokens,
removeCustomToken,
addCustomToken,
};
};
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as WagmiWalletUI } from './WagmiWalletUI';
export type { WagmiWalletUIProps } from './WagmiWalletUI';
export type { Token } from './types';
4 changes: 3 additions & 1 deletion lib/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
"SYMBOL": "Symbol",
"PRECISION": "Precision",
"DECIMALS": "Decimals",
"ADD": "Add"
"ADD": "Add",
"UNKNOWN_CHAIN": "Unknown chain",
"AMOUNT_EXCEEDS_BALANCE": "Amount exceeds balance"
}
1 change: 1 addition & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type Token = {
symbol: string;
address: string;
decimals: number;
chainId: number;
img?: ReactNode | string;
};

Expand Down
4 changes: 3 additions & 1 deletion lib/views/addTokenView/AddTokenForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import * as viem from 'viem';
import { useReadContract } from 'wagmi';
import { useChainId, useReadContract } from 'wagmi';
import { useContext, useEffect } from 'react';
import { useTranslation } from '@/helpers/useTranslation';
import { LoaderIcon } from 'lucide-react';
Expand All @@ -23,6 +23,7 @@ import { useCustomTokens } from '@/helpers/useCustomTokens';
export function AddTokenForm() {
const { setCurrentView } = useContext(WagmiWalletUiStore);
const { addCustomToken } = useCustomTokens();
const chainId = useChainId();

const t = useTranslation();

Expand Down Expand Up @@ -105,6 +106,7 @@ export function AddTokenForm() {
decimals: values.precision,
name: tokenName || values.symbol,
img: '',
chainId,
});
setCurrentView('wallet');
}
Expand Down
72 changes: 53 additions & 19 deletions lib/views/sendView/SendForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ import {
import { Token } from '@/types';
import {
useAccount,
useBalance,
useChainId,
useChains,
usePrepareTransactionRequest,
usePublicClient,
useReadContract,
useSendTransaction,
useSimulateContract,
useWalletClient,
useWriteContract,
} from 'wagmi';
import { useContext } from 'react';
Expand All @@ -48,13 +49,18 @@ export function SendForm({ tokens }: { tokens: Token[] }) {
withNativeToken,
nativeTokenImg,
onSendNativeToken,
setCurrentView,
} = useContext(WagmiWalletUiStore);
const t = useTranslation();
const { address } = useAccount();
const chainId = useChainId();
const chains = useChains();
const selectedChain = chains.find(chain => chain.id === chainId);

const { data: nativeTokenBalance } = useBalance({
address,
});

const FormSchema = z.object({
token: z.string().min(1, {
message: t('SELECT_TOKEN_TO_SEND'),
Expand All @@ -74,7 +80,19 @@ export function SendForm({ tokens }: { tokens: Token[] }) {
message: t('ENTER_VALID_WALLET'),
},
),
amount: z.string().min(1),
amount: z
.string()
.min(1)
.refine(
value =>
nativeTokenBalance?.value
? viem.parseUnits(
value,
selectedChain?.nativeCurrency.decimals || 18,
) <= nativeTokenBalance?.value
: true,
{ message: t('AMOUNT_EXCEEDS_BALANCE') },
),
});

const form = useForm<z.infer<typeof FormSchema>>({
Expand Down Expand Up @@ -109,7 +127,8 @@ export function SendForm({ tokens }: { tokens: Token[] }) {
type: 'eip1559',
account: address!,
});
const { writeContractAsync: sendToken } = useWriteContract();
const { writeContractAsync: sendToken, isPending: isSendingErc20Token } =
useWriteContract();

const {
data: transactionRequest,
Expand All @@ -119,9 +138,10 @@ export function SendForm({ tokens }: { tokens: Token[] }) {
to: form.watch('targetAddress') as `0x${string}`,
value: viem.parseUnits(form.watch('amount'), selectedToken?.decimals || 18),
});
const { sendTransactionAsync, isPending: isSendingNativeToken } =
useSendTransaction();

const publicClient = usePublicClient();
const { data: walletClient } = useWalletClient();

async function onSubmit() {
if (
Expand All @@ -134,9 +154,7 @@ export function SendForm({ tokens }: { tokens: Token[] }) {

try {
const txHash = await (isNativeTransferSelected
? walletClient!.sendTransaction(
await onSendNativeToken(transactionRequest!),
)
? sendTransactionAsync(await onSendNativeToken(transactionRequest!))
: sendToken(
await onSendErc20Token(
selectedToken!,
Expand All @@ -150,6 +168,7 @@ export function SendForm({ tokens }: { tokens: Token[] }) {
hash: txHash,
});
await onTxInclusion?.(receipt);
setCurrentView('wallet');
} catch (error) {
await onTxFail?.(error as Error);
}
Expand Down Expand Up @@ -227,15 +246,25 @@ export function SendForm({ tokens }: { tokens: Token[] }) {
type="button"
variant="ghost"
className="ww-ml-auto"
onClick={() =>
form.setValue(
'amount',
viem.formatUnits(
tokenBalance || 0n,
selectedToken?.decimals || 18,
),
)
}
onClick={() => {
if (isNativeTransferSelected) {
form.setValue(
'amount',
viem.formatUnits(
nativeTokenBalance?.value || 0n,
nativeTokenBalance?.decimals || 18,
),
);
} else {
form.setValue(
'amount',
viem.formatUnits(
tokenBalance || 0n,
selectedToken?.decimals || 18,
),
);
}
}}
>
{t('MAX')}
</Button>
Expand All @@ -251,10 +280,15 @@ export function SendForm({ tokens }: { tokens: Token[] }) {
<div className="ww-w-full ww-text-destructive">
{(error as any)?.cause?.shortMessage}
</div>
<Button type="submit">
{(isNativeTransferSelected
<Button
disabled={isSendingErc20Token || isSendingNativeToken}
type="submit"
>
{((isNativeTransferSelected
? isLoadingTransactionRequest
: isLoadingTransferSimulation) && (
: isLoadingTransferSimulation) ||
isSendingErc20Token ||
isSendingNativeToken) && (
<LoaderIcon className="ww-animate-spin ww-size-4 ww-mr-2" />
)}
{t('SEND')}
Expand Down
16 changes: 7 additions & 9 deletions lib/views/walletView/ChainBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Badge } from '@/components/ui/badge';
import { useTranslation } from '@/helpers/useTranslation';
import { WagmiWalletUiStore } from '@/store';
import { useContext } from 'react';
import { useChainId, useChains } from 'wagmi';
import { useAccount } from 'wagmi';

const ChainBadge = () => {
const { onChainSelectorClick } = useContext(WagmiWalletUiStore);
const chainId = useChainId();
const chains = useChains();
const defaultChain = chains[0];
const selectedChain =
chains.find(chain => chain.id === chainId) ?? defaultChain;
const { chain } = useAccount();
const t = useTranslation();

return (
<Badge
Expand All @@ -21,10 +19,10 @@ const ChainBadge = () => {
body.style.pointerEvents = 'auto';
}
}}
variant="default"
className="ww-w-fit ww-cursor-pointer ww-rounded-md"
variant={chain ? 'default' : 'destructive'}
className={'ww-w-fit ww-cursor-pointer ww-rounded-md'}
>
{selectedChain.name}
{chain?.name ?? t('UNKNOWN_CHAIN')}
</Badge>
);
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wagmi-wallet-ui",
"version": "0.3.0",
"version": "0.3.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
35 changes: 23 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { RainbowKitProvider, useChainModal } from '@rainbow-me/rainbowkit';
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { Toaster } from 'sonner';
import { toast } from 'sonner';
import { base } from 'viem/chains';
import { base, polygon } from 'viem/chains';

const queryClient = new QueryClient();

Expand Down Expand Up @@ -36,6 +36,7 @@ const Wallet = () => {
name: 'USD Coin',
symbol: 'USDC',
img: 'https://etherscan.io/token/images/centre-usdc_28.png',
chainId: base.id,
},
]
: [
Expand All @@ -45,6 +46,7 @@ const Wallet = () => {
name: 'USD Coin',
symbol: 'USDC',
img: 'https://etherscan.io/token/images/centre-usdc_28.png',
chainId: polygon.id,
},
];

Expand All @@ -58,17 +60,26 @@ const Wallet = () => {
: 'https://cdn-icons-png.freepik.com/512/12114/12114233.png'
}
// Override translations (example in Polish)
// translations={{
// AMOUNT: 'Ilość',
// DISCONNECT: 'Rozłącz',
// MAX: 'MAX',
// SEND: 'Wyślij',
// SELECT_TOKEN_TO_SEND: 'Wybierz token do wysłania...',
// TO_ADDRESS: 'Na adres',
// TOKEN: 'Token',
// TOKENS: 'Tokeny',
// ENTER_VALID_WALLET: 'Wprowadź poprawny adres portfela',
// }}
translations={{
AMOUNT: 'Ilość',
DISCONNECT: 'Rozłącz',
MAX: 'MAX',
SEND: 'Wyślij',
SELECT_TOKEN_TO_SEND: 'Wybierz token do wysłania...',
TO_ADDRESS: 'Na adres',
TOKEN: 'Token',
TOKENS: 'Tokeny',
ENTER_VALID_WALLET: 'Wprowadź poprawny adres portfela',
ADD: 'Dodaj',
DECIMALS: 'Miejsca dziesiętne',
IMPORT_TOKENS: 'Importuj tokeny',
PRECISION: 'Precyzja',
SYMBOL: 'Symbol',
REFRESH_LIST: 'Odśwież listę',
TOKEN_ADDRESS: 'Adres tokena',
UNKNOWN_CHAIN: 'Nieznana sieć',
AMOUNT_EXCEEDS_BALANCE: 'Kwota przekracza saldo',
}}
withNativeToken
onSendErc20Token={(token, _txRequest) => {
toast.loading(`Sending ${token.symbol}...`, {
Expand Down
Loading

0 comments on commit 11a63f6

Please sign in to comment.