@huma-finance/widgets 0.0.72-beta.883 → 0.0.72-beta.895

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huma-finance/widgets",
3
- "version": "0.0.72-beta.883+c4af575",
3
+ "version": "0.0.72-beta.895+0df1d3a",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -29,9 +29,9 @@
29
29
  "@ethersproject/experimental": "^5.7.0",
30
30
  "@ethersproject/providers": "^5.6.0",
31
31
  "@ethersproject/units": "^5.6.0",
32
- "@huma-finance/sdk": "^0.0.72-beta.883+c4af575",
33
- "@huma-finance/shared": "^0.0.72-beta.883+c4af575",
34
- "@huma-finance/web-shared": "^0.0.72-beta.883+c4af575",
32
+ "@huma-finance/sdk": "^0.0.72-beta.895+0df1d3a",
33
+ "@huma-finance/shared": "^0.0.72-beta.895+0df1d3a",
34
+ "@huma-finance/web-shared": "^0.0.72-beta.895+0df1d3a",
35
35
  "@mui/x-date-pickers": "^5.0.7",
36
36
  "@notifi-network/notifi-frontend-client": "^1.1.2",
37
37
  "@notifi-network/notifi-react": "^1.1.2",
@@ -201,5 +201,5 @@
201
201
  "optionalDependencies": {
202
202
  "encoding": "^0.1.13"
203
203
  },
204
- "gitHead": "c4af575e22fe85c2e554886d83d567a2a3465d7f"
204
+ "gitHead": "0df1d3a122dc84c3d351becf9ef5a99f578b436f"
205
205
  }
@@ -8,10 +8,10 @@ import { selectWidgetState } from '../../../store/widgets.selectors'
8
8
  import { WidgetWrapper } from '../../WidgetWrapper'
9
9
  import { WIDGET_STEP } from '../../../store/widgets.store'
10
10
  import { setStep } from '../../../store/widgets.reducers'
11
- import { ErrorModal } from '../../ErrorModal'
12
11
  import { ChooseAmount } from './1-ChooseAmount'
13
12
  import { Transfer } from './2-Transfer'
14
13
  import { Done } from './3-Done'
14
+ import { SolanaErrorModal } from '../../SolanaErrorModal'
15
15
 
16
16
  /**
17
17
  * Lend pool supply props
@@ -81,7 +81,8 @@ export function SolanaBorrow({
81
81
  />
82
82
  )}
83
83
  {step === WIDGET_STEP.Error && (
84
- <ErrorModal
84
+ <SolanaErrorModal
85
+ chainId={poolInfo.chainId}
85
86
  title={title}
86
87
  errorReason='Sorry there was an error'
87
88
  errorMessage={errorMessage}
@@ -8,10 +8,10 @@ import { selectWidgetState } from '../../../store/widgets.selectors'
8
8
  import { WidgetWrapper } from '../../WidgetWrapper'
9
9
  import { WIDGET_STEP } from '../../../store/widgets.store'
10
10
  import { setStep } from '../../../store/widgets.reducers'
11
- import { ErrorModal } from '../../ErrorModal'
12
11
  import { ChooseAmount } from './1-ChooseAmount'
13
12
  import { Transfer } from './2-Transfer'
14
13
  import { Done } from './3-Done'
14
+ import { SolanaErrorModal } from '../../SolanaErrorModal'
15
15
 
16
16
  /**
17
17
  * Lend pool supply props
@@ -74,7 +74,8 @@ export function SolanaPayment({
74
74
  <Done poolInfo={poolInfo} handleAction={handleClose} />
75
75
  )}
76
76
  {step === WIDGET_STEP.Error && (
77
- <ErrorModal
77
+ <SolanaErrorModal
78
+ chainId={poolInfo.chainId}
78
79
  title={title}
79
80
  errorReason='Sorry there was an error'
80
81
  errorMessage={errorMessage}
@@ -4,6 +4,7 @@ import { txAtom } from '@huma-finance/web-shared'
4
4
  import { useResetAtom } from 'jotai/utils'
5
5
  import React, { useCallback } from 'react'
6
6
 
7
+ import { openInNewTab } from '@huma-finance/shared'
7
8
  import { useAppDispatch } from '../hooks/useRedux'
8
9
  import { resetState } from '../store/widgets.reducers'
9
10
  import { SorryImg } from './images'
@@ -12,6 +13,7 @@ type Props = {
12
13
  title: string
13
14
  errorReason?: string
14
15
  errorMessage?: string
16
+ errorUrl?: string
15
17
  handleOk: () => void
16
18
  okText?: string
17
19
  shouldResetState?: boolean
@@ -21,6 +23,7 @@ export function ErrorModal({
21
23
  title,
22
24
  errorReason,
23
25
  errorMessage,
26
+ errorUrl,
24
27
  handleOk,
25
28
  okText = 'OKAY',
26
29
  shouldResetState = true,
@@ -85,11 +88,17 @@ export function ErrorModal({
85
88
  text-overflow: ellipsis;
86
89
  margin-right: ${theme.spacing(2)};
87
90
  `,
88
- okButton: css`
89
- & .MuiButtonBase-root {
90
- width: 100%;
91
- position: absolute;
92
- bottom: 0;
91
+ bottomButtonGroup: css`
92
+ width: 100%;
93
+ position: absolute;
94
+ bottom: 0;
95
+ display: flex;
96
+ row-gap: ${theme.spacing(1)};
97
+ column-gap: ${theme.spacing(1)};
98
+
99
+ button {
100
+ height: 40px;
101
+ margin-top: ${theme.spacing(1)};
93
102
  }
94
103
  `,
95
104
  }
@@ -119,8 +128,17 @@ export function ErrorModal({
119
128
  </Tooltip>
120
129
  </Box>
121
130
  </Box>
122
- <Box css={styles.okButton}>
123
- <Button variant='contained' onClick={handleCloseModal}>
131
+ <Box css={styles.bottomButtonGroup}>
132
+ {errorUrl && (
133
+ <Button
134
+ variant='outlined'
135
+ fullWidth
136
+ onClick={() => openInNewTab(errorUrl)}
137
+ >
138
+ SEE DETAILS
139
+ </Button>
140
+ )}
141
+ <Button variant='contained' fullWidth onClick={handleCloseModal}>
124
142
  {okText}
125
143
  </Button>
126
144
  </Box>
@@ -2,14 +2,15 @@
2
2
  import {
3
3
  CHAIN_TYPE,
4
4
  checkIsDev,
5
+ COMMON_ERROR_MESSAGE,
5
6
  IdentityServiceV2,
6
7
  NETWORK_TYPE,
8
+ SOLANA_TRANSACTION_EXPIRED_BLOCKHEIGHT_EXCEEDED_ERROR,
7
9
  timeUtil,
8
10
  TrancheType,
9
11
  } from '@huma-finance/shared'
10
12
  import { useChainInfo } from '@huma-finance/web-shared'
11
13
  import React, { useCallback, useEffect } from 'react'
12
-
13
14
  import { useAppDispatch } from '../../../hooks/useRedux'
14
15
  import { setError, setStep } from '../../../store/widgets.reducers'
15
16
  import { WIDGET_STEP } from '../../../store/widgets.store'
@@ -59,11 +60,7 @@ export function ApproveLenderBase({
59
60
  tryAttempts = 0
60
61
  } catch (e: unknown) {
61
62
  if (tryAttempts === 0) {
62
- dispatch(
63
- setError({
64
- errorMessage: 'Something went wrong, please try again later.',
65
- }),
66
- )
63
+ throw e
67
64
  } else {
68
65
  await timeUtil.sleep(3000)
69
66
  console.error(e)
@@ -71,28 +68,38 @@ export function ApproveLenderBase({
71
68
  }
72
69
  }
73
70
  },
74
- [
75
- account,
76
- chainId,
77
- chainSpecificData,
78
- dispatch,
79
- documentHash,
80
- isDev,
81
- networkType,
82
- ],
71
+ [account, chainId, chainSpecificData, documentHash, isDev, networkType],
83
72
  )
84
73
 
85
74
  useEffect(() => {
86
75
  const approveLenderAsync = async () => {
87
76
  if (account && chainId) {
88
- if (isUniTranche) {
89
- await approveLender(juniorTrancheVault)
90
- changeTranche('junior')
91
- dispatch(setStep(WIDGET_STEP.ChooseAmount))
92
- } else {
93
- await approveLender(juniorTrancheVault)
94
- await approveLender(seniorTrancheVault)
95
- dispatch(setStep(WIDGET_STEP.ChooseTranche))
77
+ try {
78
+ if (isUniTranche) {
79
+ await approveLender(juniorTrancheVault)
80
+ changeTranche('junior')
81
+ dispatch(setStep(WIDGET_STEP.ChooseAmount))
82
+ } else {
83
+ await approveLender(juniorTrancheVault)
84
+ await approveLender(seniorTrancheVault)
85
+ dispatch(setStep(WIDGET_STEP.ChooseTranche))
86
+ }
87
+ } catch (e: unknown) {
88
+ console.error(e)
89
+ // @ts-ignore
90
+ let errorMessage = e.message ?? COMMON_ERROR_MESSAGE
91
+ if (
92
+ errorMessage.indexOf(
93
+ SOLANA_TRANSACTION_EXPIRED_BLOCKHEIGHT_EXCEEDED_ERROR,
94
+ ) >= 0
95
+ ) {
96
+ errorMessage = `Transaction failed due to blockchain congestion. Please try again.`
97
+ }
98
+ dispatch(
99
+ setError({
100
+ errorMessage,
101
+ }),
102
+ )
96
103
  }
97
104
  }
98
105
  }
@@ -14,10 +14,10 @@ import { WidgetWrapper } from '../../WidgetWrapper'
14
14
  import { WIDGET_STEP } from '../../../store/widgets.store'
15
15
  import { ChooseAmount } from './2-ChooseAmount'
16
16
  import { setStep } from '../../../store/widgets.reducers'
17
- import { ErrorModal } from '../../ErrorModal'
18
17
  import { ChooseTranche } from './1-ChooseTranche'
19
18
  import { Done } from './4-Done'
20
19
  import { Transfer } from './3-Transfer'
20
+ import { SolanaErrorModal } from '../../SolanaErrorModal'
21
21
 
22
22
  /**
23
23
  * Lend pool supply props
@@ -143,7 +143,8 @@ export function SolanaLendAddRedemption({
143
143
  <Done poolInfo={poolInfo} handleAction={handleClose} />
144
144
  )}
145
145
  {step === WIDGET_STEP.Error && (
146
- <ErrorModal
146
+ <SolanaErrorModal
147
+ chainId={poolInfo.chainId}
147
148
  title={title}
148
149
  errorReason='Sorry there was an error'
149
150
  errorMessage={errorMessage}
@@ -9,10 +9,10 @@ import { selectWidgetState } from '../../../store/widgets.selectors'
9
9
  import { WidgetWrapper } from '../../WidgetWrapper'
10
10
  import { WIDGET_STEP } from '../../../store/widgets.store'
11
11
  import { setStep } from '../../../store/widgets.reducers'
12
- import { ErrorModal } from '../../ErrorModal'
13
12
  import { Transfer } from './2-Transfer'
14
13
  import { ChooseTranche } from './1-ChooseTranche'
15
14
  import { Done } from './3-Done'
15
+ import { SolanaErrorModal } from '../../SolanaErrorModal'
16
16
 
17
17
  /**
18
18
  * Lend pool supply props
@@ -150,7 +150,8 @@ export function SolanaLendCancelRedemption({
150
150
  />
151
151
  )}
152
152
  {step === WIDGET_STEP.Error && (
153
- <ErrorModal
153
+ <SolanaErrorModal
154
+ chainId={poolInfo.chainId}
154
155
  title={title}
155
156
  errorReason='Sorry there was an error'
156
157
  errorMessage={errorMessage}
@@ -7,11 +7,11 @@ import { useAppSelector } from '../../../hooks/useRedux'
7
7
  import { setStep } from '../../../store/widgets.reducers'
8
8
  import { selectWidgetState } from '../../../store/widgets.selectors'
9
9
  import { WIDGET_STEP } from '../../../store/widgets.store'
10
- import { ErrorModal } from '../../ErrorModal'
11
10
  import { WidgetWrapper } from '../../WidgetWrapper'
12
11
  import { Transfer } from './2-Transfer'
13
12
  import { Success } from './3-Success'
14
13
  import { ApproveAllowance } from './1-ApproveAllowance'
14
+ import { SolanaErrorModal } from '../../SolanaErrorModal'
15
15
 
16
16
  /**
17
17
  * Lend pool supply props
@@ -64,7 +64,8 @@ export function SolanaEnableAutoRedemption({
64
64
  />
65
65
  )}
66
66
  {step === WIDGET_STEP.Error && (
67
- <ErrorModal
67
+ <SolanaErrorModal
68
+ chainId={poolInfo.chainId}
68
69
  title='Auto-Redemption'
69
70
  errorReason='Sorry there was an error'
70
71
  errorMessage={errorMessage}
@@ -16,7 +16,6 @@ import { useAppSelector } from '../../../hooks/useRedux'
16
16
  import { setStep } from '../../../store/widgets.reducers'
17
17
  import { selectWidgetState } from '../../../store/widgets.selectors'
18
18
  import { WIDGET_STEP } from '../../../store/widgets.store'
19
- import { ErrorModal } from '../../ErrorModal'
20
19
  import { PointsEarned } from '../../PointsEarned'
21
20
  import { WidgetWrapper } from '../../WidgetWrapper'
22
21
  import { Evaluation } from './1-Evaluation'
@@ -26,6 +25,7 @@ import { ChooseAmount } from './4-ChooseAmount'
26
25
  import { ApproveAllowance } from './5-ApproveAllowance'
27
26
  import { Transfer } from './6-Transfer'
28
27
  import { Success } from './7-Success'
28
+ import { SolanaErrorModal } from '../../SolanaErrorModal'
29
29
 
30
30
  export interface Campaign {
31
31
  id: string
@@ -182,7 +182,8 @@ export function SolanaLendSupply({
182
182
  <PointsEarned lpConfig={poolState} handleAction={handleClose} />
183
183
  )}
184
184
  {step === WIDGET_STEP.Error && (
185
- <ErrorModal
185
+ <SolanaErrorModal
186
+ chainId={poolInfo.chainId}
186
187
  title='Supply'
187
188
  errorReason='Sorry there was an error'
188
189
  errorMessage={errorMessage}
@@ -0,0 +1,43 @@
1
+ import React from 'react'
2
+
3
+ import { getSolanaExplorerUrl, SolanaChainEnum } from '@huma-finance/shared'
4
+ import { useAppSelector } from '../hooks/useRedux'
5
+ import { ErrorModal } from './ErrorModal'
6
+ import { selectWidgetState } from '../store/widgets.selectors'
7
+
8
+ type Props = {
9
+ chainId: SolanaChainEnum
10
+ title: string
11
+ errorReason?: string
12
+ errorMessage?: string
13
+ handleOk: () => void
14
+ okText?: string
15
+ shouldResetState?: boolean
16
+ }
17
+
18
+ export function SolanaErrorModal({
19
+ chainId,
20
+ title,
21
+ errorReason,
22
+ errorMessage,
23
+ handleOk,
24
+ okText = 'OKAY',
25
+ shouldResetState = true,
26
+ }: Props): React.ReactElement {
27
+ const { solanaSignature } = useAppSelector(selectWidgetState)
28
+ const errorUrl = solanaSignature
29
+ ? getSolanaExplorerUrl(chainId, solanaSignature, 'tx')
30
+ : undefined
31
+
32
+ return (
33
+ <ErrorModal
34
+ title={title}
35
+ errorReason={errorReason}
36
+ errorMessage={errorMessage}
37
+ errorUrl={errorUrl ?? undefined}
38
+ handleOk={handleOk}
39
+ okText={okText}
40
+ shouldResetState={shouldResetState}
41
+ />
42
+ )
43
+ }
@@ -1,5 +1,5 @@
1
1
  import { sleep, SolanaChainEnum } from '@huma-finance/shared'
2
- import React, { useEffect, useState } from 'react'
2
+ import React, { useCallback, useEffect, useState } from 'react'
3
3
  import {
4
4
  buildOptimalTransactionFromConnection,
5
5
  extractWritableAccounts,
@@ -7,10 +7,13 @@ import {
7
7
 
8
8
  import { useConnection, useWallet } from '@solana/wallet-adapter-react'
9
9
  import { Transaction } from '@solana/web3.js'
10
+ import { useForceRefresh } from '@huma-finance/web-shared'
11
+ import { Box, Button, css, Typography, useTheme } from '@mui/material'
10
12
  import { useAppDispatch } from '../hooks/useRedux'
11
13
  import { setError, setSolanaSignature } from '../store/widgets.reducers'
12
14
  import { LoadingModal } from './LoadingModal'
13
15
  import { SolanaViewOnExplorer } from './SolanaViewOnExplorer'
16
+ import { SorryImg } from './images'
14
17
 
15
18
  type Props = {
16
19
  chainId: SolanaChainEnum
@@ -23,25 +26,78 @@ export function SolanaTxSendModal({
23
26
  tx,
24
27
  handleSuccess,
25
28
  }: Props): React.ReactElement | null {
29
+ const theme = useTheme()
26
30
  const dispatch = useAppDispatch()
27
- const { sendTransaction } = useWallet()
31
+ const { publicKey, signTransaction, sendTransaction } = useWallet()
28
32
  const { connection } = useConnection()
29
33
  const [signature, setSignature] = useState<string>('')
34
+ const [showRetryScreen, setShowRetryScreen] = useState<boolean>(false)
35
+ const [useHighPriority, setUseHighPriority] = useState<boolean>(false)
36
+ const [refreshCount, refresh] = useForceRefresh()
37
+
38
+ const styles = {
39
+ wrapper: css`
40
+ height: 438px;
41
+ position: relative;
42
+ `,
43
+ sorry: css`
44
+ ${theme.cssMixins.rowHCentered};
45
+ margin-top: ${theme.spacing(12)};
46
+ margin-right: ${theme.spacing(4)};
47
+ & > img {
48
+ width: 170px;
49
+ }
50
+ `,
51
+ bottomButtonGroup: css`
52
+ width: 100%;
53
+ position: absolute;
54
+ bottom: 0;
55
+ display: flex;
56
+ flex-direction: column;
57
+ row-gap: ${theme.spacing(1)};
58
+ column-gap: ${theme.spacing(1)};
59
+
60
+ button {
61
+ height: 40px;
62
+ margin-top: ${theme.spacing(1)};
63
+ }
64
+ `,
65
+ content: css`
66
+ margin-top: ${theme.spacing(2)};
67
+ font-size: 16px;
68
+ `,
69
+ retryHighPriorityButton: css`
70
+ font-size: 16px;
71
+ `,
72
+ }
30
73
 
31
74
  useEffect(() => {
32
75
  async function sendTx() {
33
- if (!connection || !tx) {
76
+ if (!connection || !tx || !publicKey || !signTransaction) {
34
77
  return
35
78
  }
36
79
 
37
80
  let signatureResult = ''
38
81
  try {
39
82
  // Optimize transaction
40
- const lockedWritableAccounts = extractWritableAccounts(tx)
83
+ // Make a copy of the tx so we can use the original tx for retries
84
+ const txCopy = new Transaction()
85
+ txCopy.instructions = [...tx.instructions]
86
+ const recentBlockhash = await connection.getLatestBlockhash('confirmed')
87
+ txCopy.recentBlockhash = recentBlockhash.blockhash
88
+ txCopy.lastValidBlockHeight = recentBlockhash.lastValidBlockHeight
89
+ txCopy.feePayer = publicKey
90
+ // Extract writable accounts
91
+ const txAccounts = extractWritableAccounts(txCopy)
92
+ // Add on compute unit limit + price instructions
41
93
  const optimizedTx = await buildOptimalTransactionFromConnection(
42
- tx,
43
- lockedWritableAccounts,
94
+ txCopy,
95
+ txAccounts,
44
96
  connection,
97
+ chainId,
98
+ publicKey,
99
+ useHighPriority ? 'High' : undefined,
100
+ process.env.REACT_APP_HELIUS_API_KEY,
45
101
  )
46
102
  signatureResult = await sendTransaction(optimizedTx, connection, {
47
103
  preflightCommitment: 'confirmed',
@@ -54,10 +110,24 @@ export function SolanaTxSendModal({
54
110
  lastValidBlockHeight: optimizedTx.lastValidBlockHeight!,
55
111
  signature: signatureResult,
56
112
  })
113
+ const txResult = await connection.getParsedTransaction(
114
+ signatureResult,
115
+ 'confirmed',
116
+ )
117
+ if (txResult?.meta?.err) {
118
+ dispatch(
119
+ setError({
120
+ errorMessage:
121
+ 'Your transaction was confirmed but had errors. Please check the transaction details on the explorer for more information.',
122
+ }),
123
+ )
124
+ return
125
+ }
57
126
  handleSuccess({ signature: signatureResult })
58
127
  } catch (error: unknown) {
59
- let signatureStatusRetries = 0
128
+ console.log(error)
60
129
 
130
+ let signatureStatusRetries = 0
61
131
  while (signatureStatusRetries < 5) {
62
132
  // Attempt to load the signature status using transaction history
63
133
  // eslint-disable-next-line no-await-in-loop
@@ -70,6 +140,15 @@ export function SolanaTxSendModal({
70
140
  result?.value?.confirmationStatus === 'finalized' ||
71
141
  result?.value?.confirmationStatus === 'confirmed'
72
142
  ) {
143
+ if (result?.value?.err) {
144
+ dispatch(
145
+ setError({
146
+ errorMessage:
147
+ 'Your transaction was confirmed but had errors. Please check the transaction details on the explorer for more information.',
148
+ }),
149
+ )
150
+ return
151
+ }
73
152
  handleSuccess({ signature: signatureResult })
74
153
  return
75
154
  }
@@ -78,11 +157,70 @@ export function SolanaTxSendModal({
78
157
  }
79
158
 
80
159
  const err = error as Error
81
- dispatch(setError({ errorMessage: err?.message || '' }))
160
+ if (err?.message?.includes('block height exceeded')) {
161
+ // Allow the user to retry the transaction
162
+ setShowRetryScreen(true)
163
+ } else {
164
+ dispatch(setError({ errorMessage: err?.message || '' }))
165
+ }
82
166
  }
83
167
  }
84
168
  sendTx()
85
- }, [connection, dispatch, handleSuccess, sendTransaction, tx])
169
+ }, [
170
+ chainId,
171
+ connection,
172
+ dispatch,
173
+ handleSuccess,
174
+ publicKey,
175
+ sendTransaction,
176
+ signTransaction,
177
+ tx,
178
+ refreshCount,
179
+ useHighPriority,
180
+ ])
181
+
182
+ const handleRetry = useCallback(
183
+ (highPriority: boolean) => {
184
+ console.log('handleRetry')
185
+ setUseHighPriority(highPriority)
186
+ setShowRetryScreen(false)
187
+ refresh()
188
+ },
189
+ [refresh],
190
+ )
191
+
192
+ if (showRetryScreen) {
193
+ return (
194
+ <Box css={styles.wrapper}>
195
+ <Box css={styles.sorry}>
196
+ <img src={SorryImg} alt='sorry' />
197
+ </Box>
198
+ <Typography variant='h6'>Block Height Exceeded</Typography>
199
+ <Box css={styles.content}>
200
+ Your transaction was unable to be confirmed in time. This can happen
201
+ when the network is congested. Please retry submitting your
202
+ transaction or retry with a higher priority fee.
203
+ </Box>
204
+ <Box css={styles.bottomButtonGroup}>
205
+ <Button
206
+ variant='outlined'
207
+ fullWidth
208
+ onClick={() => handleRetry(false)}
209
+ >
210
+ RETRY
211
+ </Button>
212
+ <Button
213
+ css={styles.retryHighPriorityButton}
214
+ variant='contained'
215
+ fullWidth
216
+ onClick={() => handleRetry(true)}
217
+ >
218
+ RETRY (HIGH PRIORITY)
219
+ </Button>
220
+ </Box>
221
+ </Box>
222
+ )
223
+ }
86
224
 
87
225
  return (
88
226
  <LoadingModal