@huma-finance/widgets 0.0.59-beta.489 → 0.0.59-beta.492

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.
Files changed (41) hide show
  1. package/API.md +1 -0
  2. package/dist/cjs/index.cjs +1095 -323
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/index.d.ts +6 -0
  5. package/dist/index.js +1096 -325
  6. package/dist/index.js.map +1 -1
  7. package/package.json +6 -4
  8. package/src/components/Activity.tsx +1 -2
  9. package/src/components/ApproveAllowanceModal.tsx +1 -1
  10. package/src/components/ApproveAllowanceModalV2.tsx +1 -1
  11. package/src/components/ChooseAmountModal.tsx +10 -8
  12. package/src/components/ConfirmTransferModal.tsx +3 -3
  13. package/src/components/CreditLine/borrow/1-Evaluation.tsx +2 -2
  14. package/src/components/ErrorModal.tsx +3 -10
  15. package/src/components/InputAmountModal.tsx +7 -5
  16. package/src/components/Lend/supply/1-Evaluation/EvaluationEA.tsx +1 -1
  17. package/src/components/Lend/supply/1-Evaluation/EvaluationKYC.tsx +1 -1
  18. package/src/components/Lend/supplyV2/2-Evaluation.tsx +28 -329
  19. package/src/components/Lend/supplyV2/3-ChooseAmount.tsx +54 -9
  20. package/src/components/Lend/supplyV2/6-Success.tsx +49 -6
  21. package/src/components/Lend/supplyV2/7-Notifications.tsx +22 -3
  22. package/src/components/Lend/supplyV2/8-PointsEarned.tsx +219 -0
  23. package/src/components/Lend/supplyV2/components/PersonaEvaluation.tsx +426 -0
  24. package/src/components/Lend/supplyV2/components/SecuritizeEvaluation.tsx +345 -0
  25. package/src/components/Lend/supplyV2/index.tsx +61 -5
  26. package/src/components/Lend/withdrawV2/1-ConfirmTransfer.tsx +1 -1
  27. package/src/components/Notifi/NotifiSubscriptionModal.tsx +1 -1
  28. package/src/components/SignIn.tsx +8 -3
  29. package/src/components/TxDoneModal.tsx +22 -3
  30. package/src/components/WrapperModal.tsx +1 -1
  31. package/src/components/humaModal/HumaModal.tsx +4 -3
  32. package/src/components/humaModal/HumaModalHeader.tsx +1 -0
  33. package/src/components/icons/congratulations.svg +54 -0
  34. package/src/components/icons/huma-points.svg +15 -0
  35. package/src/components/icons/index.tsx +15 -0
  36. package/src/components/icons/ribbon.svg +9 -0
  37. package/src/store/widgets.reducers.ts +0 -1
  38. package/src/store/widgets.store.ts +1 -0
  39. package/src/theme/components.ts +2 -1
  40. package/src/theme/palette.ts +7 -7
  41. package/src/theme/typography.ts +0 -1
@@ -0,0 +1,426 @@
1
+ import {
2
+ CAMPAIGN_REFERENCE_CODE,
3
+ CampaignService,
4
+ checkIsDev,
5
+ formatNumber,
6
+ IdentityServiceV2,
7
+ IdentityVerificationStatusV2,
8
+ KYCCopy,
9
+ PoolInfoV2,
10
+ TrancheType,
11
+ useAuthErrorHandling,
12
+ VerificationStatusResultV2,
13
+ } from '@huma-finance/shared'
14
+ import { Box, css, useTheme } from '@mui/material'
15
+ import { useWeb3React } from '@web3-react/core'
16
+ import { BigNumber } from 'ethers'
17
+ import Persona, { Client } from 'persona'
18
+ import React, { useCallback, useEffect, useRef, useState } from 'react'
19
+
20
+ import { Campaign } from '..'
21
+ import { useAppDispatch } from '../../../../hooks/useRedux'
22
+ import { setError, setStep } from '../../../../store/widgets.reducers'
23
+ import { WIDGET_STEP } from '../../../../store/widgets.store'
24
+ import { BottomButton } from '../../../BottomButton'
25
+ import { ApproveLenderImg } from '../../../images'
26
+ import { LoadingModal } from '../../../LoadingModal'
27
+ import { WrapperModal } from '../../../WrapperModal'
28
+
29
+ type LoadingType = 'verificationStatus' | 'startKYC' | 'approveLender'
30
+
31
+ const LoadingCopiesByType: {
32
+ [key: string]: {
33
+ description: string
34
+ }
35
+ } = {
36
+ verificationStatus: {
37
+ description: `Checking your verification status...`,
38
+ },
39
+ startKYC: {
40
+ description: `Starting KYC...`,
41
+ },
42
+ approveLender: {
43
+ description: `Approving as lender...`,
44
+ },
45
+ }
46
+
47
+ type Props = {
48
+ poolInfo: PoolInfoV2
49
+ isUniTranche: boolean
50
+ minDepositAmount: BigNumber
51
+ campaign?: Campaign
52
+ changeTranche: (tranche: TrancheType) => void
53
+ handleClose: () => void
54
+ }
55
+
56
+ export function PersonaEvaluation({
57
+ poolInfo,
58
+ isUniTranche,
59
+ minDepositAmount: minDepositAmountBN,
60
+ campaign,
61
+ changeTranche,
62
+ handleClose,
63
+ }: Props): React.ReactElement | null {
64
+ const theme = useTheme()
65
+ const isDev = checkIsDev()
66
+ const dispatch = useAppDispatch()
67
+ const { account, chainId } = useWeb3React()
68
+ const {
69
+ errorType,
70
+ setError: setAuthError,
71
+ isWalletOwnershipVerified,
72
+ isWalletOwnershipVerificationRequired,
73
+ } = useAuthErrorHandling(isDev)
74
+ const KYCCopies = poolInfo.KYC!.Persona!
75
+ const [loadingType, setLoadingType] = useState<LoadingType>()
76
+ const [kycCopy, setKYCCopy] = useState<KYCCopy>(KYCCopies.verifyIdentity)
77
+ const [inquiryId, setInquiryId] = useState<string>()
78
+ const [sessionToken, setSessionToken] = useState<string>()
79
+ const [verificationStatus, setVerificationStatus] =
80
+ useState<VerificationStatusResultV2>()
81
+ const [showPersonaClient, setShowPersonaClient] = useState<boolean>(false)
82
+ const [campaignBasePoints, setCampaignBasePoints] = useState<number>(0)
83
+ const isKYCCompletedRef = useRef<boolean>(false)
84
+ const isActionOngoingRef = useRef<boolean>(false)
85
+ const isKYCResumedRef = useRef<boolean>(false)
86
+
87
+ useEffect(() => {
88
+ const createNewWallet = async () => {
89
+ if (isWalletOwnershipVerified && campaign && account) {
90
+ await CampaignService.createNewWallet(
91
+ account,
92
+ localStorage.getItem(CAMPAIGN_REFERENCE_CODE) ?? undefined,
93
+ isDev,
94
+ )
95
+ }
96
+ }
97
+ createNewWallet()
98
+ }, [account, campaign, isDev, isWalletOwnershipVerified])
99
+
100
+ useEffect(() => {
101
+ const getBasePoints = async () => {
102
+ if (campaign) {
103
+ const estimatedPoints = await CampaignService.getEstimatedPoints(
104
+ campaign.campaignGroupId,
105
+ minDepositAmountBN.toString(),
106
+ isDev,
107
+ )
108
+ const campaignPoints = estimatedPoints.find(
109
+ (item) => item.campaignId === campaign.id,
110
+ )
111
+ if (campaignPoints) {
112
+ const { juniorTranchePoints, seniorTranchePoints } = campaignPoints
113
+ if (isUniTranche) {
114
+ setCampaignBasePoints(juniorTranchePoints ?? 0)
115
+ } else {
116
+ setCampaignBasePoints(
117
+ juniorTranchePoints < seniorTranchePoints
118
+ ? juniorTranchePoints
119
+ : seniorTranchePoints,
120
+ )
121
+ }
122
+ }
123
+ }
124
+ }
125
+ getBasePoints()
126
+ }, [campaign, isDev, isUniTranche, minDepositAmountBN])
127
+
128
+ const approveLender = useCallback(async () => {
129
+ try {
130
+ isActionOngoingRef.current = true
131
+ setLoadingType('approveLender')
132
+ await IdentityServiceV2.approveLender(
133
+ account!,
134
+ chainId!,
135
+ poolInfo.juniorTrancheVault,
136
+ isDev,
137
+ )
138
+ if (!isUniTranche) {
139
+ await IdentityServiceV2.approveLender(
140
+ account!,
141
+ chainId!,
142
+ poolInfo.seniorTrancheVault,
143
+ isDev,
144
+ )
145
+ }
146
+ if (isUniTranche) {
147
+ changeTranche('junior')
148
+ dispatch(setStep(WIDGET_STEP.ChooseAmount))
149
+ } else {
150
+ dispatch(setStep(WIDGET_STEP.ChooseTranche))
151
+ }
152
+ } finally {
153
+ isActionOngoingRef.current = false
154
+ setLoadingType(undefined)
155
+ }
156
+ }, [
157
+ account,
158
+ chainId,
159
+ changeTranche,
160
+ dispatch,
161
+ isDev,
162
+ isUniTranche,
163
+ poolInfo.juniorTrancheVault,
164
+ poolInfo.seniorTrancheVault,
165
+ ])
166
+
167
+ const checkVerificationStatus = useCallback(async () => {
168
+ if (isActionOngoingRef.current || isKYCResumedRef.current) {
169
+ return
170
+ }
171
+ try {
172
+ if (account && chainId) {
173
+ isActionOngoingRef.current = true
174
+ setLoadingType('verificationStatus')
175
+ const verificationStatus =
176
+ await IdentityServiceV2.getVerificationStatusV2(
177
+ account,
178
+ chainId,
179
+ isDev,
180
+ )
181
+ setVerificationStatus(verificationStatus)
182
+ setInquiryId(verificationStatus.personaInquiryId)
183
+
184
+ switch (verificationStatus.status) {
185
+ case IdentityVerificationStatusV2.NOT_STARTED: {
186
+ const startVerificationResult =
187
+ await IdentityServiceV2.startVerification(account, chainId, isDev)
188
+ setInquiryId(startVerificationResult.personaInquiryId)
189
+ setKYCCopy(KYCCopies.verifyIdentity)
190
+ setLoadingType(undefined)
191
+ break
192
+ }
193
+
194
+ case IdentityVerificationStatusV2.CREATED: {
195
+ setKYCCopy(KYCCopies.verifyIdentity)
196
+ setLoadingType(undefined)
197
+ break
198
+ }
199
+
200
+ case IdentityVerificationStatusV2.PENDING: {
201
+ if (!isKYCCompletedRef.current) {
202
+ const resumeVerificationResult =
203
+ await IdentityServiceV2.resumeVerification(
204
+ account,
205
+ chainId,
206
+ isDev,
207
+ )
208
+ verificationStatus.status = resumeVerificationResult.status
209
+ setVerificationStatus(verificationStatus)
210
+ setSessionToken(resumeVerificationResult.sessionToken)
211
+ setLoadingType(undefined)
212
+ isKYCResumedRef.current = true
213
+ } else {
214
+ setLoadingType('verificationStatus')
215
+ }
216
+ setKYCCopy(KYCCopies.verifyIdentity)
217
+ break
218
+ }
219
+
220
+ case IdentityVerificationStatusV2.EXPIRED: {
221
+ const resumeVerificationResult =
222
+ await IdentityServiceV2.resumeVerification(
223
+ account,
224
+ chainId,
225
+ isDev,
226
+ )
227
+ verificationStatus.status = resumeVerificationResult.status
228
+ setVerificationStatus(verificationStatus)
229
+ setSessionToken(resumeVerificationResult.sessionToken)
230
+ setKYCCopy(KYCCopies.verifyIdentity)
231
+ setLoadingType(undefined)
232
+ isKYCResumedRef.current = true
233
+ break
234
+ }
235
+
236
+ case IdentityVerificationStatusV2.APPROVED: {
237
+ await approveLender()
238
+ break
239
+ }
240
+
241
+ case IdentityVerificationStatusV2.DECLINED: {
242
+ setKYCCopy(KYCCopies.verificationDeclined)
243
+ setLoadingType(undefined)
244
+ break
245
+ }
246
+
247
+ case IdentityVerificationStatusV2.NEEDS_REVIEW: {
248
+ setKYCCopy(KYCCopies.verificationNeedsReview)
249
+ setLoadingType(undefined)
250
+ break
251
+ }
252
+
253
+ default:
254
+ break
255
+ }
256
+ }
257
+ } catch (e: unknown) {
258
+ setAuthError(e)
259
+ } finally {
260
+ isActionOngoingRef.current = false
261
+ }
262
+ }, [KYCCopies, account, approveLender, chainId, isDev, setAuthError])
263
+
264
+ useEffect(() => {
265
+ checkVerificationStatus()
266
+ }, [checkVerificationStatus])
267
+
268
+ useEffect(() => {
269
+ if (isWalletOwnershipVerificationRequired && isWalletOwnershipVerified) {
270
+ checkVerificationStatus()
271
+ }
272
+ }, [
273
+ checkVerificationStatus,
274
+ isWalletOwnershipVerificationRequired,
275
+ isWalletOwnershipVerified,
276
+ ])
277
+
278
+ useEffect(() => {
279
+ const interval = setInterval(() => {
280
+ if (
281
+ verificationStatus?.status &&
282
+ [
283
+ IdentityVerificationStatusV2.CREATED,
284
+ IdentityVerificationStatusV2.PENDING,
285
+ ].includes(verificationStatus.status)
286
+ ) {
287
+ checkVerificationStatus()
288
+ }
289
+ }, 10 * 1000)
290
+ // eslint-disable-next-line consistent-return
291
+ return () => clearInterval(interval)
292
+ }, [checkVerificationStatus, verificationStatus?.status])
293
+
294
+ useEffect(() => {
295
+ if (errorType === 'NotSignedIn') {
296
+ setLoadingType(undefined)
297
+ setKYCCopy(KYCCopies.signInRequired)
298
+ } else if (errorType === 'UserRejected') {
299
+ dispatch(
300
+ setError({
301
+ errorMessage: 'User has rejected the transaction.',
302
+ }),
303
+ )
304
+ } else if (errorType === 'Other') {
305
+ dispatch(
306
+ setError({
307
+ errorMessage: 'Something went wrong. Please try again later.',
308
+ }),
309
+ )
310
+ }
311
+ }, [KYCCopies.signInRequired, dispatch, errorType])
312
+
313
+ useEffect(() => {
314
+ const modalElement = document.getElementById('huma-modal')
315
+ if (modalElement) {
316
+ modalElement.style.visibility = showPersonaClient ? 'hidden' : 'visible'
317
+ }
318
+ }, [showPersonaClient])
319
+
320
+ const startKYC = async () => {
321
+ isActionOngoingRef.current = true
322
+ isKYCResumedRef.current = false
323
+ setLoadingType('startKYC')
324
+ const client: Client = new Persona.Client({
325
+ inquiryId,
326
+ sessionToken,
327
+ onReady: () => {
328
+ client.open()
329
+ setShowPersonaClient(true)
330
+ },
331
+ onComplete: () => {
332
+ isKYCCompletedRef.current = true
333
+ isActionOngoingRef.current = false
334
+ setShowPersonaClient(false)
335
+ checkVerificationStatus()
336
+ },
337
+ onCancel: () => {
338
+ isActionOngoingRef.current = false
339
+ setShowPersonaClient(false)
340
+ setLoadingType(undefined)
341
+ },
342
+ onError: () => {
343
+ isActionOngoingRef.current = false
344
+ setShowPersonaClient(false)
345
+ setLoadingType(undefined)
346
+ },
347
+ })
348
+ }
349
+
350
+ const handleAction = () => {
351
+ if (verificationStatus) {
352
+ switch (verificationStatus.status) {
353
+ case IdentityVerificationStatusV2.NOT_STARTED:
354
+ case IdentityVerificationStatusV2.CREATED:
355
+ case IdentityVerificationStatusV2.PENDING:
356
+ case IdentityVerificationStatusV2.EXPIRED:
357
+ startKYC()
358
+ break
359
+
360
+ case IdentityVerificationStatusV2.DECLINED:
361
+ case IdentityVerificationStatusV2.NEEDS_REVIEW:
362
+ handleClose()
363
+ break
364
+
365
+ default:
366
+ break
367
+ }
368
+ }
369
+ }
370
+
371
+ const styles = {
372
+ iconWrapper: css`
373
+ ${theme.cssMixins.rowCentered};
374
+ margin-top: ${theme.spacing(8)};
375
+ & > img {
376
+ width: 144px;
377
+ }
378
+ `,
379
+ description: css`
380
+ margin-top: ${theme.spacing(10)};
381
+ padding: ${theme.spacing(0, 2)};
382
+ font-weight: 400;
383
+ font-size: 16px;
384
+ color: ${theme.palette.text.primary};
385
+ `,
386
+ points: css`
387
+ color: ${theme.palette.primary.main};
388
+ font-weight: 700;
389
+ `,
390
+ }
391
+
392
+ if (!loadingType) {
393
+ return (
394
+ <WrapperModal title={kycCopy.title}>
395
+ <Box css={styles.iconWrapper}>
396
+ <img src={ApproveLenderImg} alt='approve-lender' />
397
+ </Box>
398
+ {campaign && kycCopy === KYCCopies.verifyIdentity ? (
399
+ <Box css={styles.description}>
400
+ <span>You'll be rewarded with a minimum of</span>
401
+ <span css={styles.points}>
402
+ {' '}
403
+ {formatNumber(campaignBasePoints)} Huma points{' '}
404
+ </span>
405
+ <span>after completing KYC and your first investment.</span>
406
+ </Box>
407
+ ) : (
408
+ <Box css={styles.description}>{kycCopy.description}</Box>
409
+ )}
410
+
411
+ {Boolean(kycCopy.buttonText) && (
412
+ <BottomButton variant='contained' onClick={handleAction}>
413
+ {kycCopy.buttonText}
414
+ </BottomButton>
415
+ )}
416
+ </WrapperModal>
417
+ )
418
+ }
419
+
420
+ return (
421
+ <LoadingModal
422
+ title='Lender Approval'
423
+ description={LoadingCopiesByType[loadingType].description}
424
+ />
425
+ )
426
+ }