Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* dev
  • Loading branch information
EvgeniiVoznyuk authored Jun 30, 2022
1 parent 1729f85 commit b5c1015
Show file tree
Hide file tree
Showing 11 changed files with 1,102 additions and 2 deletions.
9 changes: 9 additions & 0 deletions Strategies/components/DepositModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import SolendModalContent from './SolendModalContent'
import MangoDeposit from './MangoDepositComponent'
import BigNumber from 'bignumber.js'
import { SolendStrategy } from 'Strategies/types/types'
import EverlendModalContent from './EverlendModalContent'

const DepositModal = ({
onClose,
Expand Down Expand Up @@ -49,6 +50,14 @@ const DepositModal = ({
createProposalFcn={createProposalFcn}
></MangoDeposit>
) : null}
{protocolName === 'Everlend' ? (
<EverlendModalContent
proposedInvestment={proposedInvestment}
governedTokenAccount={governedTokenAccount}
handledMint={handledMint}
createProposalFcn={createProposalFcn}
/>
) : null}
</Modal>
)
}
Expand Down
85 changes: 85 additions & 0 deletions Strategies/components/EverlendModalContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import ButtonGroup from '@components/ButtonGroup'
import { useEffect, useState } from 'react'
import { TreasuryStrategy } from 'Strategies/types/types'
import { CreateEverlendProposal } from 'Strategies/protocols/everlend/tools'
import { AssetAccount } from '@utils/uiTypes/assets'
import EverlendDeposit from './everlend/EverlendDeposit'
import EverlendWithdraw from './everlend/EverlendWithdraw'
import { findAssociatedTokenAccount } from '@everlend/common'
import { PublicKey } from '@solana/web3.js'
import useWalletStore from 'stores/useWalletStore'

enum Tabs {
DEPOSIT = 'Deposit',
WITHDRAW = 'Withdraw',
}

interface IProps {
proposedInvestment: TreasuryStrategy & { poolMint: string }
handledMint: string
createProposalFcn: CreateEverlendProposal
governedTokenAccount: AssetAccount
}

const EverlendModalContent = ({
proposedInvestment,
handledMint,
createProposalFcn,
governedTokenAccount,
}: IProps) => {
const [selectedTab, setSelectedTab] = useState(Tabs.DEPOSIT)
const [depositedAmount, setDepositedAmount] = useState(0)
const tabs = Object.values(Tabs)
const connection = useWalletStore((s) => s.connection)

const isSol = governedTokenAccount.isSol
const owner = isSol
? governedTokenAccount!.pubkey
: governedTokenAccount!.extensions!.token!.account.owner

useEffect(() => {
const loadMaxAmount = async () => {
const tokenMintATA = await findAssociatedTokenAccount(
owner,
new PublicKey(proposedInvestment.poolMint)
)
const tokenMintATABalance = await connection.current.getTokenAccountBalance(
tokenMintATA
)
setDepositedAmount(Number(tokenMintATABalance.value.uiAmount))
}
loadMaxAmount()
}, [proposedInvestment, handledMint])

return (
<div>
<div className="pb-4">
<ButtonGroup
activeValue={selectedTab}
onChange={(tab) => setSelectedTab(tab)}
values={tabs}
/>
</div>
{selectedTab === Tabs.DEPOSIT && (
<EverlendDeposit
proposedInvestment={proposedInvestment}
createProposalFcn={createProposalFcn}
governedTokenAccount={governedTokenAccount}
handledMint={handledMint}
depositedAmount={depositedAmount}
/>
)}
{selectedTab === Tabs.WITHDRAW && (
<EverlendWithdraw
proposedInvestment={proposedInvestment}
createProposalFcn={createProposalFcn}
governedTokenAccount={governedTokenAccount}
handledMint={handledMint}
depositedAmount={depositedAmount}
/>
)}
</div>
)
}

export default EverlendModalContent
244 changes: 244 additions & 0 deletions Strategies/components/everlend/EverlendDeposit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import Button, { LinkButton } from '@components/Button'
import Tooltip from '@components/Tooltip'
import Input from '@components/inputs/Input'
import { useState } from 'react'
import { useRouter } from 'next/router'
import useQueryContext from '@hooks/useQueryContext'
import useRealm from '@hooks/useRealm'
import useVotePluginsClientStore from 'stores/useVotePluginsClientStore'
import useWalletStore from 'stores/useWalletStore'
import tokenService from '@utils/services/token'
import BN from 'bn.js'
import {
fmtMintAmount,
getMintMinAmountAsDecimal,
getMintNaturalAmountFromDecimalAsBN,
} from '@tools/sdk/units'
import { RpcContext } from '@solana/spl-governance'
import { PublicKey } from '@solana/web3.js'
import { getProgramVersionForRealm } from '@models/registry/api'
import { AssetAccount } from '@utils/uiTypes/assets'
import { CreateEverlendProposal } from '../../protocols/everlend/tools'
import AdditionalProposalOptions from '@components/AdditionalProposalOptions'
import * as yup from 'yup'
import { precision } from '@utils/formatting'
import { validateInstruction } from '@utils/instructionTools'
import useGovernanceAssets from '@hooks/useGovernanceAssets'
import Loading from '@components/Loading'

interface IProps {
proposedInvestment
handledMint: string
createProposalFcn: CreateEverlendProposal
governedTokenAccount: AssetAccount
depositedAmount: number
}

const EverlendDeposit = ({
proposedInvestment,
createProposalFcn,
governedTokenAccount,
depositedAmount,
}: IProps) => {
const [amount, setAmount] = useState(0)
const tokenSymbol = tokenService.getTokenInfo(
governedTokenAccount.extensions.mint!.publicKey.toBase58()
)?.symbol

const proposalTitle = `Deposit ${amount} ${
tokenSymbol || 'tokens'
} to the Everlend pool`

const [proposalInfo, setProposalInfo] = useState({
title: '',
description: '',
})
const [formErrors, setFormErrors] = useState({})
const [isDepositing, setIsDepositing] = useState(false)
const router = useRouter()
const { fmtUrlWithCluster } = useQueryContext()
const {
realmInfo,
realm,
mint,
councilMint,
ownVoterWeight,
symbol,
} = useRealm()
const [voteByCouncil, setVoteByCouncil] = useState(false)
const client = useVotePluginsClientStore(
(s) => s.state.currentRealmVotingClient
)
const connection = useWalletStore((s) => s.connection)
const wallet = useWalletStore((s) => s.current)

const { canUseTransferInstruction } = useGovernanceAssets()

const treasuryAmount = new BN(
governedTokenAccount.isSol
? governedTokenAccount.extensions.amount!.toNumber()
: governedTokenAccount.extensions.token!.account.amount
)

const mintInfo = governedTokenAccount.extensions?.mint?.account

const mintMinAmount = mintInfo ? getMintMinAmountAsDecimal(mintInfo) : 1
const currentPrecision = precision(mintMinAmount)
const maxAmountFormatted = fmtMintAmount(mintInfo, treasuryAmount)

const handleDeposit = async () => {
const isValid = await validateInstruction({
schema,
form: { amount },
setFormErrors,
})
if (!isValid) {
return
}
try {
setIsDepositing(true)
const rpcContext = new RpcContext(
new PublicKey(realm!.owner),
getProgramVersionForRealm(realmInfo!),
wallet!,
connection.current,
connection.endpoint
)
const ownTokenRecord = ownVoterWeight.getTokenRecordToCreateProposal(
governedTokenAccount!.governance!.account.config,
voteByCouncil
)
const defaultProposalMint = voteByCouncil
? realm?.account.config.councilMint
: !mint?.supply.isZero() ||
realm?.account.config.useMaxCommunityVoterWeightAddin
? realm!.account.communityMint
: !councilMint?.supply.isZero()
? realm!.account.config.councilMint
: undefined

const proposalAddress = await createProposalFcn(
rpcContext,
{
title: proposalInfo.title || proposalTitle,
description: proposalInfo.description,
amountFmt: String(amount),
bnAmount: getMintNaturalAmountFromDecimalAsBN(
amount as number,
governedTokenAccount.extensions.mint!.account.decimals
),
action: 'Deposit',
poolPubKey: proposedInvestment.poolPubKey,
tokenMint: proposedInvestment.handledMint,
poolMint: proposedInvestment.poolMint,
},
realm!,
governedTokenAccount!,
ownTokenRecord,
defaultProposalMint!,
governedTokenAccount!.governance!.account!.proposalCount,
false,
connection,
client
)
const url = fmtUrlWithCluster(
`/dao/${symbol}/proposal/${proposalAddress}`
)
router.push(url)
} catch (e) {
console.error(e)
}
setIsDepositing(false)
}

const schema = yup.object().shape({
amount: yup
.number()
.required('Amount is required')
.max(Number(maxAmountFormatted)),
})

const validateAmountOnBlur = () => {
setAmount(
parseFloat(
Math.max(
Number(mintMinAmount),
Math.min(Number(Number.MAX_SAFE_INTEGER), Number(amount))
).toFixed(currentPrecision)
)
)
}

return (
<div>
<div className="flex my-1.5 text-sm">
Amount
<div className="ml-auto flex items-center text-xs">
<span className="text-fgd-3 mr-1">Bal:</span>{' '}
{Number(maxAmountFormatted)}
<LinkButton
onClick={() => {
setAmount(Number(maxAmountFormatted))
}}
className="font-bold ml-2 text-primary-light"
>
Max
</LinkButton>
</div>
</div>
<Input
type="number"
onChange={(e) => setAmount(e.target.value)}
value={amount}
onBlur={validateAmountOnBlur}
error={formErrors['amount']}
/>
<AdditionalProposalOptions
title={proposalInfo.title}
description={proposalInfo.description}
defaultTitle={proposalTitle}
defaultDescription={`Deposit ${tokenSymbol} into Everlend to mint cTokens and earn interest`}
setTitle={(evt) => setProposalInfo((prev) => ({ ...prev, title: evt }))}
setDescription={(evt) =>
setProposalInfo((prev) => ({ ...prev, description: evt }))
}
voteByCouncil={voteByCouncil}
setVoteByCouncil={setVoteByCouncil}
/>
<div className="border border-fgd-4 p-4 rounded-md mb-6 mt-4 space-y-1 text-sm">
<div className="flex justify-between">
<span className="text-fgd-3">Current Deposits</span>
<span className="font-bold text-fgd-1">
{depositedAmount}{' '}
<span className="font-normal text-fgd-3">{tokenSymbol}</span>
</span>
</div>
<div className="flex justify-between">
<span className="text-fgd-3">Proposed Deposit</span>
<span className="font-bold text-fgd-1">
{amount?.toLocaleString() || (
<span className="font-normal text-red">Enter an amount</span>
)}{' '}
<span className="font-normal text-fgd-3">
{amount && tokenSymbol}
</span>
</span>
</div>
</div>
<div className="mt-4">
<Button
disabled={!amount || !canUseTransferInstruction || isDepositing}
onClick={() => handleDeposit()}
className="w-full"
>
<Tooltip content={''}>
{' '}
{!isDepositing ? 'Propose deposit' : <Loading></Loading>}
</Tooltip>
</Button>
</div>
</div>
)
}

export default EverlendDeposit
Loading

0 comments on commit b5c1015

Please sign in to comment.