@huma-finance/widgets 0.0.6-alpha.0

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 (203) hide show
  1. package/.env.example +3 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc.js +64 -0
  4. package/.prettierignore +4 -0
  5. package/.prettierrc.js +16 -0
  6. package/API.md +331 -0
  7. package/LICENSE +661 -0
  8. package/README.md +48 -0
  9. package/babel.config.js +15 -0
  10. package/config-overrides.js +9 -0
  11. package/cosmos.config.json +7 -0
  12. package/cypress/e2e/01-invoice-factoring-borrow.test.ts +62 -0
  13. package/cypress/support/e2e.ts +76 -0
  14. package/cypress/support/ethereum.ts +142 -0
  15. package/cypress/tsconfig.json +9 -0
  16. package/cypress/utils/index.ts +54 -0
  17. package/cypress.config.ts +20 -0
  18. package/dist/assets/output-15e10f9f.css +44 -0
  19. package/dist/cjs/assets/output-15e10f9f.css +44 -0
  20. package/dist/cjs/index.cjs +17576 -0
  21. package/dist/cjs/index.cjs.map +1 -0
  22. package/dist/index.d.ts +211 -0
  23. package/dist/index.js +17529 -0
  24. package/dist/index.js.map +1 -0
  25. package/jsdoc2md.json +20 -0
  26. package/package.json +193 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/index.html +40 -0
  29. package/public/logo192.png +0 -0
  30. package/public/logo512.png +0 -0
  31. package/public/manifest.json +25 -0
  32. package/public/robots.txt +3 -0
  33. package/rollup.config.js +81 -0
  34. package/src/components/Activity.tsx +123 -0
  35. package/src/components/AllowListModal.tsx +113 -0
  36. package/src/components/ApolloWrapper.tsx +15 -0
  37. package/src/components/ApproveAllowanceModal.tsx +118 -0
  38. package/src/components/BottomButton.tsx +31 -0
  39. package/src/components/ChooseAmountModal.tsx +315 -0
  40. package/src/components/CreditLine/borrow/1-Evaluation.tsx +159 -0
  41. package/src/components/CreditLine/borrow/2-AllowList.tsx +34 -0
  42. package/src/components/CreditLine/borrow/3-ChooseAmount.tsx +74 -0
  43. package/src/components/CreditLine/borrow/4-ApproveAllowance.tsx +29 -0
  44. package/src/components/CreditLine/borrow/5-Transfer.tsx +30 -0
  45. package/src/components/CreditLine/borrow/6-Success.tsx +30 -0
  46. package/src/components/CreditLine/borrow/index.tsx +119 -0
  47. package/src/components/CreditLine/payment/1-ChooseAmount.tsx +67 -0
  48. package/src/components/CreditLine/payment/2-ApproveAllowance.tsx +23 -0
  49. package/src/components/CreditLine/payment/3-Transfer.tsx +34 -0
  50. package/src/components/CreditLine/payment/4-Success.tsx +39 -0
  51. package/src/components/CreditLine/payment/index.tsx +94 -0
  52. package/src/components/ErrorModal.tsx +132 -0
  53. package/src/components/Factoring/FactoredList.tsx +143 -0
  54. package/src/components/Factoring/UpcomingList.tsx +90 -0
  55. package/src/components/Factoring/index.ts +2 -0
  56. package/src/components/HumaLoading.tsx +68 -0
  57. package/src/components/HumaTable.tsx +158 -0
  58. package/src/components/InvoiceFactoring/borrow/1-Evaluation.tsx +46 -0
  59. package/src/components/InvoiceFactoring/borrow/2-AllowList.tsx +34 -0
  60. package/src/components/InvoiceFactoring/borrow/3-ChooseAmount.tsx +92 -0
  61. package/src/components/InvoiceFactoring/borrow/4-ConfirmTransfer.tsx +104 -0
  62. package/src/components/InvoiceFactoring/borrow/5-ApproveNFT.tsx +55 -0
  63. package/src/components/InvoiceFactoring/borrow/6-Transfer.tsx +34 -0
  64. package/src/components/InvoiceFactoring/borrow/7-Success.tsx +20 -0
  65. package/src/components/InvoiceFactoring/borrow/index.tsx +96 -0
  66. package/src/components/InvoiceFactoring/payment/1-ChooseAmount.tsx +67 -0
  67. package/src/components/InvoiceFactoring/payment/2-ApproveAllowance.tsx +23 -0
  68. package/src/components/InvoiceFactoring/payment/3-Transfer.tsx +34 -0
  69. package/src/components/InvoiceFactoring/payment/4-Success.tsx +23 -0
  70. package/src/components/InvoiceFactoring/payment/index.tsx +86 -0
  71. package/src/components/Lend/supply/1-Evaluation.tsx +86 -0
  72. package/src/components/Lend/supply/2-ChooseAmount.tsx +70 -0
  73. package/src/components/Lend/supply/3-ApproveAllowance.tsx +25 -0
  74. package/src/components/Lend/supply/4-ConfirmTransfer.tsx +104 -0
  75. package/src/components/Lend/supply/6-Transfer.tsx +32 -0
  76. package/src/components/Lend/supply/7-Success.tsx +49 -0
  77. package/src/components/Lend/supply/index.tsx +144 -0
  78. package/src/components/Lend/withdraw/1-CheckWithdrawble.tsx +73 -0
  79. package/src/components/Lend/withdraw/2-ChooseAmount.tsx +50 -0
  80. package/src/components/Lend/withdraw/3-Transfer.tsx +32 -0
  81. package/src/components/Lend/withdraw/4-Success.tsx +49 -0
  82. package/src/components/Lend/withdraw/index.tsx +101 -0
  83. package/src/components/LoadingModal.tsx +49 -0
  84. package/src/components/StreamFactoring/borrow/1-Evaluation.tsx +57 -0
  85. package/src/components/StreamFactoring/borrow/2-ChooseAmount.tsx +124 -0
  86. package/src/components/StreamFactoring/borrow/3-ConfirmTransfer.tsx +104 -0
  87. package/src/components/StreamFactoring/borrow/4-ApproveAllowance.tsx +65 -0
  88. package/src/components/StreamFactoring/borrow/5-Permit.tsx +144 -0
  89. package/src/components/StreamFactoring/borrow/6-Transfer.tsx +37 -0
  90. package/src/components/StreamFactoring/borrow/7-Success.tsx +20 -0
  91. package/src/components/StreamFactoring/borrow/index.tsx +95 -0
  92. package/src/components/StreamFactoring/payment/ChooseAmount.tsx +54 -0
  93. package/src/components/StreamFactoring/payment/Success.tsx +23 -0
  94. package/src/components/StreamFactoring/payment/Transfer.tsx +34 -0
  95. package/src/components/StreamFactoring/payment/index.tsx +66 -0
  96. package/src/components/SuperfluidFactoring/SuperfluidFactoredList.tsx +61 -0
  97. package/src/components/SuperfluidFactoring/SuperfluidUpcomingList.tsx +185 -0
  98. package/src/components/SuperfluidFactoring/index.tsx +85 -0
  99. package/src/components/TxDoneModal.tsx +83 -0
  100. package/src/components/TxSendModal.tsx +59 -0
  101. package/src/components/ViewOnExplorer.tsx +26 -0
  102. package/src/components/WidgetWrapper.tsx +74 -0
  103. package/src/components/WrapperModal.tsx +54 -0
  104. package/src/components/buttons/TableActionButton.tsx +36 -0
  105. package/src/components/buttons/index.ts +1 -0
  106. package/src/components/humaModal/HumaModal.tsx +68 -0
  107. package/src/components/humaModal/HumaModalHeader.tsx +70 -0
  108. package/src/components/humaModal/index.tsx +2 -0
  109. package/src/components/icons/approve-lender.svg +30 -0
  110. package/src/components/icons/auto-payback.svg +40 -0
  111. package/src/components/icons/check-circle.svg +3 -0
  112. package/src/components/icons/check-green.svg +3 -0
  113. package/src/components/icons/check.svg +10 -0
  114. package/src/components/icons/donut.svg +904 -0
  115. package/src/components/icons/ellipse.svg +3 -0
  116. package/src/components/icons/ethereum.svg +1 -0
  117. package/src/components/icons/huma-full.svg +22 -0
  118. package/src/components/icons/huma.svg +15 -0
  119. package/src/components/icons/index.tsx +93 -0
  120. package/src/components/icons/loader.svg +229 -0
  121. package/src/components/icons/metamask.svg +31 -0
  122. package/src/components/icons/polygon.svg +16 -0
  123. package/src/components/icons/receipt.svg +3 -0
  124. package/src/components/icons/separator.svg +3 -0
  125. package/src/components/icons/sorry.svg +31 -0
  126. package/src/components/icons/spinner.gif +0 -0
  127. package/src/components/icons/usdc.svg +5 -0
  128. package/src/components/icons/wallet-connect.svg +12 -0
  129. package/src/cosmos/CreditLineBorrow.fixture.tsx +85 -0
  130. package/src/cosmos/CreditLinePayment.fixture.tsx +49 -0
  131. package/src/cosmos/InvoiceFactoringBorrow.fixture.tsx +111 -0
  132. package/src/cosmos/InvoiceFactoringPayment.fixture.tsx +67 -0
  133. package/src/cosmos/LendSupply.fixture.tsx +75 -0
  134. package/src/cosmos/LendWithdraw.fixture.tsx +43 -0
  135. package/src/cosmos/SDKTesting.fixture.tsx +507 -0
  136. package/src/cosmos/SuperfluidFactoringBorrow.fixture.tsx +12 -0
  137. package/src/cosmos/WalletWrapper.tsx +52 -0
  138. package/src/cosmos/abis/InvoiceNFT.json +644 -0
  139. package/src/cosmos/abis/ReceivableFactoringPool.json +1529 -0
  140. package/src/cosmos/utils/index.ts +169 -0
  141. package/src/fonts/Inter/Inter-Black.ttf +0 -0
  142. package/src/fonts/Inter/Inter-Bold.ttf +0 -0
  143. package/src/fonts/Inter/Inter-ExtraBold.ttf +0 -0
  144. package/src/fonts/Inter/Inter-ExtraLight.ttf +0 -0
  145. package/src/fonts/Inter/Inter-Light.ttf +0 -0
  146. package/src/fonts/Inter/Inter-Medium.ttf +0 -0
  147. package/src/fonts/Inter/Inter-Regular.ttf +0 -0
  148. package/src/fonts/Inter/Inter-SemiBold.ttf +0 -0
  149. package/src/fonts/Inter/Inter-Thin.ttf +0 -0
  150. package/src/fonts/Recoleta/Recoleta-Black.otf +0 -0
  151. package/src/fonts/Recoleta/Recoleta-Bold.otf +0 -0
  152. package/src/fonts/Recoleta/Recoleta-Light.otf +0 -0
  153. package/src/fonts/Recoleta/Recoleta-Medium.otf +0 -0
  154. package/src/fonts/Recoleta/Recoleta-Regular.otf +0 -0
  155. package/src/fonts/Recoleta/Recoleta-RegularDEMO.otf +0 -0
  156. package/src/fonts/Recoleta/Recoleta-SemiBold.otf +0 -0
  157. package/src/fonts/Recoleta/Recoleta-Thin.otf +0 -0
  158. package/src/fonts/Roboto/LICENSE.txt +202 -0
  159. package/src/fonts/Roboto/Roboto-Black.ttf +0 -0
  160. package/src/fonts/Roboto/Roboto-BlackItalic.ttf +0 -0
  161. package/src/fonts/Roboto/Roboto-Bold.ttf +0 -0
  162. package/src/fonts/Roboto/Roboto-BoldItalic.ttf +0 -0
  163. package/src/fonts/Roboto/Roboto-Italic.ttf +0 -0
  164. package/src/fonts/Roboto/Roboto-Light.ttf +0 -0
  165. package/src/fonts/Roboto/Roboto-LightItalic.ttf +0 -0
  166. package/src/fonts/Roboto/Roboto-Medium.ttf +0 -0
  167. package/src/fonts/Roboto/Roboto-MediumItalic.ttf +0 -0
  168. package/src/fonts/Roboto/Roboto-Regular.ttf +0 -0
  169. package/src/fonts/Roboto/Roboto-Thin.ttf +0 -0
  170. package/src/fonts/Roboto/Roboto-ThinItalic.ttf +0 -0
  171. package/src/fonts/UniNeue/UniNeue-Black.ttf +0 -0
  172. package/src/fonts/UniNeue/UniNeue-Bold.ttf +0 -0
  173. package/src/fonts/UniNeue/UniNeue-Book.ttf +0 -0
  174. package/src/fonts/UniNeue/UniNeue-Heavy.ttf +0 -0
  175. package/src/fonts/UniNeue/UniNeue-Light.ttf +0 -0
  176. package/src/fonts/UniNeue/UniNeue-Regular.ttf +0 -0
  177. package/src/fonts/UniNeue/UniNeue-Thin.ttf +0 -0
  178. package/src/hooks/useEA.ts +70 -0
  179. package/src/hooks/useRedux.ts +6 -0
  180. package/src/index.css +61 -0
  181. package/src/index.tsx +219 -0
  182. package/src/react-app-env.d.ts +1 -0
  183. package/src/reportWebVitals.ts +15 -0
  184. package/src/setupTests.ts +6 -0
  185. package/src/store/index.ts +27 -0
  186. package/src/store/widgets.reducers.ts +120 -0
  187. package/src/store/widgets.selectors.ts +3 -0
  188. package/src/store/widgets.store.ts +55 -0
  189. package/src/theme/breakpoints.ts +10 -0
  190. package/src/theme/components.ts +70 -0
  191. package/src/theme/coreThemeConstants.ts +29 -0
  192. package/src/theme/cssMixins.ts +92 -0
  193. package/src/theme/hooks/useGetSizes.ts +34 -0
  194. package/src/theme/index.ts +24 -0
  195. package/src/theme/palette.ts +150 -0
  196. package/src/theme/shape.ts +7 -0
  197. package/src/theme/theme.types.ts +80 -0
  198. package/src/theme/typography.ts +117 -0
  199. package/src/utilTypes.d.ts +29 -0
  200. package/src/utils/env.ts +9 -0
  201. package/tsconfig.build.json +7 -0
  202. package/tsconfig.json +32 -0
  203. package/webpack.cosmos.js +54 -0
@@ -0,0 +1,101 @@
1
+ import { useWeb3React } from '@web3-react/core'
2
+ import {
3
+ POOL_NAME,
4
+ POOL_TYPE,
5
+ useLenderPosition,
6
+ usePoolInfo,
7
+ } from '@huma-finance/shared'
8
+ import React, { useCallback, useEffect } from 'react'
9
+ import { useDispatch } from 'react-redux'
10
+
11
+ import { useAppSelector } from '../../../hooks/useRedux'
12
+ import { setStep } from '../../../store/widgets.reducers'
13
+ import { selectWidgetState } from '../../../store/widgets.selectors'
14
+ import { WIDGET_STEP } from '../../../store/widgets.store'
15
+ import { ErrorModal } from '../../ErrorModal'
16
+ import { WidgetWrapper } from '../../WidgetWrapper'
17
+ import { CheckWithdrawable } from './1-CheckWithdrawble'
18
+ import { ChooseAmount } from './2-ChooseAmount'
19
+ import { Transfer } from './3-Transfer'
20
+ import { Success } from './4-Success'
21
+
22
+ /**
23
+ * Lend pool withdraw props
24
+ * @typedef {Object} LendWithdrawProps
25
+ * @property {POOL_NAME} poolName The name of the pool.
26
+ * @property {POOL_TYPE} poolType The type of the pool.
27
+ * @property {function():void} handleClose Function to notify to close the widget modal when user clicks the 'x' close button.
28
+ * @property {function((number|undefined)):void|undefined} handleSuccess Optional function to notify that the lending pool withdraw action is successful.
29
+ */
30
+ export type LendWithdrawProps = {
31
+ poolName: keyof typeof POOL_NAME
32
+ poolType: keyof typeof POOL_TYPE
33
+ handleClose: () => void
34
+ handleSuccess?: (blockNumber?: number) => void
35
+ }
36
+
37
+ export function LendWithdraw({
38
+ poolName: poolNameStr,
39
+ poolType: poolTypeStr,
40
+ handleClose,
41
+ handleSuccess,
42
+ }: LendWithdrawProps): React.ReactElement | null {
43
+ const dispatch = useDispatch()
44
+ const { account } = useWeb3React()
45
+ const poolName = POOL_NAME[poolNameStr]
46
+ const poolType = POOL_TYPE[poolTypeStr]
47
+ const poolInfo = usePoolInfo(poolName, poolType)
48
+ const { step, errorMessage } = useAppSelector(selectWidgetState)
49
+ const [lenderPosition, refreshLenderPosition] = useLenderPosition(
50
+ poolName,
51
+ poolType,
52
+ account,
53
+ )
54
+
55
+ useEffect(() => {
56
+ if (!step && lenderPosition) {
57
+ dispatch(setStep(WIDGET_STEP.CheckWithdrawable))
58
+ }
59
+ }, [dispatch, lenderPosition, step])
60
+
61
+ const handleWithdrawSuccess = useCallback(
62
+ (blockNumber: number) => {
63
+ refreshLenderPosition()
64
+ if (handleSuccess) {
65
+ handleSuccess(blockNumber)
66
+ }
67
+ },
68
+ [handleSuccess, refreshLenderPosition],
69
+ )
70
+
71
+ if (!poolInfo) {
72
+ return null
73
+ }
74
+
75
+ return (
76
+ <WidgetWrapper
77
+ isOpen
78
+ handleClose={handleClose}
79
+ handleSuccess={handleWithdrawSuccess}
80
+ >
81
+ {(!step || step === WIDGET_STEP.CheckWithdrawable) && (
82
+ <CheckWithdrawable poolInfo={poolInfo} handleClose={handleClose} />
83
+ )}
84
+ {step === WIDGET_STEP.ChooseAmount && (
85
+ <ChooseAmount poolInfo={poolInfo} lenderPosition={lenderPosition!} />
86
+ )}
87
+ {step === WIDGET_STEP.Transfer && <Transfer poolInfo={poolInfo} />}
88
+ {step === WIDGET_STEP.Done && (
89
+ <Success poolInfo={poolInfo} handleAction={handleClose} />
90
+ )}
91
+ {step === WIDGET_STEP.Error && (
92
+ <ErrorModal
93
+ title='Withdraw'
94
+ errorReason='Sorry there was an error'
95
+ errorMessage={errorMessage}
96
+ handleOk={handleClose}
97
+ />
98
+ )}
99
+ </WidgetWrapper>
100
+ )
101
+ }
@@ -0,0 +1,49 @@
1
+ import { Box, css, useTheme } from '@mui/material'
2
+ import React from 'react'
3
+ import { HumaLoading } from './HumaLoading'
4
+
5
+ import { WrapperModal } from './WrapperModal'
6
+
7
+ type Props = {
8
+ title: string
9
+ description?: string
10
+ hideLoader?: boolean
11
+ children?: React.ReactNode
12
+ }
13
+
14
+ export function LoadingModal({
15
+ title,
16
+ description,
17
+ hideLoader = false,
18
+ children,
19
+ }: Props): React.ReactElement {
20
+ const theme = useTheme()
21
+
22
+ const styles = {
23
+ loader: css`
24
+ width: 100%;
25
+ ${theme.cssMixins.rowHCentered};
26
+ position: absolute;
27
+ top: 50%;
28
+ transform: translateY(-50%);
29
+ `,
30
+ bottom: css`
31
+ & .MuiButtonBase-root {
32
+ width: 100%;
33
+ position: absolute;
34
+ bottom: 0;
35
+ }
36
+ `,
37
+ }
38
+
39
+ return (
40
+ <WrapperModal title={title} subTitle={description}>
41
+ {!hideLoader && (
42
+ <Box css={styles.loader}>
43
+ <HumaLoading />
44
+ </Box>
45
+ )}
46
+ <Box css={styles.bottom}>{children}</Box>
47
+ </WrapperModal>
48
+ )
49
+ }
@@ -0,0 +1,57 @@
1
+ import { useWeb3React } from '@web3-react/core'
2
+ import { PoolInfoType, toBigNumber } from '@huma-finance/shared'
3
+ import React, { useEffect } from 'react'
4
+
5
+ import useEA from '../../../hooks/useEA'
6
+ import { LoadingModal } from '../../LoadingModal'
7
+ import { WIDGET_STEP } from '../../../store/widgets.store'
8
+
9
+ type Props = {
10
+ poolInfo: PoolInfoType
11
+ payerAddress: string
12
+ superToken: string
13
+ currentFlowRate: string
14
+ }
15
+
16
+ export function Evaluation({
17
+ poolInfo,
18
+ payerAddress,
19
+ superToken,
20
+ currentFlowRate,
21
+ }: Props): React.ReactElement {
22
+ const { account } = useWeb3React()
23
+ const { checkingEA } = useEA()
24
+ const ONE_MONTH_SECONDS = 30 * 24 * 60 * 60
25
+ const creditLimit = toBigNumber(currentFlowRate)
26
+ .mul(ONE_MONTH_SECONDS)
27
+ .toString()
28
+
29
+ useEffect(() => {
30
+ if (account) {
31
+ checkingEA(
32
+ {
33
+ creditLimit,
34
+ poolAddress: poolInfo.pool,
35
+ borrowerWalletAddress: account,
36
+ payerAddress,
37
+ superToken,
38
+ },
39
+ WIDGET_STEP.ChooseAmount,
40
+ )
41
+ }
42
+ }, [
43
+ account,
44
+ checkingEA,
45
+ creditLimit,
46
+ payerAddress,
47
+ poolInfo.pool,
48
+ superToken,
49
+ ])
50
+
51
+ return (
52
+ <LoadingModal
53
+ title='Checking Stream'
54
+ description='Checking if the stream qualifies for factoring...'
55
+ />
56
+ )
57
+ }
@@ -0,0 +1,124 @@
1
+ import {
2
+ downScale,
3
+ PoolInfoType,
4
+ toBigNumber,
5
+ upScale,
6
+ useFeeManager,
7
+ } from '@huma-finance/shared'
8
+ import React, { useCallback, useState } from 'react'
9
+
10
+ import { useAppDispatch, useAppSelector } from '../../../hooks/useRedux'
11
+ import { setBorrowInfo, setStream } from '../../../store/widgets.reducers'
12
+ import { selectWidgetState } from '../../../store/widgets.selectors'
13
+ import { WIDGET_STEP } from '../../../store/widgets.store'
14
+ import { ChooseAmountModal } from '../../ChooseAmountModal'
15
+
16
+ type Props = {
17
+ poolInfo: PoolInfoType
18
+ currentFlowRate: string
19
+ }
20
+
21
+ export function ChooseAmount({
22
+ poolInfo,
23
+ currentFlowRate,
24
+ }: Props): React.ReactElement | null {
25
+ const dispatch = useAppDispatch()
26
+ const { approveInfo } = useAppSelector(selectWidgetState)
27
+ const { getFeesCharged } = useFeeManager(poolInfo.poolName, poolInfo.poolType)
28
+ const [chargedFees, setChargedFees] = useState(0)
29
+ const [currentAmount, setCurrentAmount] = useState(0)
30
+ const borrowPeriodInSeconds = approveInfo!.intervalInDays * 24 * 60 * 60
31
+
32
+ const getBorrowFlowrateAndAmount = useCallback(
33
+ (borrowAmount: number) => {
34
+ const currentFlowRateBN = toBigNumber(currentFlowRate)
35
+ const borrowAmountBN = toBigNumber(
36
+ upScale(borrowAmount, approveInfo!.tokenDecimal),
37
+ )
38
+
39
+ const totalAmountInBorrowPeriod = currentFlowRateBN.mul(
40
+ borrowPeriodInSeconds,
41
+ )
42
+ let borrowFlowrate = borrowAmountBN
43
+ .mul(currentFlowRateBN)
44
+ .div(totalAmountInBorrowPeriod)
45
+ // To ensure enough flowRate due to round down when convert to string
46
+ .add(1)
47
+
48
+ // Can not borrow all the available flowrate
49
+ if (borrowFlowrate.gte(currentFlowRateBN)) {
50
+ borrowFlowrate = currentFlowRateBN.sub(1)
51
+ }
52
+
53
+ // As borrowFlowrate adds 1, need to calculate the new accurate borrow amount
54
+ const newBorrowAmountBN = borrowFlowrate
55
+ .mul(totalAmountInBorrowPeriod)
56
+ .div(currentFlowRateBN)
57
+ const newBorrowAmount = downScale(
58
+ newBorrowAmountBN,
59
+ approveInfo!.tokenDecimal,
60
+ )
61
+
62
+ return {
63
+ borrowFlowrate: borrowFlowrate.toString(),
64
+ borrowAmount: Number(newBorrowAmount),
65
+ borrowAmountBN: newBorrowAmountBN.toString(),
66
+ }
67
+ },
68
+ [approveInfo, borrowPeriodInSeconds, currentFlowRate],
69
+ )
70
+
71
+ const handleChangeAmount = useCallback(
72
+ (newAmount: number) => {
73
+ if (approveInfo) {
74
+ setCurrentAmount(newAmount)
75
+ setChargedFees(getFeesCharged(newAmount))
76
+ }
77
+ },
78
+ [approveInfo, getFeesCharged],
79
+ )
80
+ const handleAction = useCallback(() => {
81
+ const { borrowFlowrate, borrowAmount, borrowAmountBN } =
82
+ getBorrowFlowrateAndAmount(currentAmount)
83
+
84
+ dispatch(
85
+ setStream({
86
+ borrowFlowrate,
87
+ durationInSeconds: borrowPeriodInSeconds,
88
+ }),
89
+ )
90
+ dispatch(
91
+ setBorrowInfo({
92
+ borrowAmount,
93
+ borrowAmountBN,
94
+ chargedFees,
95
+ nextStep: WIDGET_STEP.ConfirmTransfer,
96
+ }),
97
+ )
98
+ }, [
99
+ borrowPeriodInSeconds,
100
+ chargedFees,
101
+ currentAmount,
102
+ dispatch,
103
+ getBorrowFlowrateAndAmount,
104
+ ])
105
+
106
+ if (!approveInfo) {
107
+ return null
108
+ }
109
+
110
+ return (
111
+ <ChooseAmountModal
112
+ title='Choose Amount'
113
+ description1='Access up to 100% of your stream flowrate'
114
+ sliderMax={Number(approveInfo.creditLimit)}
115
+ currentAmount={currentAmount}
116
+ tokenSymbol={approveInfo.tokenSymbol}
117
+ topLeft='Fees'
118
+ topRight={`${chargedFees} ${approveInfo.tokenSymbol}`}
119
+ handleChangeAmount={handleChangeAmount}
120
+ handleAction={handleAction}
121
+ actionText='Confirm Transfer'
122
+ />
123
+ )
124
+ }
@@ -0,0 +1,104 @@
1
+ import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'
2
+ import { Box, Button, css, Typography, useTheme } from '@mui/material'
3
+ import { useWeb3React } from '@web3-react/core'
4
+ import { CHAINS, isEmpty } from '@huma-finance/shared'
5
+ import React from 'react'
6
+
7
+ import { useAppDispatch, useAppSelector } from '../../../hooks/useRedux'
8
+ import { setStep } from '../../../store/widgets.reducers'
9
+ import { selectWidgetState } from '../../../store/widgets.selectors'
10
+ import { WIDGET_STEP } from '../../../store/widgets.store'
11
+
12
+ export function ConfirmTransfer(): React.ReactElement | null {
13
+ const theme = useTheme()
14
+ const dispatch = useAppDispatch()
15
+ const { chainId } = useWeb3React()
16
+ const { borrowAmount, approveInfo } = useAppSelector(selectWidgetState)
17
+
18
+ const styles = {
19
+ wrapper: css`
20
+ height: 518px;
21
+ position: relative;
22
+ ${theme.cssMixins.colStretch};
23
+ `,
24
+ header: css`
25
+ ${theme.cssMixins.rowHCentered};
26
+ margin-top: -5px;
27
+ `,
28
+ description: css`
29
+ font-weight: 500;
30
+ font-size: 16px;
31
+ color: #49505b;
32
+ margin-top: 30px;
33
+ `,
34
+ transferWrapper: css`
35
+ margin-top: 75px;
36
+ `,
37
+ transfer: css`
38
+ ${theme.cssMixins.rowSpaceBetweened}
39
+ ${theme.cssMixins.rowVCentered}
40
+ height: 72px;
41
+ background: #f9f8fa;
42
+ border-radius: 4px;
43
+ font-weight: 500;
44
+ font-size: 16px;
45
+ color: #49505b;
46
+ padding: 24px 32px;
47
+ margin-bottom: 9px;
48
+ position: relative;
49
+ `,
50
+ arrow: css`
51
+ color: #d2d0d6;
52
+ position: absolute;
53
+ margin: auto;
54
+ top: 0;
55
+ left: 0;
56
+ bottom: 0;
57
+ right: 0;
58
+ `,
59
+ okButton: css`
60
+ width: 100%;
61
+ position: absolute;
62
+ bottom: 0;
63
+ `,
64
+ }
65
+
66
+ const handleAction = () => {
67
+ const step = CHAINS[chainId!].isTestnet
68
+ ? WIDGET_STEP.ApproveAllowance
69
+ : WIDGET_STEP.Permit
70
+ dispatch(setStep(step))
71
+ }
72
+
73
+ if (isEmpty(borrowAmount) || isEmpty(approveInfo)) {
74
+ return null
75
+ }
76
+
77
+ return (
78
+ <Box css={styles.wrapper}>
79
+ <Typography variant='h6' css={styles.header}>
80
+ Confirm transaction details
81
+ </Typography>
82
+ <Box css={styles.description}>
83
+ Huma keeps your Tradable Stream NFT as collateral until it's fully paid.
84
+ </Box>
85
+ <Box css={styles.transferWrapper}>
86
+ <Box css={styles.transfer}>
87
+ <Box>Tradable Stream</Box>
88
+ <ArrowForwardIosIcon css={styles.arrow} />
89
+ <Box>Huma</Box>
90
+ </Box>
91
+ <Box css={styles.transfer}>
92
+ <Box>
93
+ {borrowAmount!.toFixed(2)} {approveInfo?.tokenSymbol}
94
+ </Box>
95
+ <ArrowForwardIosIcon css={styles.arrow} />
96
+ <Box>You</Box>
97
+ </Box>
98
+ </Box>
99
+ <Button variant='contained' css={styles.okButton} onClick={handleAction}>
100
+ Approve Transfer
101
+ </Button>
102
+ </Box>
103
+ )
104
+ }
@@ -0,0 +1,65 @@
1
+ import { useWeb3React } from '@web3-react/core'
2
+ import {
3
+ PoolInfoType,
4
+ toBigNumber,
5
+ upScale,
6
+ usePoolAllowance,
7
+ } from '@huma-finance/shared'
8
+ import React, { useCallback } from 'react'
9
+ import { useDispatch } from 'react-redux'
10
+
11
+ import { useAppSelector } from '../../../hooks/useRedux'
12
+ import { setStep } from '../../../store/widgets.reducers'
13
+ import { selectWidgetState } from '../../../store/widgets.selectors'
14
+ import { WIDGET_STEP } from '../../../store/widgets.store'
15
+ import { ApproveAllowanceModal } from '../../ApproveAllowanceModal'
16
+ import { LoadingModal } from '../../LoadingModal'
17
+
18
+ type Props = {
19
+ poolInfo: PoolInfoType
20
+ }
21
+
22
+ export function ApproveAllowance({
23
+ poolInfo,
24
+ }: Props): React.ReactElement | null {
25
+ const dispatch = useDispatch()
26
+ const { account } = useWeb3React()
27
+ const { borrowAmount, approveInfo } = useAppSelector(selectWidgetState)
28
+ const { allowance, loaded } = usePoolAllowance(
29
+ poolInfo.poolName,
30
+ poolInfo.poolType,
31
+ account,
32
+ )
33
+
34
+ const getNewAllowance = () => {
35
+ if (!borrowAmount || !loaded) {
36
+ return null
37
+ }
38
+ return toBigNumber(
39
+ upScale(borrowAmount, poolInfo.poolUnderlyingToken.decimals),
40
+ ).add(allowance)
41
+ }
42
+
43
+ const handleSuccess = useCallback(() => {
44
+ dispatch(setStep(WIDGET_STEP.Permit))
45
+ }, [dispatch])
46
+
47
+ const newAllowance = getNewAllowance()
48
+
49
+ if (!newAllowance) {
50
+ return (
51
+ <LoadingModal
52
+ title={`Approve ${approveInfo?.tokenSymbol}`}
53
+ description='Waiting for approval confirmation...'
54
+ />
55
+ )
56
+ }
57
+
58
+ return (
59
+ <ApproveAllowanceModal
60
+ poolInfo={poolInfo}
61
+ allowanceAmount={newAllowance}
62
+ handleSuccess={handleSuccess}
63
+ />
64
+ )
65
+ }
@@ -0,0 +1,144 @@
1
+ import { Contract, ethers } from 'ethers'
2
+ import {
3
+ CHAINS,
4
+ Erc2612,
5
+ PoolInfoType,
6
+ StreamFactoringPool,
7
+ useERC2612Permit,
8
+ useMount,
9
+ } from '@huma-finance/shared'
10
+ import ERC2612Abi from '@huma-finance/shared/dist/abis/erc2612.json'
11
+ import PoolAbi from '@huma-finance/shared/dist/abis/StreamFactoringPool.json'
12
+ import React from 'react'
13
+ import { useDispatch } from 'react-redux'
14
+
15
+ import { useAppSelector } from '../../../hooks/useRedux'
16
+ import {
17
+ setError,
18
+ setMultisend,
19
+ setStep,
20
+ } from '../../../store/widgets.reducers'
21
+ import { selectWidgetState } from '../../../store/widgets.selectors'
22
+ import { WIDGET_STEP } from '../../../store/widgets.store'
23
+ import { LoadingModal } from '../../LoadingModal'
24
+
25
+ type Props = {
26
+ poolInfo: PoolInfoType
27
+ payerAddress: string
28
+ chainId: number
29
+ borrower: string
30
+ superToken: string
31
+ }
32
+
33
+ export function Permit({
34
+ poolInfo,
35
+ payerAddress,
36
+ chainId,
37
+ borrower,
38
+ superToken,
39
+ }: Props): React.ReactElement | null {
40
+ const dispatch = useDispatch()
41
+ const { address: tokenAddress } = poolInfo.poolUnderlyingToken
42
+ const { borrowAmountBN, stream } = useAppSelector(selectWidgetState)
43
+ const { getERC2612PermitMessage, getTradableStreamPermitMessage } =
44
+ useERC2612Permit()
45
+
46
+ const getApproveAllowanceCallData = async () => {
47
+ const erc2612PermitResult = await getERC2612PermitMessage(
48
+ tokenAddress,
49
+ poolInfo.pool,
50
+ borrowAmountBN!,
51
+ )
52
+ const tokenContract = new Contract(tokenAddress, ERC2612Abi) as Erc2612
53
+ return tokenContract.interface.encodeFunctionData('permit', [
54
+ poolInfo.pool,
55
+ borrowAmountBN!,
56
+ erc2612PermitResult.nonce,
57
+ erc2612PermitResult.deadline,
58
+ erc2612PermitResult.v,
59
+ erc2612PermitResult.r,
60
+ erc2612PermitResult.s,
61
+ ])
62
+ }
63
+
64
+ const getDrawdownCallData = async () => {
65
+ const NFTPermitResult = await getTradableStreamPermitMessage(
66
+ poolInfo.assetAddress!,
67
+ borrower,
68
+ superToken,
69
+ payerAddress,
70
+ poolInfo.pool,
71
+ stream!.borrowFlowrate!,
72
+ stream!.durationInSeconds!,
73
+ )
74
+
75
+ const drawdownEncodedData = ethers.utils.defaultAbiCoder.encode(
76
+ [
77
+ 'address',
78
+ 'address',
79
+ 'address',
80
+ 'int96',
81
+ 'uint256',
82
+ 'uint256',
83
+ 'uint8',
84
+ 'bytes32',
85
+ 'bytes32',
86
+ ],
87
+ [
88
+ borrower,
89
+ superToken,
90
+ payerAddress,
91
+ stream?.borrowFlowrate,
92
+ stream?.durationInSeconds,
93
+ NFTPermitResult.expiry,
94
+ NFTPermitResult.v,
95
+ NFTPermitResult.r,
96
+ NFTPermitResult.s,
97
+ ],
98
+ )
99
+ const poolContract = new Contract(
100
+ poolInfo.pool,
101
+ PoolAbi,
102
+ ) as StreamFactoringPool
103
+
104
+ return poolContract.interface.encodeFunctionData(
105
+ 'drawdownWithAuthorization',
106
+ [borrowAmountBN!, poolInfo.assetAddress!, drawdownEncodedData],
107
+ )
108
+ }
109
+
110
+ useMount(async () => {
111
+ try {
112
+ const callData = []
113
+ const addresses: string[] = []
114
+
115
+ // The token in Goerli hasn't integrated ERC-2612
116
+ if (!CHAINS[chainId].isTestnet) {
117
+ const approveAllowanceCallData = await getApproveAllowanceCallData()
118
+ addresses.push(tokenAddress)
119
+ callData.push(approveAllowanceCallData)
120
+ }
121
+
122
+ const drawdownCalldata = await getDrawdownCallData()
123
+ addresses.push(poolInfo.pool)
124
+ callData.push(drawdownCalldata)
125
+
126
+ dispatch(setMultisend({ addresses, callData }))
127
+ dispatch(setStep(WIDGET_STEP.Transfer))
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ } catch (e: any) {
130
+ let errorMessage = e.reason || `Sign permit failed`
131
+ if (e.code === 4001 || e.code === 'ACTION_REJECTED') {
132
+ errorMessage = 'User has rejected the permit sign'
133
+ }
134
+ dispatch(setError({ errorMessage, errorReason: errorMessage }))
135
+ }
136
+ })
137
+
138
+ return (
139
+ <LoadingModal
140
+ title='Sign Token and NFT'
141
+ description='Waiting for signing...'
142
+ />
143
+ )
144
+ }
@@ -0,0 +1,37 @@
1
+ import { PoolInfoType, useMultiSendContract } from '@huma-finance/shared'
2
+ import React, { useCallback } from 'react'
3
+
4
+ import { useAppDispatch, useAppSelector } from '../../../hooks/useRedux'
5
+ import { setStep } from '../../../store/widgets.reducers'
6
+ import { selectWidgetState } from '../../../store/widgets.selectors'
7
+ import { WIDGET_STEP } from '../../../store/widgets.store'
8
+ import { TxSendModal } from '../../TxSendModal'
9
+
10
+ type Props = {
11
+ poolInfo: PoolInfoType
12
+ }
13
+
14
+ export function Transfer({ poolInfo }: Props): React.ReactElement | null {
15
+ const dispatch = useAppDispatch()
16
+ const multiSendContract = useMultiSendContract()
17
+ const { multisend } = useAppSelector(selectWidgetState)
18
+
19
+ const handleSuccess = useCallback(() => {
20
+ dispatch(setStep(WIDGET_STEP.Done))
21
+ }, [dispatch])
22
+
23
+ if (!multisend || !multiSendContract) {
24
+ return null
25
+ }
26
+
27
+ return (
28
+ <TxSendModal
29
+ poolInfo={poolInfo}
30
+ contract={multiSendContract}
31
+ title='Transfer Tradable Stream and Money'
32
+ method='multisend'
33
+ params={[multisend.addresses, multisend.callData]}
34
+ handleSuccess={handleSuccess}
35
+ />
36
+ )
37
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import { useAppSelector } from '../../../hooks/useRedux'
3
+ import { selectWidgetState } from '../../../store/widgets.selectors'
4
+ import { TxDoneModal } from '../../TxDoneModal'
5
+
6
+ type Props = {
7
+ handleAction: () => void
8
+ }
9
+
10
+ export function Success({ handleAction }: Props): React.ReactElement {
11
+ const { borrowAmount, approveInfo } = useAppSelector(selectWidgetState)
12
+
13
+ const content = [
14
+ `${borrowAmount?.toFixed(2)} ${
15
+ approveInfo?.tokenSymbol
16
+ } is now in your wallet.`,
17
+ ]
18
+
19
+ return <TxDoneModal handleAction={handleAction} content={content} />
20
+ }