-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
495 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
packages/huma-widget/src/components/Lend/solanaEnableAutoRedemption/1-ApproveAllowance.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { timeUtil } from '@huma-finance/shared' | ||
import { SolanaPoolState } from '@huma-finance/web-shared' | ||
import dayjs from 'dayjs' | ||
import React, { useCallback } from 'react' | ||
import { Box, css, useTheme } from '@mui/material' | ||
import { useAppDispatch } from '../../../hooks/useRedux' | ||
import { WrapperModal } from '../../WrapperModal' | ||
import { WIDGET_STEP } from '../../../store/widgets.store' | ||
import { setStep } from '../../../store/widgets.reducers' | ||
import { BottomButton } from '../../BottomButton' | ||
import { AutoPaybackImg } from '../../images' | ||
|
||
type Props = { | ||
poolState: SolanaPoolState | ||
} | ||
|
||
export function ApproveAllowance({ poolState }: Props): React.ReactElement { | ||
const theme = useTheme() | ||
const dispatch = useAppDispatch() | ||
const handleNext = useCallback(() => { | ||
dispatch(setStep(WIDGET_STEP.Transfer)) | ||
}, [dispatch]) | ||
|
||
const styles = { | ||
iconWrapper: css` | ||
${theme.cssMixins.rowCentered}; | ||
margin-top: ${theme.spacing(6)}; | ||
& > img { | ||
width: 220px; | ||
} | ||
`, | ||
description: css` | ||
margin-top: ${theme.spacing(4)}; | ||
font-weight: 400; | ||
font-size: 16px; | ||
color: ${theme.palette.text.secondary}; | ||
padding: ${theme.spacing(0, 1)}; | ||
`, | ||
} | ||
|
||
const lockupEndTime = dayjs() | ||
.add(poolState.withdrawalLockupPeriodDays ?? 0, 'day') | ||
.date(1) | ||
const withdrawTime = lockupEndTime.add(1, 'month') | ||
|
||
return ( | ||
<WrapperModal title='Auto-Redemption'> | ||
<Box css={styles.iconWrapper}> | ||
<img src={AutoPaybackImg} alt='auto-payback' /> | ||
</Box> | ||
<Box css={styles.description}> | ||
This allowance transaction will enable auto-redemption for your existing | ||
tranche shares. Redemption requests will be automatically submitted on{' '} | ||
{timeUtil.timestampToLL(lockupEndTime.unix())}. Your deposit can be | ||
redeemed and yield rewards will stop on{' '} | ||
{timeUtil.timestampToLL(withdrawTime.unix())}. | ||
</Box> | ||
<BottomButton variant='contained' onClick={handleNext}> | ||
APPROVE ALLOWANCE | ||
</BottomButton> | ||
</WrapperModal> | ||
) | ||
} |
178 changes: 178 additions & 0 deletions
178
packages/huma-widget/src/components/Lend/solanaEnableAutoRedemption/2-Transfer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import { | ||
convertToShares, | ||
getSentinelAddress, | ||
getTokenAccounts, | ||
SolanaPoolInfo, | ||
} from '@huma-finance/shared' | ||
import React, { useCallback, useEffect, useState } from 'react' | ||
|
||
import { | ||
SolanaPoolState, | ||
useHumaProgram, | ||
useLenderAccounts, | ||
useTrancheTokenAccounts, | ||
} from '@huma-finance/web-shared' | ||
import { | ||
createApproveCheckedInstruction, | ||
TOKEN_2022_PROGRAM_ID, | ||
} from '@solana/spl-token' | ||
import { useWallet } from '@solana/wallet-adapter-react' | ||
import { PublicKey, Transaction } from '@solana/web3.js' | ||
import { BN } from '@coral-xyz/anchor' | ||
import { useAppDispatch } from '../../../hooks/useRedux' | ||
import { setError, setStep } from '../../../store/widgets.reducers' | ||
import { WIDGET_STEP } from '../../../store/widgets.store' | ||
import { LoadingModal } from '../../LoadingModal' | ||
import { SolanaTxSendModal } from '../../SolanaTxSendModal' | ||
|
||
type Props = { | ||
poolInfo: SolanaPoolInfo | ||
poolState: SolanaPoolState | ||
} | ||
|
||
export function Transfer({ | ||
poolInfo, | ||
poolState, | ||
}: Props): React.ReactElement | null { | ||
const dispatch = useAppDispatch() | ||
const { publicKey } = useWallet() | ||
const sentinel = getSentinelAddress(poolInfo.chainId) | ||
const [transaction, setTransaction] = useState<Transaction>() | ||
const { | ||
juniorLenderApprovedAccountPDA, | ||
seniorLenderApprovedAccountPDA, | ||
seniorLenderStateAccount, | ||
juniorLenderStateAccount, | ||
seniorTrancheMintSupply, | ||
juniorTrancheMintSupply, | ||
loading: isLoadingLenderAccounts, | ||
} = useLenderAccounts(poolInfo.chainId, poolInfo.poolName) | ||
const { | ||
seniorTokenAccount, | ||
juniorTokenAccount, | ||
loading: isLoadingTrancheTokenAccounts, | ||
} = useTrancheTokenAccounts(poolInfo) | ||
const program = useHumaProgram(poolInfo.chainId) | ||
|
||
const handleSuccess = useCallback(() => { | ||
dispatch(setStep(WIDGET_STEP.Done)) | ||
}, [dispatch]) | ||
|
||
useEffect(() => { | ||
async function getTx() { | ||
if ( | ||
!publicKey || | ||
transaction || | ||
isLoadingLenderAccounts || | ||
isLoadingTrancheTokenAccounts | ||
) { | ||
return | ||
} | ||
|
||
const tx = new Transaction() | ||
|
||
const { seniorTrancheATA, juniorTrancheATA } = getTokenAccounts( | ||
poolInfo, | ||
publicKey, | ||
) | ||
const sentinelPubKey = new PublicKey(sentinel) | ||
|
||
if (!seniorTokenAccount?.amount && !juniorTokenAccount?.amount) { | ||
dispatch( | ||
setError({ errorMessage: 'Error reading tranche token balance' }), | ||
) | ||
return | ||
} | ||
if ( | ||
seniorTokenAccount && | ||
seniorTokenAccount.amount > 0 && | ||
(seniorTokenAccount.delegate == null || | ||
!sentinelPubKey.equals(seniorTokenAccount.delegate) || | ||
seniorTokenAccount.delegatedAmount < seniorTokenAccount.amount) | ||
) { | ||
const sharesAmount = convertToShares( | ||
new BN(poolState.seniorTrancheAssets ?? 0), | ||
seniorTrancheMintSupply ?? new BN(0), | ||
new BN(seniorTokenAccount.amount.toString()), | ||
) | ||
tx.add( | ||
createApproveCheckedInstruction( | ||
seniorTrancheATA, | ||
new PublicKey(poolInfo.seniorTrancheMint), | ||
new PublicKey(sentinel), // delegate | ||
publicKey, // owner of the wallet | ||
BigInt(sharesAmount.muln(1.1).toString()), // amount | ||
poolInfo.trancheDecimals, | ||
undefined, // multiSigners | ||
TOKEN_2022_PROGRAM_ID, | ||
), | ||
) | ||
} | ||
if ( | ||
juniorTokenAccount && | ||
juniorTokenAccount.amount > 0 && | ||
(juniorTokenAccount.delegate == null || | ||
!sentinelPubKey.equals(juniorTokenAccount.delegate) || | ||
juniorTokenAccount.delegatedAmount < juniorTokenAccount.amount) | ||
) { | ||
const sharesAmount = convertToShares( | ||
new BN(poolState.juniorTrancheAssets ?? 0), | ||
juniorTrancheMintSupply ?? new BN(0), | ||
new BN(juniorTokenAccount.amount.toString()), | ||
) | ||
tx.add( | ||
createApproveCheckedInstruction( | ||
juniorTrancheATA, | ||
new PublicKey(poolInfo.juniorTrancheMint), | ||
new PublicKey(sentinel), // delegate | ||
publicKey, // owner of the wallet | ||
BigInt(sharesAmount.muln(1.1).toString()), // amount | ||
poolInfo.trancheDecimals, | ||
undefined, // multiSigners | ||
TOKEN_2022_PROGRAM_ID, | ||
), | ||
) | ||
} | ||
if (!tx.instructions.length) { | ||
dispatch( | ||
setError({ errorMessage: 'No tranches require Auto-Redemption' }), | ||
) | ||
return | ||
} | ||
|
||
setTransaction(tx) | ||
} | ||
getTx() | ||
}, [ | ||
dispatch, | ||
isLoadingLenderAccounts, | ||
isLoadingTrancheTokenAccounts, | ||
juniorLenderApprovedAccountPDA, | ||
juniorLenderStateAccount, | ||
juniorTokenAccount, | ||
juniorTrancheMintSupply, | ||
poolInfo, | ||
poolState.juniorTrancheAssets, | ||
poolState.seniorTrancheAssets, | ||
program.methods, | ||
publicKey, | ||
seniorLenderApprovedAccountPDA, | ||
seniorLenderStateAccount, | ||
seniorTokenAccount, | ||
seniorTrancheMintSupply, | ||
sentinel, | ||
transaction, | ||
]) | ||
|
||
if (isLoadingLenderAccounts || isLoadingTrancheTokenAccounts) { | ||
return <LoadingModal title='Auto-Redeem' /> | ||
} | ||
|
||
return ( | ||
<SolanaTxSendModal | ||
tx={transaction} | ||
chainId={poolInfo.chainId} | ||
handleSuccess={handleSuccess} | ||
/> | ||
) | ||
} |
47 changes: 47 additions & 0 deletions
47
packages/huma-widget/src/components/Lend/solanaEnableAutoRedemption/3-Success.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { | ||
CloseModalOptions, | ||
SolanaPoolInfo, | ||
timeUtil, | ||
} from '@huma-finance/shared' | ||
import { SolanaPoolState } from '@huma-finance/web-shared' | ||
import dayjs from 'dayjs' | ||
import React from 'react' | ||
import { useAppSelector } from '../../../hooks/useRedux' | ||
import { selectWidgetState } from '../../../store/widgets.selectors' | ||
import { SolanaTxDoneModal } from '../../SolanaTxDoneModal' | ||
|
||
type Props = { | ||
poolInfo: SolanaPoolInfo | ||
poolState: SolanaPoolState | ||
handleAction: (options?: CloseModalOptions) => void | ||
} | ||
|
||
export function Success({ | ||
poolInfo, | ||
poolState, | ||
handleAction, | ||
}: Props): React.ReactElement { | ||
const { solanaSignature } = useAppSelector(selectWidgetState) | ||
|
||
const lockupEndTime = dayjs() | ||
.add(poolState.withdrawalLockupPeriodDays ?? 0, 'day') | ||
.date(1) | ||
const withdrawTime = lockupEndTime.add(1, 'month') | ||
const content = [ | ||
`Redemption request will be automatically submitted on ${timeUtil.timestampToLL( | ||
lockupEndTime.unix(), | ||
)}. Your deposit can be redeemed and yield rewards will stop on ${timeUtil.timestampToLL( | ||
withdrawTime.unix(), | ||
)}.`, | ||
] | ||
|
||
return ( | ||
<SolanaTxDoneModal | ||
handleAction={handleAction} | ||
content={content} | ||
chainId={poolInfo.chainId} | ||
solanaSignature={solanaSignature} | ||
buttonText='DONE' | ||
/> | ||
) | ||
} |
Oops, something went wrong.