@huma-finance/widgets 0.0.62-beta.633 → 0.0.62-beta.635

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 (43) hide show
  1. package/API.md +195 -0
  2. package/dist/cjs/index.cjs +1871 -299
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/index.d.ts +138 -2
  5. package/dist/index.js +1869 -302
  6. package/dist/index.js.map +1 -1
  7. package/package.json +18 -8
  8. package/src/components/CreditLine/paymentV2/1-ChooseAmount.tsx +1 -0
  9. package/src/components/CreditLine/solanaBorrow/1-ChooseAmount.tsx +67 -0
  10. package/src/components/CreditLine/solanaBorrow/2-Transfer.tsx +159 -0
  11. package/src/components/CreditLine/solanaBorrow/3-Done.tsx +38 -0
  12. package/src/components/CreditLine/solanaBorrow/index.tsx +93 -0
  13. package/src/components/CreditLine/solanaPayment/1-ChooseAmount.tsx +75 -0
  14. package/src/components/CreditLine/solanaPayment/2-Transfer.tsx +92 -0
  15. package/src/components/CreditLine/solanaPayment/3-Done.tsx +60 -0
  16. package/src/components/CreditLine/solanaPayment/index.tsx +86 -0
  17. package/src/components/InputAmountModal.tsx +13 -2
  18. package/src/components/Lend/{supplyV2/components → components}/PersonaEvaluation.tsx +19 -13
  19. package/src/components/Lend/{supplyV2/components → components}/SecuritizeEvaluation.tsx +7 -8
  20. package/src/components/Lend/solanaAddRedemption/1-ChooseTranche.tsx +114 -0
  21. package/src/components/Lend/solanaAddRedemption/2-ChooseAmount.tsx +183 -0
  22. package/src/components/Lend/solanaAddRedemption/3-Transfer.tsx +102 -0
  23. package/src/components/Lend/solanaAddRedemption/4-Done.tsx +29 -0
  24. package/src/components/Lend/solanaAddRedemption/index.tsx +155 -0
  25. package/src/components/Lend/solanaCancelRedemption/1-ChooseTranche.tsx +114 -0
  26. package/src/components/Lend/solanaCancelRedemption/2-Transfer.tsx +99 -0
  27. package/src/components/Lend/solanaCancelRedemption/3-Done.tsx +55 -0
  28. package/src/components/Lend/solanaCancelRedemption/index.tsx +162 -0
  29. package/src/components/Lend/solanaSupply/1-Evaluation.tsx +43 -0
  30. package/src/components/Lend/solanaSupply/2-ChooseTranche.tsx +116 -0
  31. package/src/components/Lend/solanaSupply/3-ChooseAmount.tsx +131 -0
  32. package/src/components/Lend/solanaSupply/4-Transfer.tsx +137 -0
  33. package/src/components/Lend/solanaSupply/5-Success.tsx +52 -0
  34. package/src/components/Lend/solanaSupply/index.tsx +155 -0
  35. package/src/components/Lend/supplyV2/2-Evaluation.tsx +4 -3
  36. package/src/components/Lend/supplyV2/6-Success.tsx +3 -3
  37. package/src/components/SolanaTxDoneModal.tsx +119 -0
  38. package/src/components/SolanaTxSendModal.tsx +61 -0
  39. package/src/components/SolanaViewOnExplorer.tsx +29 -0
  40. package/src/components/WidgetWrapper.tsx +6 -4
  41. package/src/index.tsx +139 -0
  42. package/src/store/widgets.reducers.ts +5 -0
  43. package/src/store/widgets.store.ts +1 -0
@@ -0,0 +1,86 @@
1
+ import { SolanaPoolInfo } from '@huma-finance/shared'
2
+ import { useBorrowerAccounts } from '@huma-finance/web-shared'
3
+ import React, { useEffect } from 'react'
4
+ import { useDispatch } from 'react-redux'
5
+
6
+ import { useAppSelector } from '../../../hooks/useRedux'
7
+ import { selectWidgetState } from '../../../store/widgets.selectors'
8
+ import { WidgetWrapper } from '../../WidgetWrapper'
9
+ import { WIDGET_STEP } from '../../../store/widgets.store'
10
+ import { setStep } from '../../../store/widgets.reducers'
11
+ import { ErrorModal } from '../../ErrorModal'
12
+ import { ChooseAmount } from './1-ChooseAmount'
13
+ import { Transfer } from './2-Transfer'
14
+ import { Done } from './3-Done'
15
+
16
+ /**
17
+ * Lend pool supply props
18
+ * @typedef {Object} SolanaPaymentProps
19
+ * @property {SolanaPoolInfo} poolInfo The metadata of the pool.
20
+ * @property {function():void|undefined} handleSuccess Optional function to notify that the lending pool supply action is successful.
21
+ */
22
+ export interface SolanaPaymentProps {
23
+ poolInfo: SolanaPoolInfo
24
+ handleClose: () => void
25
+ handleSuccess?: () => void
26
+ }
27
+
28
+ export function SolanaPayment({
29
+ poolInfo,
30
+ handleClose,
31
+ handleSuccess,
32
+ }: SolanaPaymentProps): React.ReactElement | null {
33
+ const title = 'Make Payment'
34
+ const dispatch = useDispatch()
35
+ const { step, errorMessage } = useAppSelector(selectWidgetState)
36
+ const { creditStateAccount } = useBorrowerAccounts(
37
+ poolInfo.chainId,
38
+ poolInfo.poolName,
39
+ )
40
+
41
+ useEffect(() => {
42
+ if (!step && creditStateAccount) {
43
+ dispatch(setStep(WIDGET_STEP.ChooseAmount))
44
+ }
45
+ }, [creditStateAccount, dispatch, step])
46
+
47
+ if (!creditStateAccount) {
48
+ return (
49
+ <WidgetWrapper
50
+ isOpen
51
+ isLoading
52
+ loadingTitle={title}
53
+ handleClose={handleClose}
54
+ handleSuccess={handleSuccess}
55
+ />
56
+ )
57
+ }
58
+
59
+ return (
60
+ <WidgetWrapper
61
+ isOpen
62
+ loadingTitle={title}
63
+ handleClose={handleClose}
64
+ handleSuccess={handleSuccess}
65
+ >
66
+ {step === WIDGET_STEP.ChooseAmount && creditStateAccount && (
67
+ <ChooseAmount
68
+ poolInfo={poolInfo}
69
+ creditStateAccount={creditStateAccount}
70
+ />
71
+ )}
72
+ {step === WIDGET_STEP.Transfer && <Transfer poolInfo={poolInfo} />}
73
+ {step === WIDGET_STEP.Done && (
74
+ <Done poolInfo={poolInfo} handleAction={handleClose} />
75
+ )}
76
+ {step === WIDGET_STEP.Error && (
77
+ <ErrorModal
78
+ title={title}
79
+ errorReason='Sorry there was an error'
80
+ errorMessage={errorMessage}
81
+ handleOk={handleClose}
82
+ />
83
+ )}
84
+ </WidgetWrapper>
85
+ )
86
+ }
@@ -20,6 +20,7 @@ type Props = {
20
20
  tokenSymbol: string
21
21
  suffix?: string
22
22
  currentAmount: number | string
23
+ minimumAmount?: number
23
24
  maxAmount: number | string
24
25
  maxAmountText?: string
25
26
  maxAmountTitle?: string
@@ -35,6 +36,7 @@ export function InputAmountModal({
35
36
  tokenSymbol,
36
37
  suffix: defaultSuffix,
37
38
  currentAmount,
39
+ minimumAmount,
38
40
  maxAmount,
39
41
  maxAmountText = 'MAX',
40
42
  maxAmountTitle,
@@ -91,6 +93,7 @@ export function InputAmountModal({
91
93
  font-size: 13px;
92
94
  line-height: 22px;
93
95
  letter-spacing: 0.46px;
96
+ white-space: nowrap;
94
97
  `,
95
98
  info: css`
96
99
  color: ${theme.palette.text.secondary};
@@ -125,6 +128,9 @@ export function InputAmountModal({
125
128
  if (!isEmpty(maxAmount) && Number(currentAmount) > Number(maxAmount)) {
126
129
  return true
127
130
  }
131
+ if (!isEmpty(minimumAmount) && Number(currentAmount) < minimumAmount!) {
132
+ return true
133
+ }
128
134
  return false
129
135
  }
130
136
 
@@ -157,7 +163,6 @@ export function InputAmountModal({
157
163
  />
158
164
  </Box>
159
165
  </Box>
160
-
161
166
  {infos && (
162
167
  <Box>
163
168
  {infos.map((info) => (
@@ -167,7 +172,13 @@ export function InputAmountModal({
167
172
  ))}
168
173
  </Box>
169
174
  )}
170
-
175
+ {!!minimumAmount && minimumAmount > 0 && (
176
+ <Box>
177
+ <Typography css={styles.info} key='minDeposit'>
178
+ Minimum deposit: {minimumAmount} {tokenSymbol}
179
+ </Typography>
180
+ </Box>
181
+ )}
171
182
  <BottomButton
172
183
  variant='contained'
173
184
  onClick={handleAction}
@@ -1,33 +1,38 @@
1
1
  import {
2
2
  CAMPAIGN_REFERENCE_CODE,
3
3
  CampaignService,
4
+ CHAIN_TYPE,
4
5
  checkIsDev,
5
6
  IdentityServiceV2,
6
7
  IdentityVerificationStatusV2,
7
8
  KYCCopy,
8
- PoolInfoV2,
9
+ KYCType,
9
10
  TrancheType,
10
11
  VerificationStatusResultV2,
11
12
  } from '@huma-finance/shared'
12
- import { useAuthErrorHandling } from '@huma-finance/web-shared'
13
+ import { useAuthErrorHandling, useChainInfo } from '@huma-finance/web-shared'
13
14
  import { Box, css, useTheme } from '@mui/material'
14
- import { useWeb3React } from '@web3-react/core'
15
15
  import Persona, { Client } from 'persona'
16
16
  import React, { useCallback, useEffect, useRef, useState } from 'react'
17
17
 
18
- import { Campaign } from '..'
19
- import { useAppDispatch } from '../../../../hooks/useRedux'
20
- import { setError, setStep } from '../../../../store/widgets.reducers'
21
- import { WIDGET_STEP } from '../../../../store/widgets.store'
22
- import { BottomButton } from '../../../BottomButton'
23
- import { ApproveLenderImg } from '../../../images'
24
- import { LoadingModal } from '../../../LoadingModal'
25
- import { WrapperModal } from '../../../WrapperModal'
18
+ import { useAppDispatch } from '../../../hooks/useRedux'
19
+ import { setError, setStep } from '../../../store/widgets.reducers'
20
+ import { WIDGET_STEP } from '../../../store/widgets.store'
21
+ import { BottomButton } from '../../BottomButton'
22
+ import { ApproveLenderImg } from '../../images'
23
+ import { LoadingModal } from '../../LoadingModal'
24
+ import { WrapperModal } from '../../WrapperModal'
25
+ import { Campaign } from '../supplyV2'
26
26
 
27
27
  type LoadingType = 'verificationStatus' | 'startKYC' | 'approveLender'
28
28
 
29
29
  type Props = {
30
- poolInfo: PoolInfoV2
30
+ poolInfo: {
31
+ KYC?: KYCType
32
+ juniorTrancheVault: string
33
+ seniorTrancheVault: string
34
+ }
35
+ chainType: CHAIN_TYPE
31
36
  isUniTranche: boolean
32
37
  pointsTestnetExperience: boolean
33
38
  campaign?: Campaign
@@ -40,13 +45,14 @@ export function PersonaEvaluation({
40
45
  isUniTranche,
41
46
  campaign,
42
47
  pointsTestnetExperience,
48
+ chainType,
43
49
  changeTranche,
44
50
  handleClose,
45
51
  }: Props): React.ReactElement | null {
46
52
  const theme = useTheme()
47
53
  const isDev = checkIsDev()
48
54
  const dispatch = useAppDispatch()
49
- const { account, chainId } = useWeb3React()
55
+ const { account, chainId } = useChainInfo(isDev, chainType)
50
56
  const {
51
57
  errorType,
52
58
  setError: setAuthError,
@@ -14,14 +14,13 @@ import { useAuthErrorHandling, useParamsSearch } from '@huma-finance/web-shared'
14
14
  import { Box, css, useTheme } from '@mui/material'
15
15
  import { useWeb3React } from '@web3-react/core'
16
16
  import React, { useCallback, useEffect, useMemo, useState } from 'react'
17
-
18
- import { useAppDispatch } from '../../../../hooks/useRedux'
19
- import { setError } from '../../../../store/widgets.reducers'
20
- import { BottomButton } from '../../../BottomButton'
21
- import { HumaSnackBar } from '../../../HumaSnackBar'
22
- import { ApproveLenderImg } from '../../../images'
23
- import { LoadingModal } from '../../../LoadingModal'
24
- import { WrapperModal } from '../../../WrapperModal'
17
+ import { useAppDispatch } from '../../../hooks/useRedux'
18
+ import { setError } from '../../../store/widgets.reducers'
19
+ import { HumaSnackBar } from '../../HumaSnackBar'
20
+ import { WrapperModal } from '../../WrapperModal'
21
+ import { ApproveLenderImg } from '../../images'
22
+ import { BottomButton } from '../../BottomButton'
23
+ import { LoadingModal } from '../../LoadingModal'
25
24
 
26
25
  const LoadingCopiesByType: {
27
26
  [key: string]: {
@@ -0,0 +1,114 @@
1
+ import { TrancheType } from '@huma-finance/shared'
2
+ import {
3
+ css,
4
+ FormControl,
5
+ FormControlLabel,
6
+ FormLabel,
7
+ Radio,
8
+ RadioGroup,
9
+ useTheme,
10
+ } from '@mui/material'
11
+ import React from 'react'
12
+
13
+ import { useAppDispatch } from '../../../hooks/useRedux'
14
+ import { setStep } from '../../../store/widgets.reducers'
15
+ import { WIDGET_STEP } from '../../../store/widgets.store'
16
+ import { BottomButton } from '../../BottomButton'
17
+ import { WrapperModal } from '../../WrapperModal'
18
+
19
+ type Props = {
20
+ selectedTranche: TrancheType | undefined
21
+ changeTranche: (tranche: TrancheType) => void
22
+ }
23
+
24
+ export function ChooseTranche({
25
+ selectedTranche,
26
+ changeTranche,
27
+ }: Props): React.ReactElement | null {
28
+ const theme = useTheme()
29
+ const dispatch = useAppDispatch()
30
+
31
+ const styles = {
32
+ subTitle: css`
33
+ color: ${theme.palette.text.primary} !important;
34
+ font-weight: 400;
35
+ font-size: 16px;
36
+ line-height: 150%;
37
+ letter-spacing: 0.15px;
38
+ margin-top: ${theme.spacing(5)};
39
+ margin-left: ${theme.spacing(2)};
40
+ `,
41
+ radioGroup: css`
42
+ margin-left: ${theme.spacing(3)};
43
+ margin-top: ${theme.spacing(3)};
44
+ `,
45
+ formControl: css`
46
+ margin-bottom: ${theme.spacing(1)};
47
+
48
+ .MuiFormControlLabel-label {
49
+ color: ${theme.palette.text.primary};
50
+ font-weight: 400;
51
+ font-size: 16px;
52
+ line-height: 150%;
53
+ letter-spacing: 0.15px;
54
+ }
55
+ `,
56
+ }
57
+
58
+ const handleNext = () => {
59
+ dispatch(setStep(WIDGET_STEP.ChooseAmount))
60
+ }
61
+
62
+ const items: {
63
+ label: string
64
+ value: TrancheType
65
+ }[] = [
66
+ {
67
+ label: 'Senior',
68
+ value: 'senior',
69
+ },
70
+ {
71
+ label: 'Junior',
72
+ value: 'junior',
73
+ },
74
+ ]
75
+
76
+ return (
77
+ <WrapperModal title='Redemption'>
78
+ <FormControl>
79
+ <FormLabel css={styles.subTitle}>Select Tranche Type</FormLabel>
80
+ <RadioGroup
81
+ aria-labelledby='buttons-group-label'
82
+ name='radio-buttons-group'
83
+ css={styles.radioGroup}
84
+ onChange={(e) => changeTranche(e.target.value as TrancheType)}
85
+ >
86
+ {items.map((item) => (
87
+ <FormControlLabel
88
+ key={item.label}
89
+ value={item.value}
90
+ control={
91
+ <Radio
92
+ sx={{
93
+ '& .MuiSvgIcon-root': {
94
+ fontSize: 24,
95
+ },
96
+ }}
97
+ />
98
+ }
99
+ label={item.label}
100
+ css={styles.formControl}
101
+ />
102
+ ))}
103
+ </RadioGroup>
104
+ </FormControl>
105
+ <BottomButton
106
+ variant='contained'
107
+ disabled={!selectedTranche}
108
+ onClick={handleNext}
109
+ >
110
+ NEXT
111
+ </BottomButton>
112
+ </WrapperModal>
113
+ )
114
+ }
@@ -0,0 +1,183 @@
1
+ import {
2
+ formatNumber,
3
+ SolanaPoolInfo,
4
+ SolanaTokenUtils,
5
+ timestampToLL,
6
+ TrancheType,
7
+ } from '@huma-finance/shared'
8
+ import {
9
+ LenderStateAccount,
10
+ SolanaPoolState,
11
+ useTrancheMintAccounts,
12
+ useTrancheTokenAccounts,
13
+ } from '@huma-finance/web-shared'
14
+ import dayjs from 'dayjs'
15
+ import React, { useEffect, useMemo, useState } from 'react'
16
+
17
+ import { BN } from '@coral-xyz/anchor'
18
+ import { useAppDispatch } from '../../../hooks/useRedux'
19
+ import {
20
+ setError,
21
+ setRedeemAmount,
22
+ setRedeemShares,
23
+ setStep,
24
+ } from '../../../store/widgets.reducers'
25
+ import { WIDGET_STEP } from '../../../store/widgets.store'
26
+ import { InputAmountModal } from '../../InputAmountModal'
27
+ import { LoadingModal } from '../../LoadingModal'
28
+
29
+ type Props = {
30
+ poolInfo: SolanaPoolInfo
31
+ poolState: SolanaPoolState
32
+ selectedTranche: TrancheType
33
+ lenderState: LenderStateAccount | null | undefined
34
+ }
35
+
36
+ export function ChooseAmount({
37
+ poolInfo,
38
+ poolState,
39
+ selectedTranche,
40
+ lenderState,
41
+ }: Props): React.ReactElement {
42
+ const dispatch = useAppDispatch()
43
+ const { symbol } = poolInfo.underlyingMint
44
+ const [currentAmount, setCurrentAmount] = useState<number>(0)
45
+ const [currentShares, setCurrentShares] = useState<number>(0)
46
+ const { mintAccount, loading: isLoadingTrancheMintAccounts } =
47
+ useTrancheMintAccounts(poolInfo, selectedTranche)
48
+ const {
49
+ seniorTokenAccount,
50
+ juniorTokenAccount,
51
+ loading: isLoadingTokenAccounts,
52
+ } = useTrancheTokenAccounts(poolInfo)
53
+
54
+ const maxSharesFormatted = useMemo(() => {
55
+ if (!mintAccount?.decimals) {
56
+ return 0
57
+ }
58
+
59
+ return SolanaTokenUtils.formatUnits(
60
+ selectedTranche === 'senior'
61
+ ? new BN(seniorTokenAccount?.amount?.toString() ?? 0)
62
+ : new BN(juniorTokenAccount?.amount?.toString() ?? 0),
63
+ mintAccount.decimals,
64
+ )
65
+ }, [
66
+ mintAccount,
67
+ juniorTokenAccount?.amount,
68
+ selectedTranche,
69
+ seniorTokenAccount?.amount,
70
+ ])
71
+
72
+ const sharePrice = useMemo(() => {
73
+ if (!mintAccount?.decimals || !mintAccount?.supply) {
74
+ return 0
75
+ }
76
+
77
+ const mintSupplyFormatted = Number(
78
+ SolanaTokenUtils.formatUnits(
79
+ new BN(mintAccount.supply.toString()),
80
+ mintAccount.decimals,
81
+ ),
82
+ )
83
+
84
+ if (mintSupplyFormatted === 0) {
85
+ return 0
86
+ }
87
+
88
+ const seniorTrancheAssetsFormatted = Number(
89
+ SolanaTokenUtils.formatUnits(
90
+ new BN(poolState?.seniorTrancheAssets ?? 0),
91
+ mintAccount.decimals,
92
+ ),
93
+ )
94
+
95
+ if (selectedTranche === 'senior') {
96
+ return seniorTrancheAssetsFormatted / mintSupplyFormatted
97
+ }
98
+
99
+ const juniorTrancheAssetsFormatted = Number(
100
+ SolanaTokenUtils.formatUnits(
101
+ new BN(poolState?.juniorTrancheAssets ?? 0),
102
+ mintAccount.decimals,
103
+ ),
104
+ )
105
+
106
+ return juniorTrancheAssetsFormatted / mintSupplyFormatted
107
+ }, [
108
+ mintAccount?.decimals,
109
+ mintAccount?.supply,
110
+ poolState?.juniorTrancheAssets,
111
+ poolState?.seniorTrancheAssets,
112
+ selectedTranche,
113
+ ])
114
+
115
+ useEffect(() => {
116
+ if (
117
+ poolState?.withdrawalLockupPeriodDays &&
118
+ poolState?.epochEndTime &&
119
+ lenderState
120
+ ) {
121
+ const SECONDS_IN_A_DAY = 24 * 60 * 60
122
+ const lastDepositTime = Number(lenderState.depositRecord.lastDepositTime)
123
+ const lockupEndTime =
124
+ lastDepositTime +
125
+ poolState.withdrawalLockupPeriodDays * SECONDS_IN_A_DAY
126
+ if (poolState.epochEndTime < lockupEndTime) {
127
+ const lockupEndTimeDayjs = dayjs.unix(lockupEndTime).date(1)
128
+ dispatch(setStep(WIDGET_STEP.Error))
129
+ dispatch(
130
+ setError({
131
+ errorReason: 'Redemption too soon',
132
+ errorMessage: `Your last deposit was on ${timestampToLL(
133
+ lastDepositTime,
134
+ )}. You can redeem on ${timestampToLL(lockupEndTimeDayjs.unix())}.`,
135
+ }),
136
+ )
137
+ }
138
+ }
139
+ }, [
140
+ dispatch,
141
+ lenderState,
142
+ poolState.epochEndTime,
143
+ poolState.withdrawalLockupPeriodDays,
144
+ ])
145
+
146
+ const handleChangeShares = (newShares: number) => {
147
+ const amount = Number(newShares) * sharePrice
148
+ setCurrentShares(Number(newShares))
149
+ setCurrentAmount(amount)
150
+ dispatch(setRedeemShares(Number(newShares)))
151
+ dispatch(setRedeemAmount(amount))
152
+ }
153
+
154
+ const handleAction = () => {
155
+ dispatch(setStep(WIDGET_STEP.Transfer))
156
+ }
157
+
158
+ if (
159
+ isLoadingTokenAccounts ||
160
+ isLoadingTrancheMintAccounts ||
161
+ !lenderState ||
162
+ poolState?.withdrawalLockupPeriodDays === undefined || // withdrawalLockupPeriodDays can be 0
163
+ !poolState?.epochEndTime
164
+ ) {
165
+ return <LoadingModal title='Redemption' />
166
+ }
167
+
168
+ return (
169
+ <InputAmountModal
170
+ title='Redemption'
171
+ subTitle={`Enter number of shares to redeem from the ${selectedTranche} tranche`}
172
+ tokenSymbol={symbol}
173
+ currentAmount={currentShares}
174
+ handleChangeAmount={handleChangeShares}
175
+ maxAmount={maxSharesFormatted}
176
+ infos={[`${String(formatNumber(currentAmount))} ${symbol}`]}
177
+ maxAmountTitle={`${formatNumber(maxSharesFormatted)} Shares`}
178
+ suffix='Shares'
179
+ handleAction={handleAction}
180
+ actionText='REQUEST'
181
+ />
182
+ )
183
+ }
@@ -0,0 +1,102 @@
1
+ import {
2
+ getTokenAccounts,
3
+ SolanaPoolInfo,
4
+ SolanaTokenUtils,
5
+ TrancheType,
6
+ } from '@huma-finance/shared'
7
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
8
+
9
+ import { TOKEN_2022_PROGRAM_ID } from '@solana/spl-token'
10
+ import { useWallet } from '@solana/wallet-adapter-react'
11
+ import {
12
+ useHumaProgram,
13
+ useTrancheMintAccounts,
14
+ } from '@huma-finance/web-shared'
15
+ import { Transaction } from '@solana/web3.js'
16
+ import { useAppDispatch, useAppSelector } from '../../../hooks/useRedux'
17
+ import { setStep } from '../../../store/widgets.reducers'
18
+ import { WIDGET_STEP } from '../../../store/widgets.store'
19
+ import { selectWidgetState } from '../../../store/widgets.selectors'
20
+ import { SolanaTxSendModal } from '../../SolanaTxSendModal'
21
+
22
+ type Props = {
23
+ poolInfo: SolanaPoolInfo
24
+ selectedTranche: TrancheType
25
+ }
26
+
27
+ export function Transfer({
28
+ poolInfo,
29
+ selectedTranche,
30
+ }: Props): React.ReactElement | null {
31
+ const dispatch = useAppDispatch()
32
+ const { publicKey } = useWallet()
33
+ const { redeemShares } = useAppSelector(selectWidgetState)
34
+ const { mintAccount } = useTrancheMintAccounts(poolInfo, selectedTranche)
35
+ const [transaction, setTransaction] = useState<Transaction>()
36
+ const program = useHumaProgram(poolInfo.chainId)
37
+ const redeemBN = useMemo(() => {
38
+ if (!mintAccount) {
39
+ return null
40
+ }
41
+ return SolanaTokenUtils.parseUnits(
42
+ String(redeemShares),
43
+ mintAccount?.decimals,
44
+ )
45
+ }, [mintAccount, redeemShares])
46
+
47
+ const handleSuccess = useCallback(() => {
48
+ dispatch(setStep(WIDGET_STEP.Done))
49
+ }, [dispatch])
50
+
51
+ useEffect(() => {
52
+ async function getTx() {
53
+ if (!publicKey || !redeemBN || transaction) {
54
+ return
55
+ }
56
+
57
+ const {
58
+ seniorTrancheATA,
59
+ juniorTrancheATA,
60
+ poolSeniorTrancheATA,
61
+ poolJuniorTrancheATA,
62
+ } = getTokenAccounts(poolInfo, publicKey)
63
+ const tx = await program.methods
64
+ .addRedemptionRequest(redeemBN)
65
+ .accountsPartial({
66
+ lender: publicKey,
67
+ humaConfig: poolInfo.humaConfig,
68
+ poolConfig: poolInfo.poolConfig,
69
+ trancheMint:
70
+ selectedTranche === 'senior'
71
+ ? poolInfo.seniorTrancheMint
72
+ : poolInfo.juniorTrancheMint,
73
+ poolTrancheToken:
74
+ selectedTranche === 'senior'
75
+ ? poolSeniorTrancheATA
76
+ : poolJuniorTrancheATA,
77
+ lenderTrancheToken:
78
+ selectedTranche === 'senior' ? seniorTrancheATA : juniorTrancheATA,
79
+ tokenProgram: TOKEN_2022_PROGRAM_ID,
80
+ })
81
+ .transaction()
82
+
83
+ setTransaction(tx)
84
+ }
85
+ getTx()
86
+ }, [
87
+ poolInfo,
88
+ program.methods,
89
+ publicKey,
90
+ redeemBN,
91
+ selectedTranche,
92
+ transaction,
93
+ ])
94
+
95
+ return (
96
+ <SolanaTxSendModal
97
+ tx={transaction}
98
+ chainId={poolInfo.chainId}
99
+ handleSuccess={handleSuccess}
100
+ />
101
+ )
102
+ }
@@ -0,0 +1,29 @@
1
+ import { formatNumber, SolanaPoolInfo } from '@huma-finance/shared'
2
+ import React from 'react'
3
+
4
+ import { useAppSelector } from '../../../hooks/useRedux'
5
+ import { selectWidgetState } from '../../../store/widgets.selectors'
6
+ import { SolanaTxDoneModal } from '../../SolanaTxDoneModal'
7
+
8
+ type Props = {
9
+ poolInfo: SolanaPoolInfo
10
+ handleAction: () => void
11
+ }
12
+
13
+ export function Done({ poolInfo, handleAction }: Props): React.ReactElement {
14
+ const { redeemShares, solanaSignature } = useAppSelector(selectWidgetState)
15
+
16
+ const content = [
17
+ `${formatNumber(redeemShares)} shares requested for redemption`,
18
+ ]
19
+
20
+ return (
21
+ <SolanaTxDoneModal
22
+ handleAction={handleAction}
23
+ content={content}
24
+ chainId={poolInfo.chainId}
25
+ solanaSignature={solanaSignature}
26
+ buttonText='DONE'
27
+ />
28
+ )
29
+ }