@huma-finance/widgets 0.0.59-beta.494 → 0.0.59-beta.495

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