@huma-finance/widgets 0.0.34 → 0.0.36

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 (181) hide show
  1. package/.env.example +2 -2
  2. package/.eslintignore +2 -2
  3. package/.eslintrc.js +64 -64
  4. package/.prettierignore +4 -4
  5. package/.prettierrc.js +16 -16
  6. package/API.md +332 -332
  7. package/LICENSE +660 -660
  8. package/README.md +48 -48
  9. package/babel.config.js +15 -15
  10. package/config-overrides.js +9 -9
  11. package/cosmos.config.json +7 -7
  12. package/cypress/e2e/01-invoice-factoring-borrow.test.ts +62 -62
  13. package/cypress/support/e2e.ts +76 -76
  14. package/cypress/support/ethereum.ts +142 -142
  15. package/cypress/tsconfig.json +9 -9
  16. package/cypress/utils/index.ts +54 -54
  17. package/cypress.config.ts +20 -20
  18. package/dist/cjs/index.cjs +1939 -1866
  19. package/dist/cjs/index.cjs.map +1 -1
  20. package/dist/index.d.ts +227 -195
  21. package/dist/index.js +1942 -1870
  22. package/dist/index.js.map +1 -1
  23. package/jsdoc2md.json +20 -20
  24. package/package.json +5 -4
  25. package/public/index.html +40 -40
  26. package/public/manifest.json +25 -25
  27. package/public/robots.txt +3 -3
  28. package/rollup.config.js +81 -81
  29. package/src/components/Activity.tsx +128 -132
  30. package/src/components/ApolloWrapper.tsx +15 -15
  31. package/src/components/ApproveAllowanceModal.tsx +138 -121
  32. package/src/components/BottomButton.tsx +33 -33
  33. package/src/components/ChainNotSupportedModal.tsx +33 -33
  34. package/src/components/ChooseAmountModal.tsx +322 -322
  35. package/src/components/ConfirmTransferModal.tsx +153 -153
  36. package/src/components/CreditLine/approve/1-ApproveAllowance.tsx +34 -0
  37. package/src/components/CreditLine/approve/2-Success.tsx +43 -0
  38. package/src/components/CreditLine/approve/index.tsx +85 -0
  39. package/src/components/CreditLine/borrow/1-Evaluation.tsx +157 -157
  40. package/src/components/CreditLine/borrow/2-ChooseAmount.tsx +74 -68
  41. package/src/components/CreditLine/borrow/{4-ApproveAllowance.tsx → 3-ApproveAllowance.tsx} +29 -29
  42. package/src/components/CreditLine/borrow/{3-Transfer.tsx → 4-Transfer.tsx} +34 -44
  43. package/src/components/CreditLine/borrow/5-Success.tsx +38 -38
  44. package/src/components/CreditLine/borrow/6-Notifications.tsx +10 -10
  45. package/src/components/CreditLine/borrow/index.tsx +136 -136
  46. package/src/components/CreditLine/payment/1-ChooseAmount.tsx +67 -67
  47. package/src/components/CreditLine/payment/2-ApproveAllowance.tsx +23 -23
  48. package/src/components/CreditLine/payment/3-Transfer.tsx +34 -34
  49. package/src/components/CreditLine/payment/4-Success.tsx +39 -39
  50. package/src/components/CreditLine/payment/index.tsx +94 -94
  51. package/src/components/ErrorModal.tsx +136 -132
  52. package/src/components/Factoring/FactoredList.tsx +143 -143
  53. package/src/components/Factoring/UpcomingList.tsx +90 -90
  54. package/src/components/Factoring/index.ts +2 -2
  55. package/src/components/HumaLoading.tsx +68 -68
  56. package/src/components/HumaSnackBar.tsx +60 -60
  57. package/src/components/HumaTable.tsx +203 -158
  58. package/src/components/InvoiceFactoring/borrow/1-Evaluation.tsx +49 -49
  59. package/src/components/InvoiceFactoring/borrow/2-ChooseAmount.tsx +88 -88
  60. package/src/components/InvoiceFactoring/borrow/3-ConfirmTransfer.tsx +37 -37
  61. package/src/components/InvoiceFactoring/borrow/4-ApproveNFT.tsx +55 -55
  62. package/src/components/InvoiceFactoring/borrow/5-Transfer.tsx +34 -34
  63. package/src/components/InvoiceFactoring/borrow/6-Success.tsx +20 -20
  64. package/src/components/InvoiceFactoring/borrow/index.tsx +94 -94
  65. package/src/components/InvoiceFactoring/payment/1-ChooseAmount.tsx +67 -67
  66. package/src/components/InvoiceFactoring/payment/2-ApproveAllowance.tsx +23 -23
  67. package/src/components/InvoiceFactoring/payment/3-Transfer.tsx +34 -34
  68. package/src/components/InvoiceFactoring/payment/4-Success.tsx +23 -23
  69. package/src/components/InvoiceFactoring/payment/index.tsx +86 -86
  70. package/src/components/Lend/supply/1-Evaluation/EvaluationEA.tsx +90 -86
  71. package/src/components/Lend/supply/1-Evaluation/EvaluationKYC.tsx +319 -287
  72. package/src/components/Lend/supply/1-Evaluation/index.tsx +31 -31
  73. package/src/components/Lend/supply/2-ChooseAmount.tsx +69 -69
  74. package/src/components/Lend/supply/3-ApproveAllowance.tsx +25 -25
  75. package/src/components/Lend/supply/4-Transfer.tsx +32 -32
  76. package/src/components/Lend/supply/5-Success.tsx +60 -60
  77. package/src/components/Lend/supply/6-Notifications.tsx +10 -10
  78. package/src/components/Lend/supply/index.tsx +167 -167
  79. package/src/components/Lend/withdraw/1-CheckWithdrawble.tsx +73 -73
  80. package/src/components/Lend/withdraw/2-ChooseAmount.tsx +50 -50
  81. package/src/components/Lend/withdraw/3-Transfer.tsx +32 -32
  82. package/src/components/Lend/withdraw/4-Success.tsx +49 -49
  83. package/src/components/Lend/withdraw/index.tsx +101 -101
  84. package/src/components/LoadingModal.tsx +49 -49
  85. package/src/components/Notifi/NotifiSubscriptionModal.tsx +268 -264
  86. package/src/components/StreamFactoring/borrow/1-Evaluation.tsx +55 -55
  87. package/src/components/StreamFactoring/borrow/2-ChooseAmount.tsx +127 -127
  88. package/src/components/StreamFactoring/borrow/3-ConfirmTransfer.tsx +42 -42
  89. package/src/components/StreamFactoring/borrow/4-ApproveAllowance.tsx +33 -33
  90. package/src/components/StreamFactoring/borrow/5-Permit.tsx +144 -144
  91. package/src/components/StreamFactoring/borrow/6-Transfer.tsx +37 -37
  92. package/src/components/StreamFactoring/borrow/7-Success.tsx +20 -20
  93. package/src/components/StreamFactoring/borrow/index.tsx +94 -94
  94. package/src/components/StreamFactoring/payment/ChooseAmount.tsx +54 -54
  95. package/src/components/StreamFactoring/payment/Success.tsx +23 -23
  96. package/src/components/StreamFactoring/payment/Transfer.tsx +34 -34
  97. package/src/components/StreamFactoring/payment/index.tsx +66 -66
  98. package/src/components/SuperfluidFactoring/SuperfluidFactoredList.tsx +69 -70
  99. package/src/components/SuperfluidFactoring/SuperfluidUpcomingList.tsx +166 -167
  100. package/src/components/SuperfluidFactoring/index.tsx +85 -85
  101. package/src/components/TxDoneModal.tsx +85 -85
  102. package/src/components/TxSendModal.tsx +59 -59
  103. package/src/components/ViewOnExplorer.tsx +23 -26
  104. package/src/components/WidgetWrapper.tsx +74 -74
  105. package/src/components/WrapperModal.tsx +54 -54
  106. package/src/components/buttons/TableActionButton.tsx +36 -36
  107. package/src/components/buttons/index.ts +1 -1
  108. package/src/components/humaModal/HumaModal.tsx +68 -68
  109. package/src/components/humaModal/HumaModalHeader.tsx +70 -70
  110. package/src/components/humaModal/index.tsx +2 -2
  111. package/src/components/icons/approve-lender.svg +58 -30
  112. package/src/components/icons/auto-payback.svg +139 -40
  113. package/src/components/icons/check-circle.svg +3 -3
  114. package/src/components/icons/check-green.svg +3 -3
  115. package/src/components/icons/check.svg +10 -10
  116. package/src/components/icons/donut.svg +904 -904
  117. package/src/components/icons/ellipse.svg +3 -3
  118. package/src/components/icons/huma-full.svg +22 -22
  119. package/src/components/icons/huma.svg +15 -15
  120. package/src/components/icons/index.tsx +93 -93
  121. package/src/components/icons/loader.svg +229 -229
  122. package/src/components/icons/metamask.svg +31 -31
  123. package/src/components/icons/polygon.svg +16 -16
  124. package/src/components/icons/receipt.svg +3 -3
  125. package/src/components/icons/separator.svg +3 -3
  126. package/src/components/icons/sorry.svg +17 -31
  127. package/src/components/icons/usdc.svg +5 -5
  128. package/src/components/icons/wallet-connect.svg +11 -11
  129. package/src/components/images/approve-lender.png +0 -0
  130. package/src/components/images/auto-payback.png +0 -0
  131. package/src/components/images/index.ts +3 -0
  132. package/src/components/images/sorry.png +0 -0
  133. package/src/cosmos/CreditLineBorrow.fixture.tsx +86 -86
  134. package/src/cosmos/CreditLinePayment.fixture.tsx +50 -50
  135. package/src/cosmos/InvoiceFactoringBorrow.fixture.tsx +111 -111
  136. package/src/cosmos/InvoiceFactoringPayment.fixture.tsx +67 -67
  137. package/src/cosmos/LendSupply.fixture.tsx +75 -75
  138. package/src/cosmos/LendWithdraw.fixture.tsx +43 -43
  139. package/src/cosmos/SDKTesting.fixture.tsx +616 -616
  140. package/src/cosmos/SuperfluidFactoringBorrow.fixture.tsx +15 -15
  141. package/src/cosmos/WalletWrapper.tsx +52 -52
  142. package/src/cosmos/abis/InvoiceNFT.json +644 -644
  143. package/src/cosmos/abis/ReceivableFactoringPool.json +1529 -1529
  144. package/src/cosmos/utils/env.ts +6 -6
  145. package/src/cosmos/utils/index.ts +169 -169
  146. package/src/fonts/Recoleta/Recoleta-Black.otf +0 -0
  147. package/src/fonts/Recoleta/Recoleta-Bold.otf +0 -0
  148. package/src/fonts/Recoleta/Recoleta-Light.otf +0 -0
  149. package/src/fonts/Recoleta/Recoleta-Medium.otf +0 -0
  150. package/src/fonts/Recoleta/Recoleta-Regular.otf +0 -0
  151. package/src/fonts/Recoleta/Recoleta-RegularDEMO.otf +0 -0
  152. package/src/fonts/Recoleta/Recoleta-SemiBold.otf +0 -0
  153. package/src/fonts/Recoleta/Recoleta-Thin.otf +0 -0
  154. package/src/hooks/useEA.ts +53 -53
  155. package/src/hooks/useElementDimensions.ts +30 -0
  156. package/src/hooks/useNotifi.ts +71 -71
  157. package/src/hooks/useRedux.ts +6 -6
  158. package/src/index.css +61 -61
  159. package/src/index.tsx +283 -301
  160. package/src/react-app-env.d.ts +1 -1
  161. package/src/reportWebVitals.ts +15 -15
  162. package/src/setupTests.ts +6 -6
  163. package/src/store/index.ts +27 -27
  164. package/src/store/widgets.reducers.ts +120 -120
  165. package/src/store/widgets.selectors.ts +3 -3
  166. package/src/store/widgets.store.ts +55 -55
  167. package/src/theme/breakpoints.ts +10 -10
  168. package/src/theme/components.ts +70 -70
  169. package/src/theme/coreThemeConstants.ts +29 -29
  170. package/src/theme/cssMixins.ts +92 -92
  171. package/src/theme/hooks/useGetSizes.ts +31 -31
  172. package/src/theme/index.ts +24 -24
  173. package/src/theme/palette.ts +150 -150
  174. package/src/theme/shape.ts +7 -7
  175. package/src/theme/theme.types.ts +80 -80
  176. package/src/theme/typography.ts +117 -117
  177. package/src/utilTypes.d.ts +30 -31
  178. package/src/utils/env.ts +10 -10
  179. package/tsconfig.build.json +7 -7
  180. package/tsconfig.json +32 -32
  181. package/webpack.cosmos.js +54 -54
@@ -1,287 +1,319 @@
1
- import {
2
- configUtil,
3
- DocSignatureStatus,
4
- IdentityService,
5
- PoolInfoType,
6
- timeUtil,
7
- VerificationStatusResult,
8
- } from '@huma-finance/shared'
9
- import { Box, css, useTheme } from '@mui/material'
10
- import { useWeb3React } from '@web3-react/core'
11
- import React, { useCallback, useEffect, useMemo, useState } from 'react'
12
-
13
- import { useAppDispatch } from '../../../../hooks/useRedux'
14
- import { setError } from '../../../../store/widgets.reducers'
15
- import { envUtil } from '../../../../utils/env'
16
- import { BottomButton } from '../../../BottomButton'
17
- import { HumaSnackBar } from '../../../HumaSnackBar'
18
- import { ApproveLenderIcon } from '../../../icons'
19
- import { LoadingModal } from '../../../LoadingModal'
20
- import { WrapperModal } from '../../../WrapperModal'
21
-
22
- const KYCProvider = 'Securitize'
23
-
24
- const KYCCopiesByState: {
25
- [key: string]: {
26
- description: string
27
- buttonText: string
28
- }
29
- } = {
30
- verifyIdentity: {
31
- description: `Link your ${KYCProvider} account or create a new one to complete identity verification.`,
32
- buttonText: 'VERIFY MY IDENTITY',
33
- },
34
- emailSignatureLink: {
35
- description: `Final step: Your digital signature on legal documents is required. Click below to email yourself the DocuSign link.`,
36
- buttonText: 'EMAIL DOCUSIGN LINK',
37
- },
38
- resendSignatureLink: {
39
- description: `DocuSign link sent successfully. Haven't received it? Check your spam folder or click below to resend it.`,
40
- buttonText: 'RESEND DOCUSIGN LINK',
41
- },
42
- docUnderReview: {
43
- description: `Documents received and under review. You'll be notified upon approval. Your patience is appreciated.`,
44
- buttonText: 'THANK YOU',
45
- },
46
- }
47
-
48
- const LoadingCopiesByType: {
49
- [key: string]: {
50
- description: string
51
- }
52
- } = {
53
- verificationStatus: {
54
- description: `Checking your verification status...`,
55
- },
56
- sendDocSignatureLink: {
57
- description: `Sending signature link...`,
58
- },
59
- }
60
-
61
- type Props = {
62
- poolInfo: PoolInfoType
63
- handleClose: () => void
64
- }
65
-
66
- export function EvaluationKYC({
67
- poolInfo,
68
- handleClose,
69
- }: Props): React.ReactElement | null {
70
- const theme = useTheme()
71
- const dispatch = useAppDispatch()
72
- const isDev = envUtil.checkIsDev()
73
- const { account, chainId } = useWeb3React()
74
- const [loadingType, setLoadingType] = useState<
75
- 'verificationStatus' | 'sendDocSignatureLink'
76
- >()
77
- const [description, setDescription] = useState<string>(
78
- KYCCopiesByState.verifyIdentity.description,
79
- )
80
- const [buttonText, setButtonText] = useState<string>(
81
- KYCCopiesByState.verifyIdentity.buttonText,
82
- )
83
- const [KYCVerifyStatus, setKYCVerifyStatus] =
84
- useState<VerificationStatusResult>()
85
- const [docSignatureStatus, setDocSignatureStatus] =
86
- useState<DocSignatureStatus['status']>()
87
- const docSignatureCompleted = docSignatureStatus === 'completed'
88
- const [openSnackBar, setOpenSnackBar] = useState<boolean>(false)
89
-
90
- const styles = {
91
- iconWrapper: css`
92
- ${theme.cssMixins.rowCentered};
93
- margin-top: 30px;
94
- `,
95
- description: css`
96
- ${theme.cssMixins.rowCentered};
97
- margin-top: 50px;
98
- font-family: 'Uni-Neue-Regular';
99
- font-size: 16px;
100
- color: #a8a1b2;
101
- `,
102
- }
103
-
104
- const { envelopeKey, envelopeLastQueryTimeKey } = useMemo(() => {
105
- const envelopeKey = `${poolInfo.pool.toLowerCase()}-${account?.toLowerCase()}`
106
- const envelopeLastQueryTimeKey = `${envelopeKey}-last-query-time`
107
- return {
108
- envelopeKey,
109
- envelopeLastQueryTimeKey,
110
- }
111
- }, [account, poolInfo.pool])
112
-
113
- // DocuSign GET requests to any specific URL can be called not more often than once every 15 minutes.
114
- // https://developers.docusign.com/platform/api-guidelines/#:~:text=Polling,not%20once%20every%2010%20minutes
115
- const checkGetDocSignatureStatusIsAvailable = useCallback(() => {
116
- const lastQueryTime = localStorage.getItem(envelopeLastQueryTimeKey)
117
- if (!lastQueryTime) {
118
- return true
119
- }
120
- const lastQueryTimeNumber = Number(lastQueryTime)
121
- const now = timeUtil.getUnixTimestamp()
122
- const diff = now - lastQueryTimeNumber
123
- const fifteenMinutes = 15 * 60
124
- return diff > fifteenMinutes
125
- }, [envelopeLastQueryTimeKey])
126
-
127
- useEffect(() => {
128
- const fetchData = async () => {
129
- if (account && chainId) {
130
- try {
131
- setLoadingType('verificationStatus')
132
- const verificationStatus =
133
- await IdentityService.getVerificationStatus(
134
- account,
135
- poolInfo.pool,
136
- chainId,
137
- isDev,
138
- )
139
- setKYCVerifyStatus(verificationStatus)
140
- if (verificationStatus.isVerified) {
141
- const envelopeId = localStorage.getItem(envelopeKey)
142
- if (!envelopeId) {
143
- setDescription(KYCCopiesByState.emailSignatureLink.description)
144
- setButtonText(KYCCopiesByState.emailSignatureLink.buttonText)
145
- } else if (!checkGetDocSignatureStatusIsAvailable()) {
146
- setDescription(KYCCopiesByState.resendSignatureLink.description)
147
- setButtonText(KYCCopiesByState.resendSignatureLink.buttonText)
148
- } else {
149
- const { status } = await IdentityService.getDocSignatureStatus(
150
- envelopeId,
151
- chainId,
152
- isDev,
153
- )
154
- localStorage.setItem(
155
- envelopeLastQueryTimeKey,
156
- String(timeUtil.getUnixTimestamp()),
157
- )
158
- setDocSignatureStatus(status)
159
- if (status === 'completed') {
160
- setDescription(KYCCopiesByState.docUnderReview.description)
161
- setButtonText(KYCCopiesByState.docUnderReview.buttonText)
162
- // For voided and declined status, we need to send a new link
163
- } else if (['voided', 'declined'].includes(status)) {
164
- localStorage.removeItem(envelopeKey)
165
- localStorage.removeItem(envelopeLastQueryTimeKey)
166
- setDescription(KYCCopiesByState.emailSignatureLink.description)
167
- setButtonText(KYCCopiesByState.emailSignatureLink.buttonText)
168
- } else {
169
- setDescription(KYCCopiesByState.resendSignatureLink.description)
170
- setButtonText(KYCCopiesByState.resendSignatureLink.buttonText)
171
- }
172
- }
173
- }
174
- } catch (e: unknown) {
175
- console.error(e)
176
- dispatch(
177
- setError({
178
- errorMessage: 'Something went wrong, please try again later.',
179
- }),
180
- )
181
- } finally {
182
- setLoadingType(undefined)
183
- }
184
- }
185
- }
186
- fetchData()
187
- }, [
188
- account,
189
- chainId,
190
- checkGetDocSignatureStatusIsAvailable,
191
- dispatch,
192
- envelopeKey,
193
- envelopeLastQueryTimeKey,
194
- isDev,
195
- poolInfo.pool,
196
- ])
197
-
198
- const approveLender = async () => {
199
- if (docSignatureCompleted) {
200
- handleClose()
201
- return
202
- }
203
-
204
- const { isNotOnboarded, isVerified } = KYCVerifyStatus || {}
205
-
206
- if (!isVerified) {
207
- const issuerId = '53a66b32-583e-40e7-ba90-baf516d2cadd'
208
- const baseUrl = configUtil.getKYCProviderBaseUrl(KYCProvider, isDev)
209
- const originUrl = window.location.href.split('?')[0]
210
- const redirectUrl = `${originUrl}?kycProvider=${KYCProvider}&kycPool=${poolInfo.pool}`
211
- const providerAuthorizeUrl = `${baseUrl}/#/authorize?issuerId=${issuerId}&scope=details&details=verification&redirectUrl=${redirectUrl}`
212
- const providerUrl = isNotOnboarded ? providerAuthorizeUrl : baseUrl
213
- window.location.href = providerUrl
214
- } else {
215
- const envelopeId = localStorage.getItem(envelopeKey)
216
- try {
217
- setLoadingType('sendDocSignatureLink')
218
- if (!envelopeId) {
219
- const { envelopeId } = await IdentityService.requestDocSignature(
220
- account!,
221
- chainId!,
222
- isDev,
223
- )
224
- localStorage.setItem(envelopeKey, envelopeId)
225
- localStorage.setItem(
226
- envelopeLastQueryTimeKey,
227
- String(timeUtil.getUnixTimestamp()),
228
- )
229
- setOpenSnackBar(true)
230
- setDescription(KYCCopiesByState.resendSignatureLink.description)
231
- setButtonText(KYCCopiesByState.resendSignatureLink.buttonText)
232
- } else {
233
- await IdentityService.resendDocSignatureLink(
234
- envelopeId,
235
- chainId!,
236
- isDev,
237
- )
238
- setOpenSnackBar(true)
239
- }
240
- } catch (e: unknown) {
241
- console.error(e)
242
- dispatch(
243
- setError({
244
- errorMessage: 'Something went wrong, please try again later.',
245
- }),
246
- )
247
- } finally {
248
- setLoadingType(undefined)
249
- }
250
- }
251
- }
252
-
253
- const getEmailLinkSentSnackbar = () => (
254
- <HumaSnackBar
255
- open={openSnackBar}
256
- title='Signature Link Sent'
257
- message='The secure signature session link has been sent to your email.'
258
- severity='success'
259
- onClose={() => setOpenSnackBar(false)}
260
- />
261
- )
262
-
263
- if (!loadingType) {
264
- return (
265
- <WrapperModal
266
- title='Lender Approval'
267
- subTitle='This pool requires lender approval'
268
- >
269
- <Box css={styles.iconWrapper}>
270
- <ApproveLenderIcon />
271
- </Box>
272
- <Box css={styles.description}>{description}</Box>
273
- <BottomButton variant='contained' onClick={approveLender}>
274
- {buttonText}
275
- </BottomButton>
276
- {getEmailLinkSentSnackbar()}
277
- </WrapperModal>
278
- )
279
- }
280
-
281
- return (
282
- <LoadingModal
283
- title='Lender Approval'
284
- description={LoadingCopiesByType[loadingType].description}
285
- />
286
- )
287
- }
1
+ import {
2
+ configUtil,
3
+ DocSignatureStatus,
4
+ IdentityService,
5
+ PoolInfoType,
6
+ timeUtil,
7
+ VerificationStatusResult,
8
+ CHAINS,
9
+ useParamsSearch,
10
+ } from '@huma-finance/shared'
11
+ import { Box, css, useTheme } from '@mui/material'
12
+ import { useWeb3React } from '@web3-react/core'
13
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
14
+
15
+ import { useAppDispatch } from '../../../../hooks/useRedux'
16
+ import { setError } from '../../../../store/widgets.reducers'
17
+ import { envUtil } from '../../../../utils/env'
18
+ import { BottomButton } from '../../../BottomButton'
19
+ import { HumaSnackBar } from '../../../HumaSnackBar'
20
+ import { ApproveLenderImg } from '../../../images'
21
+ import { LoadingModal } from '../../../LoadingModal'
22
+ import { WrapperModal } from '../../../WrapperModal'
23
+
24
+ const KYCProvider = 'Securitize'
25
+
26
+ type KYCCopy = {
27
+ title: string
28
+ description: string
29
+ buttonText: string
30
+ }
31
+
32
+ // TODO: Add copies for the other pools. Currently only the Jia pool is using the KYC process.
33
+ const JiaPoolCopies = {
34
+ verifyIdentity: {
35
+ title: 'Verify Identity',
36
+ description: `This pool is only available to accredited investors at the moment, with minimum investments of $10,000. Please complete identity verification and investor accreditation via ${KYCProvider}.`,
37
+ buttonText: 'VERIFY MY IDENTITY',
38
+ },
39
+ emailSignatureLink: {
40
+ title: 'Pool Documents',
41
+ description: `By lending to this pool, you become a subscriber member of the Jia Pioneer Fund LLC. Please sign the LLC Agreement via DocuSign, securing your off-chain claim to the Fund's returns and collateral.`,
42
+ buttonText: 'EMAIL DOCUSIGN LINK',
43
+ },
44
+ resendSignatureLink: {
45
+ title: 'Resend Documents',
46
+ description: `Please check your inbox for the LLC Agreement sent via DocuSign. If you haven't received it, check your spam folder or click below to resend.`,
47
+ buttonText: 'RESEND DOCUSIGN LINK',
48
+ },
49
+ docUnderReview: {
50
+ title: 'Under Review',
51
+ description:
52
+ 'Your documents are being reviewed and you will be notified upon approval. Thank you for your patience. Any questions? Email invest@jia.xyz.',
53
+ buttonText: 'THANK YOU',
54
+ },
55
+ }
56
+
57
+ const LoadingCopiesByType: {
58
+ [key: string]: {
59
+ description: string
60
+ }
61
+ } = {
62
+ verificationStatus: {
63
+ description: `Checking your verification status...`,
64
+ },
65
+ sendDocSignatureLink: {
66
+ description: `Sending signature link...`,
67
+ },
68
+ }
69
+
70
+ type Props = {
71
+ poolInfo: PoolInfoType
72
+ handleClose: () => void
73
+ }
74
+
75
+ export function EvaluationKYC({
76
+ poolInfo,
77
+ handleClose,
78
+ }: Props): React.ReactElement | null {
79
+ const theme = useTheme()
80
+ const dispatch = useAppDispatch()
81
+ const isDev = envUtil.checkIsDev()
82
+ const { account, chainId } = useWeb3React()
83
+ const { kycProvider, code, kycPool } = useParamsSearch()
84
+ const [loadingType, setLoadingType] = useState<
85
+ 'verificationStatus' | 'sendDocSignatureLink'
86
+ >()
87
+ const [kycCopy, setKYCCopy] = useState<KYCCopy>(JiaPoolCopies.verifyIdentity)
88
+ const [KYCVerifyStatus, setKYCVerifyStatus] =
89
+ useState<VerificationStatusResult>()
90
+ const [docSignatureStatus, setDocSignatureStatus] =
91
+ useState<DocSignatureStatus['status']>()
92
+ const docSignatureCompleted = docSignatureStatus === 'completed'
93
+ const [openSnackBar, setOpenSnackBar] = useState<boolean>(false)
94
+
95
+ const styles = {
96
+ iconWrapper: css`
97
+ ${theme.cssMixins.rowCentered};
98
+ margin-top: 65px;
99
+ & > img {
100
+ width: 144px;
101
+ }
102
+ `,
103
+ description: css`
104
+ ${theme.cssMixins.rowCentered};
105
+ margin-top: 82px;
106
+ padding: 0 20px;
107
+ font-family: 'Uni-Neue-Regular';
108
+ font-size: 16px;
109
+ color: #a8a1b2;
110
+ `,
111
+ }
112
+
113
+ const { envelopeKey, envelopeLastQueryTimeKey, envelopeDocuSignStatusKey } =
114
+ useMemo(() => {
115
+ const envelopeKey = `${poolInfo.pool.toLowerCase()}-${account?.toLowerCase()}`
116
+ const envelopeLastQueryTimeKey = `${envelopeKey}-last-query-time`
117
+ const envelopeDocuSignStatusKey = `${envelopeKey}-docuSign-status`
118
+ return {
119
+ envelopeKey,
120
+ envelopeLastQueryTimeKey,
121
+ envelopeDocuSignStatusKey,
122
+ }
123
+ }, [account, poolInfo.pool])
124
+
125
+ // DocuSign GET requests to any specific URL can be called not more often than once every 15 minutes.
126
+ // https://developers.docusign.com/platform/api-guidelines/#:~:text=Polling,not%20once%20every%2010%20minutes
127
+ const checkGetDocSignatureStatusIsAvailable = useCallback(() => {
128
+ const lastQueryTime = localStorage.getItem(envelopeLastQueryTimeKey)
129
+ if (!lastQueryTime) {
130
+ return true
131
+ }
132
+ const lastQueryTimeNumber = Number(lastQueryTime)
133
+ const now = timeUtil.getUnixTimestamp()
134
+ const diff = now - lastQueryTimeNumber
135
+ const fifteenMinutes = 15 * 60
136
+ return diff > fifteenMinutes
137
+ }, [envelopeLastQueryTimeKey])
138
+
139
+ useEffect(() => {
140
+ const docuSignStatus = localStorage.getItem(envelopeDocuSignStatusKey)
141
+ setDocSignatureStatus(docuSignStatus as DocSignatureStatus['status'])
142
+ if (docuSignStatus === 'completed') {
143
+ setKYCCopy(JiaPoolCopies.docUnderReview)
144
+ return
145
+ }
146
+
147
+ const fetchData = async () => {
148
+ try {
149
+ if (kycProvider && kycPool && account && chainId) {
150
+ setLoadingType('verificationStatus')
151
+ await IdentityService.onboard(
152
+ account,
153
+ code as string,
154
+ kycPool as string,
155
+ chainId,
156
+ isDev,
157
+ )
158
+ }
159
+ } catch (e) {
160
+ // The repeated call will throw an error of 401, so we can ignore it.
161
+ console.error(e)
162
+ }
163
+
164
+ try {
165
+ if (account && chainId) {
166
+ setLoadingType('verificationStatus')
167
+ const verificationStatus =
168
+ await IdentityService.getVerificationStatus(
169
+ account,
170
+ poolInfo.pool,
171
+ chainId,
172
+ isDev,
173
+ )
174
+ setKYCVerifyStatus(verificationStatus)
175
+ if (verificationStatus.isVerified) {
176
+ const envelopeId = localStorage.getItem(envelopeKey)
177
+ if (!envelopeId) {
178
+ setKYCCopy(JiaPoolCopies.emailSignatureLink)
179
+ } else if (!checkGetDocSignatureStatusIsAvailable()) {
180
+ setKYCCopy(JiaPoolCopies.resendSignatureLink)
181
+ } else {
182
+ const { status } = await IdentityService.getDocSignatureStatus(
183
+ envelopeId,
184
+ chainId,
185
+ isDev,
186
+ )
187
+ localStorage.setItem(
188
+ envelopeLastQueryTimeKey,
189
+ String(timeUtil.getUnixTimestamp()),
190
+ )
191
+ localStorage.setItem(envelopeDocuSignStatusKey, status)
192
+ setDocSignatureStatus(status)
193
+ if (status === 'completed') {
194
+ setKYCCopy(JiaPoolCopies.docUnderReview)
195
+ // For voided and declined status, we need to send a new link
196
+ } else if (['voided', 'declined'].includes(status)) {
197
+ localStorage.removeItem(envelopeKey)
198
+ localStorage.removeItem(envelopeLastQueryTimeKey)
199
+ setKYCCopy(JiaPoolCopies.emailSignatureLink)
200
+ } else {
201
+ setKYCCopy(JiaPoolCopies.resendSignatureLink)
202
+ }
203
+ }
204
+ }
205
+ }
206
+ } catch (e: unknown) {
207
+ console.error(e)
208
+ dispatch(
209
+ setError({
210
+ errorMessage: 'Something went wrong, please try again later.',
211
+ }),
212
+ )
213
+ } finally {
214
+ setLoadingType(undefined)
215
+ }
216
+ }
217
+ fetchData()
218
+ }, [
219
+ account,
220
+ chainId,
221
+ checkGetDocSignatureStatusIsAvailable,
222
+ code,
223
+ dispatch,
224
+ envelopeDocuSignStatusKey,
225
+ envelopeKey,
226
+ envelopeLastQueryTimeKey,
227
+ isDev,
228
+ kycPool,
229
+ kycProvider,
230
+ poolInfo.pool,
231
+ ])
232
+
233
+ const approveLender = async () => {
234
+ if (docSignatureCompleted) {
235
+ handleClose()
236
+ return
237
+ }
238
+
239
+ const { isNotOnboarded, isVerified } = KYCVerifyStatus || {}
240
+
241
+ if (!isVerified) {
242
+ const issuerId = CHAINS[chainId!].isTestnet
243
+ ? '53a66b32-583e-40e7-ba90-baf516d2cadd'
244
+ : '5557baf5-d3c2-4c80-b522-c05a11c6e586'
245
+ const baseUrl = configUtil.getKYCProviderBaseUrl(KYCProvider, chainId!)
246
+ const originUrl = window.location.href.split('?')[0]
247
+ const redirectUrl = `${originUrl}?poolName=${poolInfo.poolName}&kycProvider=${KYCProvider}&kycPool=${poolInfo.pool}`
248
+ const providerAuthorizeUrl = `${baseUrl}/#/authorize?issuerId=${issuerId}&scope=details&details=verification&redirectUrl=${redirectUrl}`
249
+ window.location.href = isNotOnboarded ? providerAuthorizeUrl : baseUrl
250
+ } else {
251
+ const envelopeId = localStorage.getItem(envelopeKey)
252
+ try {
253
+ setLoadingType('sendDocSignatureLink')
254
+ if (!envelopeId) {
255
+ const { envelopeId } = await IdentityService.requestDocSignature(
256
+ account!,
257
+ chainId!,
258
+ isDev,
259
+ )
260
+ localStorage.setItem(envelopeKey, envelopeId)
261
+ localStorage.setItem(
262
+ envelopeLastQueryTimeKey,
263
+ String(timeUtil.getUnixTimestamp()),
264
+ )
265
+ setOpenSnackBar(true)
266
+ setKYCCopy(JiaPoolCopies.resendSignatureLink)
267
+ } else {
268
+ await IdentityService.resendDocSignatureLink(
269
+ envelopeId,
270
+ chainId!,
271
+ isDev,
272
+ )
273
+ setOpenSnackBar(true)
274
+ }
275
+ } catch (e: unknown) {
276
+ console.error(e)
277
+ dispatch(
278
+ setError({
279
+ errorMessage: 'Something went wrong, please try again later.',
280
+ }),
281
+ )
282
+ } finally {
283
+ setLoadingType(undefined)
284
+ }
285
+ }
286
+ }
287
+
288
+ const getEmailLinkSentSnackbar = () => (
289
+ <HumaSnackBar
290
+ open={openSnackBar}
291
+ title='Signature Link Sent'
292
+ message='The secure signature session link has been sent to you via email.'
293
+ severity='success'
294
+ onClose={() => setOpenSnackBar(false)}
295
+ />
296
+ )
297
+
298
+ if (!loadingType) {
299
+ return (
300
+ <WrapperModal title={kycCopy.title}>
301
+ <Box css={styles.iconWrapper}>
302
+ <img src={ApproveLenderImg} alt='approve-lender' />
303
+ </Box>
304
+ <Box css={styles.description}>{kycCopy.description}</Box>
305
+ <BottomButton variant='contained' onClick={approveLender}>
306
+ {kycCopy.buttonText}
307
+ </BottomButton>
308
+ {getEmailLinkSentSnackbar()}
309
+ </WrapperModal>
310
+ )
311
+ }
312
+
313
+ return (
314
+ <LoadingModal
315
+ title='Lender Approval'
316
+ description={LoadingCopiesByType[loadingType].description}
317
+ />
318
+ )
319
+ }