1
- import { useState , useEffect } from 'react' ;
1
+ import { useEffect } from 'react' ;
2
2
import {
3
3
Box ,
4
4
Button ,
5
5
Container ,
6
- FormControl ,
7
- FormLabel ,
8
- Input ,
9
- Text ,
10
6
VStack ,
11
- useToast ,
12
- IconButton ,
13
7
useColorMode ,
14
- Link ,
8
+ IconButton ,
15
9
Heading ,
16
10
Card ,
17
11
CardBody ,
18
- HStack ,
19
12
} from '@chakra-ui/react' ;
20
- import { SunIcon , MoonIcon , RepeatIcon } from '@chakra-ui/icons' ;
13
+ import { SunIcon , MoonIcon } from '@chakra-ui/icons' ;
21
14
import { useForm } from 'react-hook-form' ;
22
15
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' ;
24
18
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' ;
36
22
37
23
function App ( ) {
38
- const [ address , setAddress ] = useState ( '' ) ;
39
- const [ walletManager , setWalletManager ] = useState < WalletManager | null > ( null )
40
24
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 ) ;
42
28
43
- const {
44
- register,
45
- handleSubmit,
46
- formState : { errors } ,
47
- reset,
48
- } = useForm < TransferFormData > ( {
29
+ const { register, handleSubmit, formState : { errors } , reset } = useForm < TransferFormData > ( {
49
30
resolver : zodResolver ( transferFormSchema ) ,
50
- defaultValues : {
51
- amount : "0.000001" ,
52
- } ,
31
+ defaultValues : { amount : '0.000001' } ,
53
32
} ) ;
54
33
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
-
183
34
const onSubmit = ( data : TransferFormData ) => {
184
35
if ( ! balance || Number ( data . amount ) > balance ) {
185
- toast ( {
186
- title : 'Insufficient balance' ,
187
- status : 'error' ,
188
- duration : 5000 ,
189
- isClosable : true ,
190
- } ) ;
191
36
return ;
192
37
}
193
38
transferMutation . mutate ( data ) ;
39
+ reset ( ) ;
194
40
} ;
195
41
196
42
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 ] ) ;
212
47
213
48
return (
214
49
< Container maxW = "container.sm" py = { 8 } >
@@ -220,57 +55,26 @@ function App() {
220
55
onClick = { toggleColorMode }
221
56
/>
222
57
</ Box >
223
-
224
58
< Card >
225
59
< CardBody >
226
60
< VStack spacing = { 4 } align = "stretch" >
227
61
< Heading size = "md" > Cosmos Wallet</ Heading >
228
-
229
62
{ ! address ? (
230
63
< Button onClick = { connectWallet } > Connect Keplr</ Button >
231
64
) : (
232
65
< >
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
+ />
274
78
</ >
275
79
) }
276
80
</ VStack >
0 commit comments