1
- import React , { useContext , useMemo , useState } from 'react' ;
1
+ import { useContext , useState } from 'react' ;
2
2
import { useDispatch , useSelector } from 'react-redux' ;
3
- import { makeStyles } from 'tss-react/mui' ;
4
- import { useMediaQuery , useTheme } from '@mui/material' ;
5
- import CircularProgress from '@mui/material/CircularProgress' ;
6
- import Collapse from '@mui/material/Collapse' ;
7
- import Stack from '@mui/material/Stack' ;
8
- import Typography from '@mui/material/Typography' ;
9
- import ChevronLeft from '@mui/icons-material/ChevronLeft' ;
10
- import IconButton from '@mui/material/IconButton' ;
11
- import { getTransferDetails } from 'telemetry' ;
12
3
import { Context } from 'sdklegacy' ;
13
4
14
- import Button from 'components/v2/Button' ;
15
5
import config from 'config' ;
16
- import { addTxToLocalStorage } from 'utils/inProgressTxCache' ;
17
6
import { RouteContext } from 'contexts/RouteContext' ;
18
- import { useGasSlider } from 'hooks/useGasSlider' ;
7
+ import { useUSDamountGetter } from 'hooks/useUSDamountGetter' ;
8
+ import { useGetTokens } from 'hooks/useGetTokens' ;
19
9
import {
20
10
setTxDetails ,
21
11
setSendTx ,
@@ -24,57 +14,41 @@ import {
24
14
} from 'store/redeem' ;
25
15
import { setRoute as setAppRoute } from 'store/router' ;
26
16
import { setAmount , setIsTransactionInProgress } from 'store/transferInput' ;
27
- import { getWrappedToken } from 'utils' ;
17
+ import { getTransferDetails } from 'telemetry' ;
18
+ import { ERR_USER_REJECTED } from 'telemetry/types' ;
19
+ import { toDecimals } from 'utils/balance' ;
28
20
import { interpretTransferError } from 'utils/errors' ;
21
+ import { addTxToLocalStorage } from 'utils/inProgressTxCache' ;
29
22
import { validate , isTransferValid } from 'utils/transferValidation' ;
30
23
import {
31
24
registerWalletSigner ,
32
25
switchChain ,
33
26
TransferWallet ,
34
27
} from 'utils/wallet' ;
35
- import GasSlider from 'views/v2/Bridge/ReviewTransaction/GasSlider' ;
36
- import SingleRoute from 'views/v2/Bridge/Routes/SingleRoute' ;
37
28
38
29
import type { RootState } from 'store' ;
39
- import { RelayerFee } from 'store/relay' ;
40
-
41
- import { amount as sdkAmount } from '@wormhole-foundation/sdk' ;
42
- import { toDecimals } from 'utils/balance' ;
43
- import { useUSDamountGetter } from 'hooks/useUSDamountGetter' ;
44
- import SendError from './SendError' ;
45
- import { ERR_USER_REJECTED } from 'telemetry/types' ;
46
- import { useGetTokens } from 'hooks/useGetTokens' ;
47
-
48
- const useStyles = makeStyles ( ) ( ( theme ) => ( {
49
- container : {
50
- gap : '16px' ,
51
- width : '100%' ,
52
- maxWidth : '420px' ,
53
- } ,
54
- confirmTransaction : {
55
- padding : '8px 16px' ,
56
- borderRadius : '8px' ,
57
- margin : 'auto' ,
58
- maxWidth : '420px' ,
59
- width : '100%' ,
60
- } ,
61
- } ) ) ;
30
+ import type { RelayerFee } from 'store/relay' ;
31
+ import type { QuoteResult } from 'routes/operator' ;
62
32
63
33
type Props = {
64
- onClose : ( ) => void ;
65
- quotes : any ;
66
- isFetchingQuotes : boolean ;
34
+ quotes : Record < string , QuoteResult | undefined > ;
67
35
} ;
68
36
69
- const ReviewTransaction = ( props : Props ) => {
70
- const { classes } = useStyles ( ) ;
71
- const dispatch = useDispatch ( ) ;
72
- const theme = useTheme ( ) ;
37
+ type ReturnProps = {
38
+ error : string | undefined ;
39
+ // errorInternal can be a result of custom validation, hence of any type.
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ errorInternal : any | undefined ;
42
+ onConfirm : ( ) => void ;
43
+ } ;
73
44
74
- const mobile = useMediaQuery ( theme . breakpoints . down ( 'sm' ) ) ;
45
+ const useConfirmTransaction = ( props : Props ) : ReturnProps => {
46
+ const dispatch = useDispatch ( ) ;
75
47
76
- const [ sendError , setSendError ] = useState < string | undefined > ( undefined ) ;
77
- const [ sendErrorInternal , setSendErrorInternal ] = useState < any | undefined > (
48
+ const [ error , setError ] = useState < string | undefined > ( undefined ) ;
49
+ // errorInternal can be a result of custom validation, hence of any type.
50
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
+ const [ errorInternal , setErrorInternal ] = useState < any | undefined > (
78
52
undefined ,
79
53
) ;
80
54
@@ -86,39 +60,32 @@ const ReviewTransaction = (props: Props) => {
86
60
amount,
87
61
fromChain : sourceChain ,
88
62
toChain : destChain ,
89
- isTransactionInProgress,
90
63
route,
91
64
validations,
92
65
} = transferInput ;
93
66
67
+ const { sourceToken, destToken } = useGetTokens ( ) ;
68
+
94
69
const wallet = useSelector ( ( state : RootState ) => state . wallet ) ;
95
70
const { sending : sendingWallet , receiving : receivingWallet } = wallet ;
96
71
97
72
const relay = useSelector ( ( state : RootState ) => state . relay ) ;
98
73
const { toNativeToken } = relay ;
99
74
100
- const getUSDAmount = useUSDamountGetter ( ) ;
101
-
102
- const { sourceToken, destToken } = useGetTokens ( ) ;
103
-
104
- const { disabled : isGasSliderDisabled , showGasSlider } = useGasSlider ( {
105
- destChain,
106
- destToken : destToken ! . key ,
107
- route,
108
- valid : true ,
109
- isTransactionInProgress,
110
- } ) ;
111
-
112
75
const quoteResult = props . quotes [ route ?? '' ] ;
113
76
const quote = quoteResult ?. success ? quoteResult : undefined ;
114
-
115
77
const receiveNativeAmount = quote ?. destinationNativeGas ;
116
78
117
- const send = async ( ) => {
118
- setSendError ( undefined ) ;
79
+ const getUSDAmount = useUSDamountGetter ( ) ;
80
+
81
+ const onConfirm = async ( ) => {
82
+ // Clear previous errors
83
+ if ( error ) {
84
+ setError ( undefined ) ;
85
+ }
119
86
120
87
if ( config . ui . previewMode ) {
121
- setSendError ( 'Connect is in preview mode' ) ;
88
+ setError ( 'Connect is in preview mode' ) ;
122
89
return ;
123
90
}
124
91
@@ -135,6 +102,8 @@ const ReviewTransaction = (props: Props) => {
135
102
return ;
136
103
}
137
104
105
+ // Validate all inputs
106
+ // The results of this check will be written back to Redux store (see transferInput.validations).
138
107
await validate ( { transferInput, relay, wallet } , dispatch , ( ) => false ) ;
139
108
140
109
const valid = isTransferValid ( validations ) ;
@@ -162,12 +131,12 @@ const ReviewTransaction = (props: Props) => {
162
131
toWalletAddress : receivingWallet . address ,
163
132
} ) ;
164
133
if ( ! isValid ) {
165
- setSendError ( error ?? 'Transfer validation failed' ) ;
134
+ setError ( error ?? 'Transfer validation failed' ) ;
166
135
return ;
167
136
}
168
- } catch ( e ) {
169
- setSendError ( 'Error validating transfer' ) ;
170
- setSendErrorInternal ( e ) ;
137
+ } catch ( e : unknown ) {
138
+ setError ( 'Error validating transfer' ) ;
139
+ setErrorInternal ( e ) ;
171
140
console . error ( e ) ;
172
141
return ;
173
142
}
@@ -176,7 +145,7 @@ const ReviewTransaction = (props: Props) => {
176
145
dispatch ( setIsTransactionInProgress ( true ) ) ;
177
146
178
147
try {
179
- const fromConfig = config . chains [ sourceChain ! ] ;
148
+ const fromConfig = config . chains [ sourceChain ] ;
180
149
181
150
if ( fromConfig ?. context === Context . ETH ) {
182
151
const chainId = fromConfig . chainId ;
@@ -242,10 +211,11 @@ const ReviewTransaction = (props: Props) => {
242
211
recipient : receivingWallet . address ,
243
212
toChain : receipt . to ,
244
213
fromChain : receipt . from ,
245
- tokenAddress : getWrappedToken ( sourceToken ) . tokenId ! . address . toString ( ) ,
214
+ receivedToken : destToken . tuple ,
246
215
token : sourceToken . tuple ,
216
+ tokenAddress : sourceToken . tuple [ 1 ] ,
217
+ tokenKey : sourceToken . key ,
247
218
tokenDecimals : sourceToken . decimals ,
248
- receivedToken : destToken . tuple , // TODO: possibly wrong (e..g if portico swap fails)
249
219
relayerFee,
250
220
receiveAmount : quote . destinationToken . amount ,
251
221
receiveNativeAmount,
@@ -280,8 +250,8 @@ const ReviewTransaction = (props: Props) => {
280
250
dispatch ( setSendTx ( txId ) ) ;
281
251
dispatch ( setRedeemRoute ( route ) ) ;
282
252
dispatch ( setAppRoute ( 'redeem' ) ) ;
283
- setSendError ( undefined ) ;
284
- } catch ( e : any ) {
253
+ setError ( undefined ) ;
254
+ } catch ( e : unknown ) {
285
255
const [ uiError , transferError ] = interpretTransferError (
286
256
e ,
287
257
transferDetails ,
@@ -294,8 +264,8 @@ const ReviewTransaction = (props: Props) => {
294
264
console . error ( 'Wormhole Connect: error completing transfer' , e ) ;
295
265
296
266
// Show error in UI
297
- setSendError ( uiError ) ;
298
- setSendErrorInternal ( e ) ;
267
+ setError ( uiError ) ;
268
+ setErrorInternal ( e ) ;
299
269
300
270
// Trigger transfer error event to integrator
301
271
config . triggerEvent ( {
@@ -309,108 +279,11 @@ const ReviewTransaction = (props: Props) => {
309
279
}
310
280
} ;
311
281
312
- const walletsConnected = useMemo (
313
- ( ) => ! ! sendingWallet . address && ! ! receivingWallet . address ,
314
- [ sendingWallet . address , receivingWallet . address ] ,
315
- ) ;
316
-
317
- // Review transaction button is shown only when everything is ready
318
- const confirmTransactionButton = useMemo ( ( ) => {
319
- if (
320
- ! sourceChain ||
321
- ! sourceToken ||
322
- ! destChain ||
323
- ! destToken ||
324
- ! route ||
325
- ! amount
326
- ) {
327
- return null ;
328
- }
329
-
330
- return (
331
- < Button
332
- disabled = { props . isFetchingQuotes || isTransactionInProgress }
333
- variant = "primary"
334
- className = { classes . confirmTransaction }
335
- onClick = { ( ) => send ( ) }
336
- >
337
- { isTransactionInProgress ? (
338
- < Typography
339
- display = "flex"
340
- alignItems = "center"
341
- gap = { 1 }
342
- textTransform = "none"
343
- >
344
- < CircularProgress
345
- size = { 16 }
346
- sx = { { color : theme . palette . primary . contrastText } }
347
- />
348
- { mobile ? 'Preparing' : 'Preparing transaction' }
349
- </ Typography >
350
- ) : ! isTransactionInProgress && props . isFetchingQuotes ? (
351
- < Typography
352
- display = "flex"
353
- alignItems = "center"
354
- gap = { 1 }
355
- textTransform = "none"
356
- >
357
- < CircularProgress color = "secondary" size = { 16 } />
358
- { mobile ? 'Refreshing' : 'Refreshing quote' }
359
- </ Typography >
360
- ) : (
361
- < Typography textTransform = "none" >
362
- { mobile ? 'Confirm' : 'Confirm transaction' }
363
- </ Typography >
364
- ) }
365
- </ Button >
366
- ) ;
367
- } , [
368
- props . isFetchingQuotes ,
369
- isTransactionInProgress ,
370
- sourceChain ,
371
- sourceToken ,
372
- destChain ,
373
- destToken ,
374
- route ,
375
- amount ,
376
- send ,
377
- ] ) ;
378
-
379
- if ( ! route || ! walletsConnected ) {
380
- return < > </ > ;
381
- }
382
-
383
- return (
384
- < Stack className = { classes . container } >
385
- < div >
386
- < IconButton
387
- disabled = { isTransactionInProgress }
388
- sx = { { padding : 0 } }
389
- onClick = { ( ) => props . onClose ?.( ) }
390
- >
391
- < ChevronLeft sx = { { fontSize : '32px' } } />
392
- </ IconButton >
393
- </ div >
394
- < SingleRoute
395
- route = { route }
396
- isSelected = { true }
397
- destinationGasDrop = { receiveNativeAmount }
398
- quote = { quote }
399
- />
400
- { showGasSlider && (
401
- < Collapse in = { showGasSlider } >
402
- < GasSlider
403
- destinationGasDrop = {
404
- receiveNativeAmount || sdkAmount . fromBaseUnits ( 0n , 8 )
405
- }
406
- disabled = { isGasSliderDisabled }
407
- />
408
- </ Collapse >
409
- ) }
410
- < SendError humanError = { sendError } internalError = { sendErrorInternal } />
411
- { confirmTransactionButton }
412
- </ Stack >
413
- ) ;
282
+ return {
283
+ onConfirm,
284
+ error,
285
+ errorInternal,
286
+ } ;
414
287
} ;
415
288
416
- export default ReviewTransaction ;
289
+ export default useConfirmTransaction ;
0 commit comments