@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huma-finance/widgets",
3
- "version": "0.0.62-beta.633+de08f40",
3
+ "version": "0.0.62-beta.635+3697d5c",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,8 +21,6 @@
21
21
  "dependencies": {
22
22
  "@apollo/client": "^3.7.2",
23
23
  "@coinbase/wallet-sdk": "^3.5.3",
24
- "@emotion/react": "^11.5.0",
25
- "@emotion/styled": "^11.3.0",
26
24
  "@ethersproject/address": "^5.7.0",
27
25
  "@ethersproject/bignumber": "^5.6.0",
28
26
  "@ethersproject/bytes": "^5.7.0",
@@ -31,9 +29,9 @@
31
29
  "@ethersproject/experimental": "^5.7.0",
32
30
  "@ethersproject/providers": "^5.6.0",
33
31
  "@ethersproject/units": "^5.6.0",
34
- "@huma-finance/sdk": "^0.0.62-beta.633+de08f40",
35
- "@huma-finance/shared": "^0.0.61",
36
- "@huma-finance/web-shared": "^0.0.61",
32
+ "@huma-finance/sdk": "^0.0.62-beta.635+3697d5c",
33
+ "@huma-finance/shared": "^0.0.62-beta.635+3697d5c",
34
+ "@huma-finance/web-shared": "^0.0.62-beta.635+3697d5c",
37
35
  "@mui/icons-material": "^5.3.0",
38
36
  "@mui/material": "^5.0.6",
39
37
  "@mui/styles": "^5.0.2",
@@ -78,7 +76,7 @@
78
76
  "react-router-dom": "^5.3.0",
79
77
  "react-scripts": "5.0.1",
80
78
  "tslib": "^2.5.0",
81
- "typescript": "^4.8.4",
79
+ "typescript": "^5.0.0",
82
80
  "utf8": "^3.0.0",
83
81
  "web-vitals": "^2.1.4",
84
82
  "yup": "^0.32.11"
@@ -188,6 +186,18 @@
188
186
  "wagmi": "^0.12.10"
189
187
  },
190
188
  "peerDependencies": {
189
+ "@coral-xyz/anchor": "^0.30.1",
190
+ "@emotion/react": "^11.5.0",
191
+ "@emotion/styled": "^11.3.0",
192
+ "@mui/icons-material": "^5.3.0",
193
+ "@mui/material": "^5.0.6",
194
+ "@mui/styles": "^5.0.2",
195
+ "@mui/system": "^5.0.6",
196
+ "@mui/x-date-pickers": "^5.0.7",
197
+ "@solana/spl-token": "^0.4.8",
198
+ "@solana/wallet-adapter-base": "^0.9.23",
199
+ "@solana/wallet-adapter-react": "^0.15.35",
200
+ "@solana/web3.js": "^1.95.3",
191
201
  "ethers": "^5.6.1",
192
202
  "react": ">=16.8.0",
193
203
  "react-dom": ">=16.8.0",
@@ -197,5 +207,5 @@
197
207
  "optionalDependencies": {
198
208
  "encoding": "^0.1.13"
199
209
  },
200
- "gitHead": "de08f401ed13fbb79a89ccb48e066ad217d6d1bc"
210
+ "gitHead": "3697d5c54ff93df02370acd34d8ca1686e90919f"
201
211
  }
@@ -45,6 +45,7 @@ export function ChooseAmount({
45
45
  ethers.utils.formatUnits(payoffAmountBN, decimals),
46
46
  )
47
47
 
48
+ // TODO: Remove redundant useEffect
48
49
  useEffect(() => {
49
50
  setCurrentAmount(totalDueAmount)
50
51
  }, [totalDueAmount])
@@ -0,0 +1,67 @@
1
+ import {
2
+ formatNumber,
3
+ SolanaPoolInfo,
4
+ SolanaTokenUtils,
5
+ } from '@huma-finance/shared'
6
+ import { CreditConfigAccount } from '@huma-finance/web-shared'
7
+ import React, { useCallback, useMemo, useState } from 'react'
8
+
9
+ import { useAppDispatch } from '../../../hooks/useRedux'
10
+ import { setBorrowInfo } from '../../../store/widgets.reducers'
11
+ import { WIDGET_STEP } from '../../../store/widgets.store'
12
+ import { InputAmountModal } from '../../InputAmountModal'
13
+
14
+ type Props = {
15
+ poolInfo: SolanaPoolInfo
16
+ creditConfigAccount: CreditConfigAccount
17
+ }
18
+
19
+ export function ChooseAmount({
20
+ poolInfo,
21
+ creditConfigAccount,
22
+ }: Props): React.ReactElement {
23
+ const dispatch = useAppDispatch()
24
+ const { symbol, decimals } = poolInfo.underlyingMint
25
+ const [currentAmount, setCurrentAmount] = useState<number>(0)
26
+ const creditAvailable = useMemo(
27
+ () =>
28
+ SolanaTokenUtils.formatUnits(
29
+ creditConfigAccount.creditAvailable,
30
+ decimals,
31
+ ),
32
+ [creditConfigAccount.creditAvailable, decimals],
33
+ )
34
+
35
+ const handleChangeAmount = (newAmount: number) => {
36
+ setCurrentAmount(newAmount)
37
+ }
38
+
39
+ const handleAction = useCallback(() => {
40
+ const borrowAmountBN = SolanaTokenUtils.parseUnits(
41
+ String(currentAmount),
42
+ decimals,
43
+ )
44
+ dispatch(
45
+ setBorrowInfo({
46
+ borrowAmount: currentAmount,
47
+ borrowAmountBN: JSON.parse(borrowAmountBN.toString()),
48
+ chargedFees: 0,
49
+ nextStep: WIDGET_STEP.Transfer,
50
+ }),
51
+ )
52
+ }, [currentAmount, decimals, dispatch])
53
+
54
+ return (
55
+ <InputAmountModal
56
+ title='Borrow'
57
+ subTitle='Choose Amount'
58
+ tokenSymbol={symbol}
59
+ currentAmount={currentAmount}
60
+ handleChangeAmount={handleChangeAmount}
61
+ maxAmount={creditAvailable}
62
+ infos={[`${String(formatNumber(creditAvailable))} ${symbol} Available`]}
63
+ handleAction={handleAction}
64
+ actionText='BORROW'
65
+ />
66
+ )
67
+ }
@@ -0,0 +1,159 @@
1
+ import {
2
+ getCreditAccounts,
3
+ getSentinelAddress,
4
+ getTokenAccounts,
5
+ SolanaPoolInfo,
6
+ SolanaTokenUtils,
7
+ } from '@huma-finance/shared'
8
+ import React, { useCallback, useEffect, useState } from 'react'
9
+
10
+ import {
11
+ ASSOCIATED_TOKEN_PROGRAM_ID,
12
+ createApproveCheckedInstruction,
13
+ createAssociatedTokenAccountInstruction,
14
+ getAccount,
15
+ TOKEN_2022_PROGRAM_ID,
16
+ TokenAccountNotFoundError,
17
+ TokenInvalidAccountOwnerError,
18
+ } from '@solana/spl-token'
19
+ import { useConnection, useWallet } from '@solana/wallet-adapter-react'
20
+ import { PublicKey, Transaction } from '@solana/web3.js'
21
+ import { useHumaProgram } from '@huma-finance/web-shared'
22
+ import { BN } from '@coral-xyz/anchor'
23
+ import { useAppDispatch, useAppSelector } from '../../../hooks/useRedux'
24
+ import { SolanaTxSendModal } from '../../SolanaTxSendModal'
25
+ import { setStep } from '../../../store/widgets.reducers'
26
+ import { WIDGET_STEP } from '../../../store/widgets.store'
27
+ import { selectWidgetState } from '../../../store/widgets.selectors'
28
+
29
+ type Props = {
30
+ poolInfo: SolanaPoolInfo
31
+ }
32
+
33
+ export function Transfer({ poolInfo }: Props): React.ReactElement | null {
34
+ const dispatch = useAppDispatch()
35
+ const { wallet, publicKey } = useWallet()
36
+ const sentinel = getSentinelAddress(poolInfo.chainId)
37
+ const { connection } = useConnection()
38
+ const [transaction, setTransaction] = useState<Transaction>()
39
+ const program = useHumaProgram(poolInfo.chainId)
40
+ const { borrowAmountBN: borrowAmountBNJson } =
41
+ useAppSelector(selectWidgetState)
42
+
43
+ const handleSuccess = useCallback(() => {
44
+ dispatch(setStep(WIDGET_STEP.Done))
45
+ }, [dispatch])
46
+
47
+ useEffect(() => {
48
+ async function getTx() {
49
+ if (!wallet || !publicKey || !connection) {
50
+ return
51
+ }
52
+
53
+ const { underlyingTokenATA } = getTokenAccounts(poolInfo, publicKey)
54
+ const tx = new Transaction()
55
+
56
+ // Create user token account if it doesn't exist
57
+ let tokenAccount
58
+ try {
59
+ tokenAccount = await getAccount(
60
+ connection,
61
+ underlyingTokenATA,
62
+ undefined,
63
+ TOKEN_2022_PROGRAM_ID,
64
+ )
65
+ } catch (error: unknown) {
66
+ // TokenAccountNotFoundError can be possible if the associated address has already received some lamports,
67
+ // becoming a system account. Assuming program derived addressing is safe, this is the only case for the
68
+ // TokenInvalidAccountOwnerError in this code path.
69
+ if (
70
+ error instanceof TokenAccountNotFoundError ||
71
+ error instanceof TokenInvalidAccountOwnerError
72
+ ) {
73
+ // As this isn't atomic, it's possible others can create associated accounts meanwhile.
74
+ tx.add(
75
+ createAssociatedTokenAccountInstruction(
76
+ publicKey,
77
+ underlyingTokenATA,
78
+ publicKey,
79
+ new PublicKey(poolInfo.underlyingMint.address),
80
+ TOKEN_2022_PROGRAM_ID,
81
+ ASSOCIATED_TOKEN_PROGRAM_ID,
82
+ ),
83
+ )
84
+ }
85
+ }
86
+
87
+ // Approve allowance to the sentinel if the account is new or delegated amount is not enough
88
+ const borrowAmountBN = new BN(borrowAmountBNJson?.toString() ?? '0')
89
+ if (
90
+ !tokenAccount ||
91
+ borrowAmountBN.gt(new BN(tokenAccount.delegatedAmount.toString())) ||
92
+ tokenAccount.delegate?.toString() !== sentinel
93
+ ) {
94
+ tx.add(
95
+ createApproveCheckedInstruction(
96
+ underlyingTokenATA,
97
+ new PublicKey(poolInfo.underlyingMint.address),
98
+ new PublicKey(sentinel), // delegate
99
+ publicKey, // owner of the wallet
100
+ BigInt(
101
+ SolanaTokenUtils.parseUnits(
102
+ '100000000000', // 100 billion
103
+ poolInfo.underlyingMint.decimals,
104
+ ).toString(),
105
+ ), // amount
106
+ poolInfo.underlyingMint.decimals,
107
+ undefined, // multiSigners
108
+ TOKEN_2022_PROGRAM_ID,
109
+ ),
110
+ )
111
+ }
112
+
113
+ const { creditConfigAccount, creditStateAccount } = getCreditAccounts(
114
+ poolInfo,
115
+ publicKey,
116
+ )
117
+ // borrowAmountBNJson is stored as a raw number string in this flow
118
+ const programTx = await program.methods
119
+ .drawdown(borrowAmountBN)
120
+ .accountsPartial({
121
+ borrower: publicKey,
122
+ humaConfig: poolInfo.humaConfig,
123
+ poolConfig: poolInfo.poolConfig,
124
+ poolState: poolInfo.poolState,
125
+ creditConfig: creditConfigAccount,
126
+ creditState: creditStateAccount,
127
+ poolAuthority: poolInfo.poolAuthority,
128
+ underlyingMint: poolInfo.underlyingMint.address,
129
+ poolUnderlyingToken: poolInfo.poolUnderlyingTokenAccount,
130
+ borrowerUnderlyingToken: underlyingTokenATA,
131
+ tokenProgram: TOKEN_2022_PROGRAM_ID,
132
+ })
133
+ .transaction()
134
+ tx.add(programTx)
135
+
136
+ setTransaction(tx)
137
+ }
138
+ getTx()
139
+ }, [
140
+ borrowAmountBNJson,
141
+ connection,
142
+ dispatch,
143
+ poolInfo,
144
+ poolInfo.underlyingMint.address,
145
+ poolInfo.underlyingMint.decimals,
146
+ program.methods,
147
+ publicKey,
148
+ sentinel,
149
+ wallet,
150
+ ])
151
+
152
+ return (
153
+ <SolanaTxSendModal
154
+ tx={transaction}
155
+ chainId={poolInfo.chainId}
156
+ handleSuccess={handleSuccess}
157
+ />
158
+ )
159
+ }
@@ -0,0 +1,38 @@
1
+ import { formatNumber, SolanaPoolInfo, timeUtil } from '@huma-finance/shared'
2
+ import React from 'react'
3
+
4
+ import { SolanaPoolState } from '@huma-finance/web-shared'
5
+ import { useAppSelector } from '../../../hooks/useRedux'
6
+ import { selectWidgetState } from '../../../store/widgets.selectors'
7
+ import { SolanaTxDoneModal } from '../../SolanaTxDoneModal'
8
+
9
+ type Props = {
10
+ poolInfo: SolanaPoolInfo
11
+ poolState: SolanaPoolState
12
+ handleAction: () => void
13
+ }
14
+
15
+ export function Done({
16
+ poolInfo,
17
+ poolState,
18
+ handleAction,
19
+ }: Props): React.ReactElement {
20
+ const { borrowAmount, solanaSignature } = useAppSelector(selectWidgetState)
21
+ const { symbol } = poolInfo.underlyingMint
22
+ const dueDate = timeUtil.timestampToLL(poolState.epochEndTime)
23
+
24
+ const content = [
25
+ `${formatNumber(borrowAmount)} ${symbol} is now in your wallet.`,
26
+ `Note: your first automatic payment will occur after ${dueDate}.`,
27
+ ]
28
+
29
+ return (
30
+ <SolanaTxDoneModal
31
+ handleAction={handleAction}
32
+ content={content}
33
+ chainId={poolInfo.chainId}
34
+ solanaSignature={solanaSignature}
35
+ buttonText='DONE'
36
+ />
37
+ )
38
+ }
@@ -0,0 +1,93 @@
1
+ import { SolanaPoolInfo } from '@huma-finance/shared'
2
+ import { SolanaPoolState, 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} SolanaBorrowProps
19
+ * @property {SolanaPoolInfo} poolInfo The metadata of the pool.
20
+ * @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.
21
+ * @property {function():void|undefined} handleSuccess Optional function to notify that the lending pool supply action is successful.
22
+ */
23
+ export interface SolanaBorrowProps {
24
+ poolInfo: SolanaPoolInfo
25
+ poolState: SolanaPoolState
26
+ handleClose: () => void
27
+ handleSuccess?: () => void
28
+ }
29
+
30
+ export function SolanaBorrow({
31
+ poolInfo,
32
+ poolState,
33
+ handleClose,
34
+ handleSuccess,
35
+ }: SolanaBorrowProps): React.ReactElement | null {
36
+ const title = 'Borrow'
37
+ const dispatch = useDispatch()
38
+ const { step, errorMessage } = useAppSelector(selectWidgetState)
39
+ const { creditConfigAccount } = useBorrowerAccounts(
40
+ poolInfo.chainId,
41
+ poolInfo.poolName,
42
+ )
43
+
44
+ useEffect(() => {
45
+ if (!step && creditConfigAccount) {
46
+ dispatch(setStep(WIDGET_STEP.ChooseAmount))
47
+ }
48
+ }, [creditConfigAccount, dispatch, step])
49
+
50
+ if (!creditConfigAccount) {
51
+ return (
52
+ <WidgetWrapper
53
+ isOpen
54
+ isLoading
55
+ loadingTitle={title}
56
+ handleClose={handleClose}
57
+ handleSuccess={handleSuccess}
58
+ />
59
+ )
60
+ }
61
+
62
+ return (
63
+ <WidgetWrapper
64
+ isOpen
65
+ loadingTitle={title}
66
+ handleClose={handleClose}
67
+ handleSuccess={handleSuccess}
68
+ >
69
+ {step === WIDGET_STEP.ChooseAmount && creditConfigAccount && (
70
+ <ChooseAmount
71
+ poolInfo={poolInfo}
72
+ creditConfigAccount={creditConfigAccount}
73
+ />
74
+ )}
75
+ {step === WIDGET_STEP.Transfer && <Transfer poolInfo={poolInfo} />}
76
+ {step === WIDGET_STEP.Done && (
77
+ <Done
78
+ poolInfo={poolInfo}
79
+ poolState={poolState}
80
+ handleAction={handleClose}
81
+ />
82
+ )}
83
+ {step === WIDGET_STEP.Error && (
84
+ <ErrorModal
85
+ title={title}
86
+ errorReason='Sorry there was an error'
87
+ errorMessage={errorMessage}
88
+ handleOk={handleClose}
89
+ />
90
+ )}
91
+ </WidgetWrapper>
92
+ )
93
+ }
@@ -0,0 +1,75 @@
1
+ import {
2
+ formatNumber,
3
+ SolanaPoolInfo,
4
+ SolanaTokenUtils,
5
+ } from '@huma-finance/shared'
6
+ import { CreditStateAccount } from '@huma-finance/web-shared'
7
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
8
+
9
+ import { useAppDispatch } from '../../../hooks/useRedux'
10
+ import { setPaymentAmount, setStep } from '../../../store/widgets.reducers'
11
+ import { WIDGET_STEP } from '../../../store/widgets.store'
12
+ import { InputAmountModal } from '../../InputAmountModal'
13
+
14
+ type Props = {
15
+ poolInfo: SolanaPoolInfo
16
+ creditStateAccount: CreditStateAccount
17
+ }
18
+
19
+ export function ChooseAmount({
20
+ poolInfo,
21
+ creditStateAccount,
22
+ }: Props): React.ReactElement {
23
+ const dispatch = useAppDispatch()
24
+ const { symbol, decimals } = poolInfo.underlyingMint
25
+ const [currentAmount, setCurrentAmount] = useState<number>(0)
26
+ const totalDueAmount = useMemo(
27
+ () =>
28
+ Number(
29
+ SolanaTokenUtils.formatUnits(
30
+ creditStateAccount.creditRecord.totalDueAmount,
31
+ decimals,
32
+ ),
33
+ ),
34
+ [creditStateAccount.creditRecord.totalDueAmount, decimals],
35
+ )
36
+ const payoffAmount = useMemo(
37
+ () =>
38
+ Number(
39
+ SolanaTokenUtils.formatUnits(
40
+ creditStateAccount.creditRecord.payoffAmount,
41
+ decimals,
42
+ ),
43
+ ),
44
+ [creditStateAccount.creditRecord.payoffAmount, decimals],
45
+ )
46
+
47
+ useEffect(() => {
48
+ setCurrentAmount(totalDueAmount)
49
+ dispatch(setPaymentAmount({ paymentAmount: totalDueAmount }))
50
+ }, [dispatch, totalDueAmount])
51
+
52
+ const handleChangeAmount = (newAmount: number) => {
53
+ setCurrentAmount(newAmount)
54
+ dispatch(setPaymentAmount({ paymentAmount: newAmount }))
55
+ }
56
+
57
+ const handleAction = useCallback(() => {
58
+ dispatch(setStep(WIDGET_STEP.Transfer))
59
+ }, [dispatch])
60
+
61
+ return (
62
+ <InputAmountModal
63
+ title='Make Payment'
64
+ subTitle='Choose Amount'
65
+ tokenSymbol={symbol}
66
+ currentAmount={currentAmount}
67
+ handleChangeAmount={handleChangeAmount}
68
+ maxAmount={payoffAmount}
69
+ maxAmountText='Pay Off'
70
+ infos={[`${formatNumber(totalDueAmount.toFixed(0))} Due`]}
71
+ handleAction={handleAction}
72
+ actionText='PAY'
73
+ />
74
+ )
75
+ }
@@ -0,0 +1,92 @@
1
+ import {
2
+ getCreditAccounts,
3
+ getTokenAccounts,
4
+ SolanaPoolInfo,
5
+ SolanaTokenUtils,
6
+ } from '@huma-finance/shared'
7
+ import React, { useCallback, useEffect, useState } from 'react'
8
+
9
+ import { TOKEN_2022_PROGRAM_ID } from '@solana/spl-token'
10
+ import { useConnection, useWallet } from '@solana/wallet-adapter-react'
11
+ import { Transaction } from '@solana/web3.js'
12
+ import { useHumaProgram } from '@huma-finance/web-shared'
13
+ import { useAppDispatch, useAppSelector } from '../../../hooks/useRedux'
14
+ import { SolanaTxSendModal } from '../../SolanaTxSendModal'
15
+ import { setStep } from '../../../store/widgets.reducers'
16
+ import { WIDGET_STEP } from '../../../store/widgets.store'
17
+ import { selectWidgetState } from '../../../store/widgets.selectors'
18
+
19
+ type Props = {
20
+ poolInfo: SolanaPoolInfo
21
+ }
22
+
23
+ export function Transfer({ poolInfo }: Props): React.ReactElement | null {
24
+ const dispatch = useAppDispatch()
25
+ const { wallet, publicKey } = useWallet()
26
+ const { connection } = useConnection()
27
+ const [transaction, setTransaction] = useState<Transaction>()
28
+ const program = useHumaProgram(poolInfo.chainId)
29
+ const { paymentAmount } = useAppSelector(selectWidgetState)
30
+ const { decimals } = poolInfo.underlyingMint
31
+
32
+ const handleSuccess = useCallback(() => {
33
+ dispatch(setStep(WIDGET_STEP.Done))
34
+ }, [dispatch])
35
+
36
+ useEffect(() => {
37
+ async function getTx() {
38
+ if (!wallet || !publicKey || !connection || !paymentAmount) {
39
+ return
40
+ }
41
+
42
+ const { underlyingTokenATA } = getTokenAccounts(poolInfo, publicKey)
43
+
44
+ const { creditConfigAccount, creditStateAccount } = getCreditAccounts(
45
+ poolInfo,
46
+ publicKey,
47
+ )
48
+ const paymentAmountBN = SolanaTokenUtils.parseUnits(
49
+ paymentAmount.toString(),
50
+ decimals,
51
+ )
52
+ const tx = await program.methods
53
+ .makePayment(paymentAmountBN)
54
+ .accountsPartial({
55
+ signer: publicKey,
56
+ humaConfig: poolInfo.humaConfig,
57
+ poolConfig: poolInfo.poolConfig,
58
+ poolState: poolInfo.poolState,
59
+ creditConfig: creditConfigAccount,
60
+ creditState: creditStateAccount,
61
+ poolAuthority: poolInfo.poolAuthority,
62
+ underlyingMint: poolInfo.underlyingMint.address,
63
+ poolUnderlyingToken: poolInfo.poolUnderlyingTokenAccount,
64
+ borrowerUnderlyingToken: underlyingTokenATA,
65
+ tokenProgram: TOKEN_2022_PROGRAM_ID,
66
+ })
67
+ .transaction()
68
+
69
+ setTransaction(tx)
70
+ }
71
+ getTx()
72
+ }, [
73
+ connection,
74
+ decimals,
75
+ dispatch,
76
+ paymentAmount,
77
+ poolInfo,
78
+ poolInfo.underlyingMint.address,
79
+ poolInfo.underlyingMint.decimals,
80
+ program.methods,
81
+ publicKey,
82
+ wallet,
83
+ ])
84
+
85
+ return (
86
+ <SolanaTxSendModal
87
+ tx={transaction}
88
+ chainId={poolInfo.chainId}
89
+ handleSuccess={handleSuccess}
90
+ />
91
+ )
92
+ }
@@ -0,0 +1,60 @@
1
+ import { formatNumber, SolanaPoolInfo } from '@huma-finance/shared'
2
+ import React, { useEffect, useMemo, useState } from 'react'
3
+
4
+ import { useConnection } from '@solana/wallet-adapter-react'
5
+ import { useAppSelector } from '../../../hooks/useRedux'
6
+ import { selectWidgetState } from '../../../store/widgets.selectors'
7
+ import { SolanaTxDoneModal } from '../../SolanaTxDoneModal'
8
+
9
+ type Props = {
10
+ poolInfo: SolanaPoolInfo
11
+ handleAction: () => void
12
+ }
13
+
14
+ export function Done({ poolInfo, handleAction }: Props): React.ReactElement {
15
+ const { solanaSignature } = useAppSelector(selectWidgetState)
16
+ const { symbol } = poolInfo.underlyingMint
17
+ const { connection } = useConnection()
18
+ const [tokensPaid, setTokensPaid] = useState<number | null>(null)
19
+
20
+ useEffect(() => {
21
+ const fetchTokensPaid = async () => {
22
+ if (!solanaSignature) {
23
+ return
24
+ }
25
+
26
+ const txData = await connection.getTransaction(solanaSignature, {
27
+ maxSupportedTransactionVersion: 2,
28
+ })
29
+
30
+ const preTokenBalance =
31
+ txData?.meta?.preTokenBalances?.[0]?.uiTokenAmount?.uiAmount
32
+ const postTokenBalance =
33
+ txData?.meta?.postTokenBalances?.[0]?.uiTokenAmount?.uiAmount
34
+
35
+ if (preTokenBalance && postTokenBalance) {
36
+ setTokensPaid(preTokenBalance - postTokenBalance)
37
+ }
38
+ }
39
+
40
+ fetchTokensPaid()
41
+ }, [connection, solanaSignature])
42
+
43
+ const content = useMemo(() => {
44
+ if (tokensPaid !== null) {
45
+ return [`You successfully paid ${formatNumber(tokensPaid)} ${symbol}.`]
46
+ }
47
+
48
+ return ['Your payment to the pool was successful']
49
+ }, [tokensPaid, symbol])
50
+
51
+ return (
52
+ <SolanaTxDoneModal
53
+ handleAction={handleAction}
54
+ content={content}
55
+ chainId={poolInfo.chainId}
56
+ solanaSignature={solanaSignature}
57
+ buttonText='DONE'
58
+ />
59
+ )
60
+ }