Skip to content

Commit e3f8626

Browse files
committed
breaking down codes
2 parents 31af251 + 0daee2a commit e3f8626

File tree

6 files changed

+271
-226
lines changed

6 files changed

+271
-226
lines changed

examples/vite-react/src/App.tsx

+30-226
Original file line numberDiff line numberDiff line change
@@ -1,214 +1,49 @@
1-
import { useState, useEffect } from 'react';
1+
import { useEffect } from 'react';
22
import {
33
Box,
44
Button,
55
Container,
6-
FormControl,
7-
FormLabel,
8-
Input,
9-
Text,
106
VStack,
11-
useToast,
12-
IconButton,
137
useColorMode,
14-
Link,
8+
IconButton,
159
Heading,
1610
Card,
1711
CardBody,
18-
HStack,
1912
} from '@chakra-ui/react';
20-
import { SunIcon, MoonIcon, RepeatIcon } from '@chakra-ui/icons';
13+
import { SunIcon, MoonIcon } from '@chakra-ui/icons';
2114
import { useForm } from 'react-hook-form';
2215
import { zodResolver } from '@hookform/resolvers/zod';
23-
import { useQuery, useMutation } from '@tanstack/react-query';
16+
import WalletDetails from './components/WalletDetails';
17+
import TransferForm from './components/TransferForm';
2418
import { transferFormSchema, type TransferFormData } from './utils/validation';
25-
import {
26-
DENOM,
27-
DENOM_DISPLAY,
28-
DECIMAL,
29-
RPC_ENDPOINT,
30-
} from './utils/constants';
31-
import { chain as cosmoshubChain, assetList as cosmoshubAssetList } from '@chain-registry/v2/mainnet/cosmoshub'
32-
import { WalletManager } from '@interchain-kit/core'
33-
import { keplrWallet } from '@interchain-kit/keplr-extension'
34-
import { createSend } from "interchainjs/cosmos/bank/v1beta1/tx.rpc.func";
35-
import { createGetBalance } from "interchainjs/cosmos/bank/v1beta1/query.rpc.func";
19+
import { useWalletManager } from './hooks/useWalletManager';
20+
import { useBalance } from './hooks/useBalance';
21+
import { useTransfer } from './hooks/useTransfer';
3622

3723
function App() {
38-
const [address, setAddress] = useState('');
39-
const [walletManager, setWalletManager] = useState<WalletManager | null>(null)
4024
const { colorMode, toggleColorMode } = useColorMode();
41-
const toast = useToast();
25+
const { walletManager, address, connectWallet } = useWalletManager();
26+
const { balance, refetchBalance } = useBalance(address, walletManager);
27+
const transferMutation = useTransfer(address, walletManager, refetchBalance);
4228

43-
const {
44-
register,
45-
handleSubmit,
46-
formState: { errors },
47-
reset,
48-
} = useForm<TransferFormData>({
29+
const { register, handleSubmit, formState: { errors }, reset } = useForm<TransferFormData>({
4930
resolver: zodResolver(transferFormSchema),
50-
defaultValues: {
51-
amount: "0.000001",
52-
},
31+
defaultValues: { amount: '0.000001' },
5332
});
5433

55-
useEffect(() => {
56-
(async () => {
57-
const walletManager = await WalletManager.create(
58-
[cosmoshubChain],
59-
[cosmoshubAssetList],
60-
[keplrWallet],
61-
{},
62-
{
63-
endpoints: {
64-
cosmoshub: {
65-
rpc: [RPC_ENDPOINT],
66-
},
67-
}
68-
}
69-
)
70-
setWalletManager(walletManager)
71-
})()
72-
}, [])
73-
74-
const { data: balance, refetch: refetchBalance } = useQuery({
75-
queryKey: ['balance', address],
76-
queryFn: async () => {
77-
if (!address) return null;
78-
try {
79-
const balanceQuery = createGetBalance(RPC_ENDPOINT);
80-
const { balance: atomBalance } = await balanceQuery({
81-
address,
82-
denom: DENOM,
83-
});
84-
return Number(atomBalance?.amount || 0) / Math.pow(10, DECIMAL);
85-
} catch (error) {
86-
console.error('Error fetching balance:', error);
87-
toast({
88-
title: 'Error fetching balance',
89-
description: (error as Error).message,
90-
status: 'error',
91-
duration: 5000,
92-
isClosable: true,
93-
});
94-
return null;
95-
}
96-
},
97-
enabled: !!address && !!walletManager,
98-
staleTime: 10000, // Consider data fresh for 10 seconds
99-
refetchInterval: 30000, // Refresh every 30 seconds
100-
});
101-
102-
const transferMutation = useMutation({
103-
mutationFn: async (data: TransferFormData) => {
104-
if (!window.keplr || !address) throw new Error('Keplr not connected');
105-
const amount = Math.floor(Number(data.amount) * Math.pow(10, DECIMAL));
106-
const fee = {
107-
amount: [{ denom: DENOM, amount: "5000" }], // adjust fee amount as needed
108-
gas: "200000" // adjust gas limit as needed
109-
};
110-
111-
const message = {
112-
fromAddress: address,
113-
toAddress: data.recipient,
114-
amount: [
115-
{
116-
denom: DENOM,
117-
amount: amount.toString()
118-
},
119-
],
120-
}
121-
const signingClient = await walletManager?.getSigningClient(keplrWallet.info?.name as string, cosmoshubChain.chainName)
122-
const txSend = createSend(signingClient);
123-
const res = await txSend(
124-
address,
125-
message,
126-
fee,
127-
''
128-
)
129-
await new Promise(resolve => setTimeout(resolve, 6000));
130-
return (res as any).hash
131-
},
132-
onSuccess: (txHash) => {
133-
toast({
134-
title: 'Transfer successful',
135-
description: (
136-
<Link
137-
href={`https://www.mintscan.io/cosmos/txs/${txHash}`}
138-
isExternal
139-
color="white"
140-
>
141-
<u>View transaction details</u>
142-
</Link>
143-
),
144-
status: 'success',
145-
duration: null,
146-
isClosable: true,
147-
});
148-
reset();
149-
refetchBalance();
150-
},
151-
onError: (error: Error) => {
152-
console.error('Error transferring funds:', error);
153-
toast({
154-
title: 'Transfer failed',
155-
description: error.message,
156-
status: 'error',
157-
duration: 5000,
158-
isClosable: true,
159-
});
160-
},
161-
});
162-
163-
const connectWallet = async () => {
164-
try {
165-
if (!window.keplr) {
166-
throw new Error('Please install Keplr extension');
167-
}
168-
await walletManager?.connect(keplrWallet.info?.name as string, cosmoshubChain.chainName)
169-
const account = await walletManager?.getAccount(keplrWallet.info?.name as string, cosmoshubChain.chainName)
170-
setAddress(account?.address as string)
171-
} catch (error) {
172-
console.error('Error connecting wallet:', error);
173-
toast({
174-
title: 'Connection failed',
175-
description: (error as Error).message,
176-
status: 'error',
177-
duration: 5000,
178-
isClosable: true,
179-
});
180-
}
181-
};
182-
18334
const onSubmit = (data: TransferFormData) => {
18435
if (!balance || Number(data.amount) > balance) {
185-
toast({
186-
title: 'Insufficient balance',
187-
status: 'error',
188-
duration: 5000,
189-
isClosable: true,
190-
});
19136
return;
19237
}
19338
transferMutation.mutate(data);
39+
reset();
19440
};
19541

19642
useEffect(() => {
197-
if (!walletManager) return
198-
connectWallet();
199-
}, [
200-
walletManager
201-
]);
202-
203-
const handleRefreshBalance = () => {
204-
refetchBalance();
205-
toast({
206-
title: 'Refreshing balance...',
207-
status: 'info',
208-
duration: 2000,
209-
isClosable: true,
210-
});
211-
};
43+
if (walletManager) {
44+
connectWallet();
45+
}
46+
}, [walletManager, connectWallet]);
21247

21348
return (
21449
<Container maxW="container.sm" py={8}>
@@ -220,57 +55,26 @@ function App() {
22055
onClick={toggleColorMode}
22156
/>
22257
</Box>
223-
22458
<Card>
22559
<CardBody>
22660
<VStack spacing={4} align="stretch">
22761
<Heading size="md">Cosmos Wallet</Heading>
228-
22962
{!address ? (
23063
<Button onClick={connectWallet}>Connect Keplr</Button>
23164
) : (
23265
<>
233-
<Text>Address: {address}</Text>
234-
<HStack>
235-
<Text>
236-
Balance: {balance ?? '0'} {DENOM_DISPLAY}
237-
</Text>
238-
<IconButton
239-
aria-label="Refresh balance"
240-
icon={<RepeatIcon />}
241-
size="sm"
242-
onClick={handleRefreshBalance}
243-
/>
244-
</HStack>
245-
246-
<form onSubmit={handleSubmit(onSubmit)}>
247-
<VStack spacing={4}>
248-
<FormControl isInvalid={!!errors.recipient}>
249-
<FormLabel>Recipient Address</FormLabel>
250-
<Input {...register('recipient')} />
251-
{errors.recipient && (
252-
<Text color="red.500">{errors.recipient.message}</Text>
253-
)}
254-
</FormControl>
255-
256-
<FormControl isInvalid={!!errors.amount}>
257-
<FormLabel>Amount ({DENOM_DISPLAY})</FormLabel>
258-
<Input {...register('amount')} type="number" step="0.000001" />
259-
{errors.amount && (
260-
<Text color="red.500">{errors.amount.message}</Text>
261-
)}
262-
</FormControl>
263-
264-
<Button
265-
type="submit"
266-
colorScheme="blue"
267-
isLoading={transferMutation.isPending}
268-
width="100%"
269-
>
270-
Transfer
271-
</Button>
272-
</VStack>
273-
</form>
66+
<WalletDetails
67+
address={address}
68+
balance={balance ?? '0'}
69+
onRefresh={refetchBalance}
70+
/>
71+
<TransferForm
72+
register={register}
73+
errors={errors}
74+
handleSubmit={handleSubmit}
75+
onSubmit={onSubmit}
76+
isLoading={transferMutation.isMutating}
77+
/>
27478
</>
27579
)}
27680
</VStack>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { FormControl, FormLabel, Input, Text, VStack, Button } from '@chakra-ui/react';
2+
import { DENOM_DISPLAY } from '../utils/constants';
3+
import { TransferFormData } from '../utils/validation';
4+
5+
interface TransferFormProps {
6+
register: ReturnType<any>;
7+
errors: any;
8+
handleSubmit: (fn: (data: TransferFormData) => void) => (e?: React.BaseSyntheticEvent) => Promise<void>;
9+
onSubmit: (data: TransferFormData) => void;
10+
isLoading: boolean;
11+
}
12+
13+
const TransferForm = ({ register, errors, handleSubmit, onSubmit, isLoading }: TransferFormProps) => {
14+
return (
15+
<form onSubmit={handleSubmit(onSubmit)}>
16+
<VStack spacing={4}>
17+
<FormControl isInvalid={!!errors.recipient}>
18+
<FormLabel>Recipient Address</FormLabel>
19+
<Input {...register('recipient')} />
20+
{errors.recipient && <Text color="red.500">{errors.recipient.message}</Text>}
21+
</FormControl>
22+
<FormControl isInvalid={!!errors.amount}>
23+
<FormLabel>Amount ({DENOM_DISPLAY})</FormLabel>
24+
<Input {...register('amount')} type="number" step="0.000001" />
25+
{errors.amount && <Text color="red.500">{errors.amount.message}</Text>}
26+
</FormControl>
27+
<Button type="submit" colorScheme="blue" isLoading={isLoading} width="100%">
28+
Transfer
29+
</Button>
30+
</VStack>
31+
</form>
32+
);
33+
};
34+
35+
export default TransferForm;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Box, Text, HStack, IconButton } from '@chakra-ui/react';
2+
import { RepeatIcon } from '@chakra-ui/icons';
3+
import { DENOM_DISPLAY } from '../utils/constants';
4+
5+
interface WalletDetailsProps {
6+
address: string;
7+
balance: number | string;
8+
onRefresh: () => void;
9+
}
10+
11+
const WalletDetails = ({ address, balance, onRefresh }: WalletDetailsProps) => {
12+
return (
13+
<Box>
14+
<Text>Address: {address}</Text>
15+
<HStack>
16+
<Text>
17+
Balance: {balance ?? '0'} {DENOM_DISPLAY}
18+
</Text>
19+
<IconButton
20+
aria-label="Refresh balance"
21+
icon={<RepeatIcon />}
22+
size="sm"
23+
onClick={onRefresh}
24+
/>
25+
</HStack>
26+
</Box>
27+
);
28+
};
29+
30+
export default WalletDetails;

0 commit comments

Comments
 (0)