@huma-finance/widgets 0.0.30 → 0.0.32

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 (27) hide show
  1. package/dist/cjs/index.cjs +911 -179
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/index.js +913 -182
  4. package/dist/index.js.map +1 -1
  5. package/package.json +5 -4
  6. package/src/components/ChooseAmountModal.tsx +322 -322
  7. package/src/components/CreditLine/borrow/5-Success.tsx +9 -1
  8. package/src/components/CreditLine/borrow/6-Notifications.tsx +10 -0
  9. package/src/components/CreditLine/borrow/index.tsx +21 -2
  10. package/src/components/Factoring/UpcomingList.tsx +1 -1
  11. package/src/components/HumaSnackBar.tsx +60 -0
  12. package/src/components/Lend/supply/1-Evaluation/EvaluationEA.tsx +86 -0
  13. package/src/components/Lend/supply/1-Evaluation/EvaluationKYC.tsx +287 -0
  14. package/src/components/Lend/supply/1-Evaluation/index.tsx +31 -0
  15. package/src/components/Lend/supply/2-ChooseAmount.tsx +0 -1
  16. package/src/components/Lend/supply/{7-Success.tsx → 5-Success.tsx} +13 -2
  17. package/src/components/Lend/supply/6-Notifications.tsx +10 -0
  18. package/src/components/Lend/supply/index.tsx +29 -6
  19. package/src/components/Notifi/NotifiSubscriptionModal.tsx +264 -0
  20. package/src/components/SuperfluidFactoring/index.tsx +85 -85
  21. package/src/components/TxDoneModal.tsx +3 -1
  22. package/src/cosmos/SDKTesting.fixture.tsx +616 -615
  23. package/src/hooks/useNotifi.ts +71 -0
  24. package/src/store/widgets.store.ts +1 -0
  25. package/src/components/Lend/supply/1-Evaluation.tsx +0 -171
  26. package/src/components/Lend/supply/4-ConfirmTransfer.tsx +0 -104
  27. /package/src/components/Lend/supply/{6-Transfer.tsx → 4-Transfer.tsx} +0 -0
@@ -1,8 +1,7 @@
1
- import { useWeb3React } from '@web3-react/core'
2
1
  import {
2
+ downScale,
3
3
  POOL_NAME,
4
4
  POOL_TYPE,
5
- downScale,
6
5
  useLenderApproved,
7
6
  useLenderPosition,
8
7
  usePoolAllowance,
@@ -10,9 +9,14 @@ import {
10
9
  usePoolUnderlyingTokenBalance,
11
10
  useWithdrawlLockoutInSeconds,
12
11
  } from '@huma-finance/shared'
12
+ import { useWeb3React } from '@web3-react/core'
13
13
  import React, { useCallback, useEffect } from 'react'
14
14
  import { useDispatch } from 'react-redux'
15
15
 
16
+ import {
17
+ useIsFirstTimeNotifiUser,
18
+ useDoesChainSupportNotifi,
19
+ } from '../../../hooks/useNotifi'
16
20
  import { useAppSelector } from '../../../hooks/useRedux'
17
21
  import { setStep } from '../../../store/widgets.reducers'
18
22
  import { selectWidgetState } from '../../../store/widgets.selectors'
@@ -22,8 +26,9 @@ import { WidgetWrapper } from '../../WidgetWrapper'
22
26
  import { Evaluation } from './1-Evaluation'
23
27
  import { ChooseAmount } from './2-ChooseAmount'
24
28
  import { ApproveAllowance } from './3-ApproveAllowance'
25
- import { Transfer } from './6-Transfer'
26
- import { Success } from './7-Success'
29
+ import { Transfer } from './4-Transfer'
30
+ import { Success } from './5-Success'
31
+ import { Notifications } from './6-Notifications'
27
32
 
28
33
  /**
29
34
  * Lend pool supply props
@@ -47,13 +52,15 @@ export function LendSupply({
47
52
  handleSuccess,
48
53
  }: LendSupplyProps): React.ReactElement | null {
49
54
  const dispatch = useDispatch()
50
- const { account } = useWeb3React()
55
+ const { account, chainId } = useWeb3React()
51
56
  const poolName = POOL_NAME[poolNameStr]
52
57
  const poolType = POOL_TYPE[poolTypeStr]
53
58
  const poolInfo = usePoolInfo(poolName, poolType)
54
59
  const { decimals } = poolInfo?.poolUnderlyingToken || {}
55
60
  const { step, errorMessage } = useAppSelector(selectWidgetState)
56
61
  const { allowance } = usePoolAllowance(poolName, poolType, account)
62
+ const { isFirstTimeNotifiUser } = useIsFirstTimeNotifiUser(account, chainId)
63
+ const { notifiChainSupported } = useDoesChainSupportNotifi(account, chainId)
57
64
  const [lenderApproved, refreshLenderApproved] = useLenderApproved(
58
65
  poolName,
59
66
  poolType,
@@ -98,6 +105,14 @@ export function LendSupply({
98
105
  [handleSuccess, refreshLenderPosition],
99
106
  )
100
107
 
108
+ const setupNotificationsOrClose = useCallback(() => {
109
+ if (isFirstTimeNotifiUser && notifiChainSupported) {
110
+ dispatch(setStep(WIDGET_STEP.Notifications))
111
+ } else {
112
+ handleClose()
113
+ }
114
+ }, [dispatch, handleClose, isFirstTimeNotifiUser, notifiChainSupported])
115
+
101
116
  if (!poolInfo) {
102
117
  return null
103
118
  }
@@ -113,6 +128,7 @@ export function LendSupply({
113
128
  {step === WIDGET_STEP.Evaluation && (
114
129
  <Evaluation
115
130
  poolInfo={poolInfo}
131
+ handleClose={handleClose}
116
132
  handleApproveSuccess={handleApproveSuccess}
117
133
  />
118
134
  )}
@@ -129,7 +145,14 @@ export function LendSupply({
129
145
  )}
130
146
  {step === WIDGET_STEP.Transfer && <Transfer poolInfo={poolInfo} />}
131
147
  {step === WIDGET_STEP.Done && (
132
- <Success poolInfo={poolInfo} handleAction={handleClose} />
148
+ <Success
149
+ poolInfo={poolInfo}
150
+ handleAction={setupNotificationsOrClose}
151
+ hasNextStep={isFirstTimeNotifiUser}
152
+ />
153
+ )}
154
+ {step === WIDGET_STEP.Notifications && (
155
+ <Notifications handleAction={handleClose} />
133
156
  )}
134
157
  {step === WIDGET_STEP.Error && (
135
158
  <ErrorModal
@@ -0,0 +1,264 @@
1
+ import React, { useCallback, useEffect, useState } from 'react'
2
+ import { useWeb3React } from '@web3-react/core'
3
+ import {
4
+ NotifiFrontendClient,
5
+ newFrontendClient,
6
+ Uint8SignMessageFunction,
7
+ SignMessageParams,
8
+ CardConfigItemV1,
9
+ DirectPushEventTypeItem,
10
+ EventTypeItem,
11
+ BroadcastEventTypeItem,
12
+ } from '@notifi-network/notifi-frontend-client'
13
+ import { getBlockchainConfigFromChain, txAtom } from '@huma-finance/shared'
14
+ import { ethers } from 'ethers'
15
+ import {
16
+ Box,
17
+ Input,
18
+ InputAdornment,
19
+ Typography,
20
+ css,
21
+ useTheme,
22
+ } from '@mui/material'
23
+ import Email from '@mui/icons-material/Email'
24
+ import { useResetAtom } from 'jotai/utils'
25
+
26
+ import { WrapperModal } from '../WrapperModal'
27
+ import { envUtil } from '../../utils/env'
28
+ import { BottomButton } from '../BottomButton'
29
+ import { LoadingModal } from '../LoadingModal'
30
+ import { CheckIcon } from '../icons'
31
+ import { resetState } from '../../store/widgets.reducers'
32
+ import { useAppDispatch } from '../../hooks/useRedux'
33
+
34
+ type Props = {
35
+ handleSuccess: () => void
36
+ }
37
+
38
+ function getIdFromEventType(eventType: EventTypeItem): string {
39
+ if (eventType.type === 'directPush') {
40
+ const directPushEventType = eventType as DirectPushEventTypeItem
41
+ return (
42
+ directPushEventType.directPushId as {
43
+ value: string
44
+ }
45
+ ).value
46
+ }
47
+ if (eventType.type === 'broadcast') {
48
+ const broadcastEventType = eventType as BroadcastEventTypeItem
49
+ return (
50
+ broadcastEventType.broadcastId as {
51
+ value: string
52
+ }
53
+ ).value
54
+ }
55
+
56
+ return ''
57
+ }
58
+
59
+ async function fetchEventTypes(
60
+ notifiClient: NotifiFrontendClient,
61
+ ): Promise<(EventTypeItem & { enabled: boolean })[]> {
62
+ const subscribedAlerts = await notifiClient.getAlerts()
63
+
64
+ const subscriptionConfig = (await notifiClient.fetchSubscriptionCard({
65
+ id: process.env.REACT_APP_NOTIFI_CONFIG_ID ?? '',
66
+ type: 'SUBSCRIPTION_CARD',
67
+ })) as CardConfigItemV1
68
+
69
+ const eventTypes = subscriptionConfig?.eventTypes?.map(
70
+ (eventType): EventTypeItem & { enabled: boolean } => {
71
+ const eventTypeId = getIdFromEventType(eventType)
72
+ let enabled = false
73
+ // Check whether the user is subscribed to the event
74
+ const subscribedAlert = subscribedAlerts?.find(
75
+ (alert) => alert.filterOptions.indexOf(eventTypeId) >= 0,
76
+ )
77
+
78
+ if (subscribedAlert) {
79
+ enabled = true
80
+ }
81
+
82
+ return {
83
+ ...eventType,
84
+ enabled,
85
+ }
86
+ },
87
+ )
88
+
89
+ return eventTypes
90
+ }
91
+
92
+ export function NotifiSubscriptionModal({
93
+ handleSuccess,
94
+ }: Props): React.ReactElement {
95
+ const theme = useTheme()
96
+ const reset = useResetAtom(txAtom)
97
+ const dispatch = useAppDispatch()
98
+ const { account, chainId, provider } = useWeb3React()
99
+ const [notifiClient, setNotifiClient] = useState<NotifiFrontendClient>()
100
+ const [showSuccessScreen, setShowSuccessScreen] = useState<boolean>(false)
101
+ const [isLoading, setIsLoading] = useState<boolean>(false)
102
+ const [emailAddress, setEmailAddress] = useState<string>()
103
+ const [emailValid, setEmailValid] = useState<boolean>()
104
+
105
+ useEffect(() => {
106
+ const initializeClient = async () => {
107
+ if (account != null && chainId != null) {
108
+ const client = newFrontendClient({
109
+ account: { publicKey: account },
110
+ tenantId: 'humadapp',
111
+ env: envUtil.checkIsDev() ? 'Development' : 'Production',
112
+ walletBlockchain: getBlockchainConfigFromChain(chainId),
113
+ })
114
+
115
+ await client.initialize()
116
+
117
+ setNotifiClient(client)
118
+ }
119
+ }
120
+
121
+ initializeClient()
122
+ }, [account, chainId])
123
+
124
+ const signMessage: Uint8SignMessageFunction = async (
125
+ message: Uint8Array,
126
+ ): Promise<Uint8Array> => {
127
+ const signer = provider!.getSigner()
128
+ const signature = await signer.signMessage(Buffer.from(message))
129
+ return ethers.utils.arrayify(signature)
130
+ }
131
+
132
+ const handleEmailAddressChange = (
133
+ event: React.ChangeEvent<HTMLInputElement>,
134
+ ) => {
135
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
136
+ setEmailValid(emailRegex.test(event.target.value ?? ''))
137
+ setEmailAddress(event.target.value)
138
+ }
139
+
140
+ const logInAndSubscribe = async (): Promise<void> => {
141
+ if (notifiClient != null && chainId != null) {
142
+ setIsLoading(true)
143
+
144
+ // Ask user to sign a message to authenticate
145
+ try {
146
+ await notifiClient.logIn({
147
+ walletBlockchain: getBlockchainConfigFromChain(chainId),
148
+ signMessage,
149
+ } as SignMessageParams)
150
+ } catch (e) {
151
+ setIsLoading(false)
152
+ return
153
+ }
154
+
155
+ // Fetch all event types
156
+ const fetchedEventTypes = await fetchEventTypes(notifiClient)
157
+
158
+ // Authorize this email address for this wallet
159
+ await notifiClient.ensureTargetGroup({
160
+ name: 'Default',
161
+ emailAddress: emailAddress!,
162
+ })
163
+
164
+ // Intentionally running this in serial because the Notifi ensureAlert can't handle
165
+ // multiple calls. Notified their team to fix this.
166
+ // eslint-disable-next-line no-restricted-syntax
167
+ for (const eventType of fetchedEventTypes) {
168
+ // eslint-disable-next-line no-await-in-loop
169
+ await notifiClient.ensureAlert({
170
+ eventType,
171
+ inputs: {},
172
+ })
173
+ }
174
+
175
+ setShowSuccessScreen(true)
176
+ setIsLoading(false)
177
+ }
178
+ }
179
+
180
+ const handleCloseModal = useCallback(() => {
181
+ reset()
182
+ dispatch(resetState())
183
+ handleSuccess()
184
+ }, [dispatch, handleSuccess, reset])
185
+
186
+ const styles = {
187
+ disclaimer: css`
188
+ ${theme.cssMixins.rowCentered};
189
+ font-family: 'Uni-Neue-Regular';
190
+ font-size: 16px;
191
+ color: #a8a1b2;
192
+ margin-top: 48px;
193
+ `,
194
+ inputField: css`
195
+ width: 90%;
196
+ font-size: 16px;
197
+ `,
198
+ inputWrapper: css`
199
+ width: 100%;
200
+ `,
201
+ header: css`
202
+ ${theme.cssMixins.rowHCentered};
203
+ margin-top: -5px;
204
+ `,
205
+ check: css`
206
+ width: 100%;
207
+ ${theme.cssMixins.rowHCentered};
208
+ margin-top: 82px;
209
+ `,
210
+ }
211
+
212
+ if (isLoading) {
213
+ return <LoadingModal title='Notifications' />
214
+ }
215
+
216
+ if (showSuccessScreen) {
217
+ return (
218
+ <WrapperModal
219
+ title='Success'
220
+ subTitle='You successfully connected your email'
221
+ >
222
+ <Box css={styles.check}>
223
+ <CheckIcon />
224
+ </Box>
225
+ <BottomButton variant='contained' onClick={handleCloseModal}>
226
+ DONE
227
+ </BottomButton>
228
+ </WrapperModal>
229
+ )
230
+ }
231
+
232
+ return (
233
+ <WrapperModal title='Connect Your Email'>
234
+ <Typography variant='body2' margin='12px 0'>
235
+ Connect your wallet to receive important emails about your account
236
+ </Typography>
237
+ <Box css={styles.inputWrapper}>
238
+ <Input
239
+ css={styles.inputField}
240
+ type='email'
241
+ value={emailAddress}
242
+ onChange={handleEmailAddressChange}
243
+ placeholder='youremail@example.com'
244
+ startAdornment={
245
+ <InputAdornment position='start'>
246
+ <Email />
247
+ </InputAdornment>
248
+ }
249
+ />
250
+ </Box>
251
+ <Box css={styles.disclaimer}>
252
+ You can adjust these settings at any time by clicking the alert icon in
253
+ the header
254
+ </Box>
255
+ <BottomButton
256
+ variant='contained'
257
+ onClick={logInAndSubscribe}
258
+ disabled={!emailValid || isLoading}
259
+ >
260
+ CONNECT WALLET
261
+ </BottomButton>
262
+ </WrapperModal>
263
+ )
264
+ }
@@ -1,85 +1,85 @@
1
- import { Box } from '@mui/material'
2
- import { useWeb3React } from '@web3-react/core'
3
- import {
4
- CreditEvent,
5
- POOL_NAME,
6
- POOL_TYPE,
7
- PoolMap,
8
- PoolSubgraphMap,
9
- useFactoring,
10
- } from '@huma-finance/shared'
11
- import React from 'react'
12
-
13
- import { Activity } from '../Activity'
14
- import { ApolloWrapper } from '../ApolloWrapper'
15
- import { StreamFactoringBorrow } from '../StreamFactoring/borrow'
16
- import { SuperfluidFactoredList } from './SuperfluidFactoredList'
17
- import { StreamItem, SuperfluidUpcomingList } from './SuperfluidUpcomingList'
18
-
19
- export function SuperfluidFactoring(): React.ReactElement {
20
- const poolName = POOL_NAME.Superfluid
21
- const { chainId } = useWeb3React()
22
- const {
23
- accountStats,
24
- accountHasActiveLoan,
25
- actionType,
26
- loading,
27
- poolInfo,
28
- modalIsOpen,
29
- selectedItem,
30
- styles,
31
- handleGetPaid,
32
- handleGetPaidSuccess,
33
- handlePayManually,
34
- handleClose,
35
- } = useFactoring<StreamItem>(poolName, POOL_TYPE.Stream)
36
-
37
- return (
38
- <Box css={styles.wrapper}>
39
- <Box css={styles.title}>Stream Factoring</Box>
40
- <Box css={styles.description} marginBottom='47px'>
41
- {PoolMap.Stream[poolName]?.borrowDesc}
42
- </Box>
43
- {poolInfo && (
44
- <SuperfluidFactoredList
45
- poolInfo={poolInfo}
46
- handlePayManually={handlePayManually}
47
- loading={loading && actionType === 'payment'}
48
- accountStats={accountStats}
49
- />
50
- )}
51
- <ApolloWrapper uri={PoolSubgraphMap[chainId!]?.subgraph!}>
52
- <SuperfluidUpcomingList
53
- poolInfo={poolInfo}
54
- handleGetPaid={handleGetPaid}
55
- hasActiveLoan={accountHasActiveLoan}
56
- loading={loading && actionType === 'borrow'}
57
- />
58
- </ApolloWrapper>
59
- <ApolloWrapper uri={PoolSubgraphMap[chainId!]?.subgraph}>
60
- {poolInfo && (
61
- <Activity
62
- poolInfo={poolInfo}
63
- targetEvents={[
64
- CreditEvent.DrawdownMadeWithReceivable,
65
- CreditEvent.PaymentMade,
66
- CreditEvent.ReceivedPaymentProcessed,
67
- ]}
68
- />
69
- )}
70
- </ApolloWrapper>
71
- {poolInfo && selectedItem && actionType === 'borrow' && (
72
- <StreamFactoringBorrow
73
- poolName={POOL_NAME.Superfluid}
74
- poolType={POOL_TYPE.Stream}
75
- payerAddress={selectedItem.payer}
76
- superToken={selectedItem.tokenId}
77
- currentFlowRate={selectedItem.flowrate}
78
- isOpen={modalIsOpen}
79
- handleClose={handleClose}
80
- handleSuccess={handleGetPaidSuccess}
81
- />
82
- )}
83
- </Box>
84
- )
85
- }
1
+ import { Box } from '@mui/material'
2
+ import { useWeb3React } from '@web3-react/core'
3
+ import {
4
+ CreditEvent,
5
+ POOL_NAME,
6
+ POOL_TYPE,
7
+ PoolMap,
8
+ PoolSubgraphMap,
9
+ useFactoring,
10
+ } from '@huma-finance/shared'
11
+ import React from 'react'
12
+
13
+ import { Activity } from '../Activity'
14
+ import { ApolloWrapper } from '../ApolloWrapper'
15
+ import { StreamFactoringBorrow } from '../StreamFactoring/borrow'
16
+ import { SuperfluidFactoredList } from './SuperfluidFactoredList'
17
+ import { StreamItem, SuperfluidUpcomingList } from './SuperfluidUpcomingList'
18
+
19
+ export function SuperfluidFactoring(): React.ReactElement {
20
+ const poolName = POOL_NAME.Superfluid
21
+ const { chainId } = useWeb3React()
22
+ const {
23
+ accountStats,
24
+ accountHasActiveLoan,
25
+ actionType,
26
+ loading,
27
+ poolInfo,
28
+ modalIsOpen,
29
+ selectedItem,
30
+ styles,
31
+ handleGetPaid,
32
+ handleGetPaidSuccess,
33
+ handlePayManually,
34
+ handleClose,
35
+ } = useFactoring<StreamItem>(poolName, POOL_TYPE.Stream)
36
+
37
+ return (
38
+ <Box css={styles.wrapper}>
39
+ <Box css={styles.title}>Stream Factoring</Box>
40
+ <Box css={styles.description} marginBottom='47px'>
41
+ {PoolMap.Stream[poolName]?.borrowDesc}
42
+ </Box>
43
+ {poolInfo && (
44
+ <SuperfluidFactoredList
45
+ poolInfo={poolInfo}
46
+ handlePayManually={handlePayManually}
47
+ loading={loading && actionType === 'payment'}
48
+ accountStats={accountStats}
49
+ />
50
+ )}
51
+ <ApolloWrapper uri={poolInfo?.extra?.subgraph!}>
52
+ <SuperfluidUpcomingList
53
+ poolInfo={poolInfo}
54
+ handleGetPaid={handleGetPaid}
55
+ hasActiveLoan={accountHasActiveLoan}
56
+ loading={loading && actionType === 'borrow'}
57
+ />
58
+ </ApolloWrapper>
59
+ <ApolloWrapper uri={PoolSubgraphMap[chainId!]?.subgraph}>
60
+ {poolInfo && (
61
+ <Activity
62
+ poolInfo={poolInfo}
63
+ targetEvents={[
64
+ CreditEvent.DrawdownMadeWithReceivable,
65
+ CreditEvent.PaymentMade,
66
+ CreditEvent.ReceivedPaymentProcessed,
67
+ ]}
68
+ />
69
+ )}
70
+ </ApolloWrapper>
71
+ {poolInfo && selectedItem && actionType === 'borrow' && (
72
+ <StreamFactoringBorrow
73
+ poolName={POOL_NAME.Superfluid}
74
+ poolType={POOL_TYPE.Stream}
75
+ payerAddress={selectedItem.payer}
76
+ superToken={selectedItem.tokenId}
77
+ currentFlowRate={selectedItem.flowrate}
78
+ isOpen={modalIsOpen}
79
+ handleClose={handleClose}
80
+ handleSuccess={handleGetPaidSuccess}
81
+ />
82
+ )}
83
+ </Box>
84
+ )
85
+ }
@@ -10,11 +10,13 @@ import { CheckIcon } from './icons'
10
10
  type Props = {
11
11
  content: string[]
12
12
  handleAction: () => void
13
+ buttonText?: string
13
14
  }
14
15
 
15
16
  export function TxDoneModal({
16
17
  content,
17
18
  handleAction,
19
+ buttonText,
18
20
  }: Props): React.ReactElement {
19
21
  const theme = useTheme()
20
22
  const dispatch = useAppDispatch()
@@ -76,7 +78,7 @@ export function TxDoneModal({
76
78
  css={styles.doneButton}
77
79
  onClick={handleCloseModal}
78
80
  >
79
- DONE
81
+ {buttonText == null ? 'DONE' : buttonText}
80
82
  </Button>
81
83
  </Box>
82
84
  )