@huma-finance/widgets 0.0.62-beta.597 → 0.0.62-beta.611

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 (33) hide show
  1. package/API.md +185 -0
  2. package/dist/cjs/index.cjs +1490 -268
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/index.d.ts +117 -2
  5. package/dist/index.js +1489 -271
  6. package/dist/index.js.map +1 -1
  7. package/package.json +11 -6
  8. package/src/components/CreditLine/solanaBorrow/1-ChooseAmount.tsx +86 -0
  9. package/src/components/CreditLine/solanaBorrow/2-ApproveAllowance.tsx +87 -0
  10. package/src/components/CreditLine/solanaBorrow/index.tsx +96 -0
  11. package/src/components/InputAmountModal.tsx +12 -2
  12. package/src/components/Lend/solanaAddRedemption/1-ChooseTranche.tsx +114 -0
  13. package/src/components/Lend/solanaAddRedemption/2-ChooseAmount.tsx +183 -0
  14. package/src/components/Lend/solanaAddRedemption/3-Transfer.tsx +102 -0
  15. package/src/components/Lend/solanaAddRedemption/4-Done.tsx +29 -0
  16. package/src/components/Lend/solanaAddRedemption/index.tsx +140 -0
  17. package/src/components/Lend/solanaCancelRedemption/1-ChooseTranche.tsx +114 -0
  18. package/src/components/Lend/solanaCancelRedemption/2-Transfer.tsx +99 -0
  19. package/src/components/Lend/solanaCancelRedemption/3-Done.tsx +55 -0
  20. package/src/components/Lend/solanaCancelRedemption/index.tsx +152 -0
  21. package/src/components/Lend/solanaSupply/1-ChooseTranche.tsx +116 -0
  22. package/src/components/Lend/solanaSupply/2-Evaluation.tsx +0 -0
  23. package/src/components/Lend/solanaSupply/3-ChooseAmount.tsx +130 -0
  24. package/src/components/Lend/solanaSupply/4-Transfer.tsx +89 -0
  25. package/src/components/Lend/solanaSupply/5-Success.tsx +50 -0
  26. package/src/components/Lend/solanaSupply/index.tsx +143 -0
  27. package/src/components/SolanaTxDoneModal.tsx +115 -0
  28. package/src/components/SolanaTxSendModal.tsx +61 -0
  29. package/src/components/SolanaViewOnExplorer.tsx +29 -0
  30. package/src/components/WidgetWrapper.tsx +6 -4
  31. package/src/index.tsx +124 -0
  32. package/src/store/widgets.reducers.ts +5 -0
  33. package/src/store/widgets.store.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huma-finance/widgets",
3
- "version": "0.0.62-beta.597+bf9d9fc",
3
+ "version": "0.0.62-beta.611+4c13dfb",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -30,9 +30,9 @@
30
30
  "@ethersproject/experimental": "^5.7.0",
31
31
  "@ethersproject/providers": "^5.6.0",
32
32
  "@ethersproject/units": "^5.6.0",
33
- "@huma-finance/sdk": "^0.0.62-beta.597+bf9d9fc",
34
- "@huma-finance/shared": "^0.0.62-beta.597+bf9d9fc",
35
- "@huma-finance/web-shared": "^0.0.61",
33
+ "@huma-finance/sdk": "^0.0.61",
34
+ "@huma-finance/shared": "^0.0.61",
35
+ "@huma-finance/web-shared": "^0.0.62-beta.611+4c13dfb",
36
36
  "@mui/icons-material": "^5.3.0",
37
37
  "@mui/material": "^5.0.6",
38
38
  "@mui/styles": "^5.0.2",
@@ -76,7 +76,7 @@
76
76
  "react-router-dom": "^5.3.0",
77
77
  "react-scripts": "5.0.1",
78
78
  "tslib": "^2.5.0",
79
- "typescript": "^4.8.4",
79
+ "typescript": "^5.0.0",
80
80
  "utf8": "^3.0.0",
81
81
  "web-vitals": "^2.1.4",
82
82
  "yup": "^0.32.11"
@@ -186,6 +186,11 @@
186
186
  "wagmi": "^0.12.10"
187
187
  },
188
188
  "peerDependencies": {
189
+ "@coral-xyz/anchor": "^0.30.1",
190
+ "@solana/spl-token": "^0.4.8",
191
+ "@solana/wallet-adapter-base": "^0.9.23",
192
+ "@solana/wallet-adapter-react": "^0.15.35",
193
+ "@solana/web3.js": "^1.95.3",
189
194
  "ethers": "^5.6.1",
190
195
  "react": ">=16.8.0",
191
196
  "react-dom": ">=16.8.0",
@@ -195,5 +200,5 @@
195
200
  "optionalDependencies": {
196
201
  "encoding": "^0.1.13"
197
202
  },
198
- "gitHead": "bf9d9fc16e7624e4af65434c712e37e75cb438d4"
203
+ "gitHead": "4c13dfb9c79496003f715a370cbed5573a53e57f"
199
204
  }
@@ -0,0 +1,86 @@
1
+ import {
2
+ formatNumber,
3
+ getSentinelAddress,
4
+ SolanaPoolInfo,
5
+ SolanaTokenUtils,
6
+ } from '@huma-finance/shared'
7
+ import { CreditConfigAccount } from '@huma-finance/web-shared'
8
+ import React, { useCallback, useMemo, useState } from 'react'
9
+
10
+ import { BN } from '@coral-xyz/anchor'
11
+ import { Account } from '@solana/spl-token'
12
+ import { useAppDispatch } from '../../../hooks/useRedux'
13
+ import { setBorrowInfo } from '../../../store/widgets.reducers'
14
+ import { WIDGET_STEP } from '../../../store/widgets.store'
15
+ import { InputAmountModal } from '../../InputAmountModal'
16
+
17
+ type Props = {
18
+ poolInfo: SolanaPoolInfo
19
+ creditConfigAccount: CreditConfigAccount
20
+ tokenAccount: Account
21
+ }
22
+
23
+ export function ChooseAmount({
24
+ poolInfo,
25
+ creditConfigAccount,
26
+ tokenAccount,
27
+ }: Props): React.ReactElement {
28
+ const dispatch = useAppDispatch()
29
+ const { symbol, decimals } = poolInfo.underlyingMint
30
+ const sentinel = useMemo(
31
+ () => getSentinelAddress(poolInfo.chainId),
32
+ [poolInfo.chainId],
33
+ )
34
+ const [currentAmount, setCurrentAmount] = useState<number>(0)
35
+ const creditAvailable = useMemo(
36
+ () =>
37
+ SolanaTokenUtils.formatUnits(
38
+ creditConfigAccount.creditAvailable,
39
+ decimals,
40
+ ),
41
+ [creditConfigAccount.creditAvailable, decimals],
42
+ )
43
+
44
+ const handleChangeAmount = (newAmount: number) => {
45
+ setCurrentAmount(newAmount)
46
+ }
47
+
48
+ const handleAction = useCallback(() => {
49
+ if (!tokenAccount) {
50
+ return
51
+ }
52
+
53
+ const borrowAmountBN = SolanaTokenUtils.parseUnits(
54
+ String(currentAmount),
55
+ decimals,
56
+ )
57
+ const nextStep =
58
+ borrowAmountBN.gt(new BN(tokenAccount.delegatedAmount)) ||
59
+ tokenAccount.delegate?.toString() !== sentinel
60
+ ? WIDGET_STEP.ApproveAllowance
61
+ : WIDGET_STEP.Transfer
62
+
63
+ dispatch(
64
+ setBorrowInfo({
65
+ borrowAmount: currentAmount,
66
+ borrowAmountBN,
67
+ chargedFees: 0,
68
+ nextStep,
69
+ }),
70
+ )
71
+ }, [currentAmount, decimals, dispatch, sentinel, tokenAccount])
72
+
73
+ return (
74
+ <InputAmountModal
75
+ title='Borrow'
76
+ subTitle='Choose Amount'
77
+ tokenSymbol={symbol}
78
+ currentAmount={currentAmount}
79
+ handleChangeAmount={handleChangeAmount}
80
+ maxAmount={creditAvailable}
81
+ infos={[`${String(formatNumber(creditAvailable))} ${symbol} Available`]}
82
+ handleAction={handleAction}
83
+ actionText='BORROW'
84
+ />
85
+ )
86
+ }
@@ -0,0 +1,87 @@
1
+ import {
2
+ getSentinelAddress,
3
+ SolanaPoolInfo,
4
+ SolanaTokenUtils,
5
+ } from '@huma-finance/shared'
6
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
7
+
8
+ import {
9
+ Account,
10
+ createApproveCheckedInstruction,
11
+ TOKEN_2022_PROGRAM_ID,
12
+ } from '@solana/spl-token'
13
+ import { useConnection, useWallet } from '@solana/wallet-adapter-react'
14
+ import { PublicKey, Transaction } from '@solana/web3.js'
15
+ import { useAppDispatch } from '../../../hooks/useRedux'
16
+ import { SolanaTxSendModal } from '../../SolanaTxSendModal'
17
+ import { setStep } from '../../../store/widgets.reducers'
18
+ import { WIDGET_STEP } from '../../../store/widgets.store'
19
+
20
+ type Props = {
21
+ poolInfo: SolanaPoolInfo
22
+ tokenAccount: Account
23
+ }
24
+
25
+ export function ApproveAllowance({
26
+ poolInfo,
27
+ tokenAccount,
28
+ }: Props): React.ReactElement | null {
29
+ const dispatch = useAppDispatch()
30
+ const { wallet, publicKey, sendTransaction, signTransaction } = useWallet()
31
+ const sentinel = useMemo(
32
+ () => getSentinelAddress(poolInfo.chainId),
33
+ [poolInfo.chainId],
34
+ )
35
+ const { connection } = useConnection()
36
+ const [transaction, setTransaction] = useState<Transaction>()
37
+
38
+ const handleSuccess = useCallback(() => {
39
+ dispatch(setStep(WIDGET_STEP.Transfer))
40
+ }, [dispatch])
41
+
42
+ useEffect(() => {
43
+ async function getTx() {
44
+ if (!wallet || !publicKey || !connection || !signTransaction) {
45
+ return
46
+ }
47
+
48
+ const tx = new Transaction().add(
49
+ createApproveCheckedInstruction(
50
+ tokenAccount.address,
51
+ new PublicKey(poolInfo.underlyingMint.address),
52
+ new PublicKey(sentinel), // delegate
53
+ publicKey, // owner of the wallet
54
+ SolanaTokenUtils.parseUnits(
55
+ '100000000000', // 100 billion
56
+ poolInfo.underlyingMint.decimals,
57
+ ), // amount
58
+ poolInfo.underlyingMint.decimals,
59
+ undefined, // multiSigners
60
+ TOKEN_2022_PROGRAM_ID,
61
+ ),
62
+ )
63
+
64
+ setTransaction(tx)
65
+ }
66
+ getTx()
67
+ }, [
68
+ connection,
69
+ dispatch,
70
+ poolInfo.underlyingMint.address,
71
+ poolInfo.underlyingMint.decimals,
72
+ publicKey,
73
+ sendTransaction,
74
+ sentinel,
75
+ signTransaction,
76
+ tokenAccount.address,
77
+ wallet,
78
+ ])
79
+
80
+ return (
81
+ <SolanaTxSendModal
82
+ tx={transaction}
83
+ chainId={poolInfo.chainId}
84
+ handleSuccess={handleSuccess}
85
+ />
86
+ )
87
+ }
@@ -0,0 +1,96 @@
1
+ import { SolanaPoolInfo } from '@huma-finance/shared'
2
+ import {
3
+ useBorrowerAccounts,
4
+ usePoolUnderlyingTokenAccount,
5
+ } from '@huma-finance/web-shared'
6
+ import React, { useEffect } from 'react'
7
+ import { useDispatch } from 'react-redux'
8
+
9
+ import { useAppSelector } from '../../../hooks/useRedux'
10
+ import { selectWidgetState } from '../../../store/widgets.selectors'
11
+ import { WidgetWrapper } from '../../WidgetWrapper'
12
+ import { WIDGET_STEP } from '../../../store/widgets.store'
13
+ import { setStep } from '../../../store/widgets.reducers'
14
+ import { ErrorModal } from '../../ErrorModal'
15
+ import { ChooseAmount } from './1-ChooseAmount'
16
+ import { ApproveAllowance } from './2-ApproveAllowance'
17
+
18
+ /**
19
+ * Lend pool supply props
20
+ * @typedef {Object} SolanaBorrowProps
21
+ * @property {SolanaPoolInfo} poolInfo The metadata of the pool.
22
+ * @property {SolanaPoolState} poolState The current state config of the pool. * @property {function():void} handleClose Function to notify to close the widget modal when user clicks the 'x' close button.
23
+ * @property {function():void|undefined} handleSuccess Optional function to notify that the lending pool supply action is successful.
24
+ */
25
+ export interface SolanaBorrowProps {
26
+ poolInfo: SolanaPoolInfo
27
+ handleClose: () => void
28
+ handleSuccess?: () => void
29
+ }
30
+
31
+ export function SolanaBorrow({
32
+ poolInfo,
33
+ handleClose,
34
+ handleSuccess,
35
+ }: SolanaBorrowProps): React.ReactElement | null {
36
+ const dispatch = useDispatch()
37
+ const { creditConfigAccount, loading: isLoadingBorrowerAccounts } =
38
+ useBorrowerAccounts(poolInfo.chainId, poolInfo.poolName)
39
+ const { account: tokenAccount, loading: isLoadingTokenAccounts } =
40
+ usePoolUnderlyingTokenAccount(poolInfo)
41
+ const { step, errorMessage } = useAppSelector(selectWidgetState)
42
+ useEffect(() => {
43
+ if (!step) {
44
+ dispatch(setStep(WIDGET_STEP.ChooseAmount))
45
+ }
46
+ }, [dispatch, step])
47
+
48
+ const title = 'Redemption'
49
+ if (isLoadingBorrowerAccounts || isLoadingTokenAccounts) {
50
+ return (
51
+ <WidgetWrapper
52
+ isOpen
53
+ isLoading
54
+ loadingTitle={title}
55
+ handleClose={handleClose}
56
+ handleSuccess={handleSuccess}
57
+ />
58
+ )
59
+ }
60
+
61
+ return (
62
+ <WidgetWrapper
63
+ isOpen
64
+ loadingTitle={title}
65
+ handleClose={handleClose}
66
+ handleSuccess={handleSuccess}
67
+ >
68
+ {step === WIDGET_STEP.ChooseAmount &&
69
+ creditConfigAccount &&
70
+ tokenAccount && (
71
+ <ChooseAmount
72
+ poolInfo={poolInfo}
73
+ creditConfigAccount={creditConfigAccount}
74
+ tokenAccount={tokenAccount}
75
+ />
76
+ )}
77
+ {step === WIDGET_STEP.ApproveAllowance && tokenAccount && (
78
+ <ApproveAllowance poolInfo={poolInfo} tokenAccount={tokenAccount} />
79
+ )}
80
+ {/* {step === WIDGET_STEP.Transfer && selectedTranche && (
81
+ <Transfer poolInfo={poolInfo} selectedTranche={selectedTranche} />
82
+ )}
83
+ {step === WIDGET_STEP.Done && (
84
+ <Done poolInfo={poolInfo} handleAction={handleClose} />
85
+ )} */}
86
+ {step === WIDGET_STEP.Error && (
87
+ <ErrorModal
88
+ title={title}
89
+ errorReason='Sorry there was an error'
90
+ errorMessage={errorMessage}
91
+ handleOk={handleClose}
92
+ />
93
+ )}
94
+ </WidgetWrapper>
95
+ )
96
+ }
@@ -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,
@@ -125,6 +127,9 @@ export function InputAmountModal({
125
127
  if (!isEmpty(maxAmount) && Number(currentAmount) > Number(maxAmount)) {
126
128
  return true
127
129
  }
130
+ if (!isEmpty(minimumAmount) && Number(currentAmount) < minimumAmount!) {
131
+ return true
132
+ }
128
133
  return false
129
134
  }
130
135
 
@@ -157,7 +162,6 @@ export function InputAmountModal({
157
162
  />
158
163
  </Box>
159
164
  </Box>
160
-
161
165
  {infos && (
162
166
  <Box>
163
167
  {infos.map((info) => (
@@ -167,7 +171,13 @@ export function InputAmountModal({
167
171
  ))}
168
172
  </Box>
169
173
  )}
170
-
174
+ {!!minimumAmount && minimumAmount > 0 && (
175
+ <Box>
176
+ <Typography css={styles.info} key='minDeposit'>
177
+ Minimum deposit: {minimumAmount} {tokenSymbol}
178
+ </Typography>
179
+ </Box>
180
+ )}
171
181
  <BottomButton
172
182
  variant='contained'
173
183
  onClick={handleAction}
@@ -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)
62
+ : new BN(juniorTokenAccount?.amount),
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),
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),
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),
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
+ }