Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deposit tokens button opens modal to set amount #1862

Merged
merged 9 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Tooltip from './Tooltip'
import Header from './Header'
import GradientCheckmarkCircle from './NewRealmWizard/components/GradientCheckmarkCircle'
import SimpleCheckmarkCircle from './NewRealmWizard/components/SimpleCheckmarkCircle'
interface ButtonProps {
export interface ButtonProps {
className?: string
isLoading?: boolean
onClick?: () => void
Expand Down Expand Up @@ -123,7 +123,7 @@ export const NewButton: FunctionComponent<NewButtonProps> = ({
'py-3 px-2 h-[64px] min-w-[208px] text-fgd-1 border border-fgd-3 focus:border-fgd-1 hover:bg-fgd-1 hover:text-bkg-1 active:bg-fgd-2 active:text-bkg-1 active:border-none disabled:bg-fgd-4 disabled:text-bkg-1 disabled:border-none '
} else {
// this is a primary button
// TODO: make sure this using the typogrpahic class for CTAs
// TODO: make sure this using the typographic class for CTAs
classNames +=
'py-4 px-2 h-[64px] min-w-[208px] text-bkg-1 bg-fgd-1 hover:bg-fgd-2 active:bg-fgd-3 active:border-none focus:border-2 focus:border-[#00E4FF] disabled:bg-fgd-4'
}
Expand Down Expand Up @@ -191,7 +191,8 @@ export const ProposalTypeRadioButton: FunctionComponent<NewButtonProps> = ({
}

if (!disabled) {
classNames += 'hover:bg-bkg-4 hover:border-fgd-1 hover:bg-white/30 hover:text-black border-fgd-3'
classNames +=
'hover:bg-bkg-4 hover:border-fgd-1 hover:bg-white/30 hover:text-black border-fgd-3'
} else {
classNames += ' bg-none text-fgd-4 border-bkg-4'
}
Expand All @@ -201,8 +202,14 @@ export const ProposalTypeRadioButton: FunctionComponent<NewButtonProps> = ({
<button className={classNames} type="button" disabled={disabled} {...props}>
<div className="flex items-center pl-4 space-x-3 md:pl-0 justify-center">
<SimpleCheckmarkCircle selected={selected} />
<div className={`inline ml-2 text-sm ${selected ? 'text-bkg-1' : 'text-fgd-3'}`}>{children}</div>
<div
className={`inline ml-2 text-sm ${
selected ? 'text-bkg-1' : 'text-fgd-3'
}`}
>
{children}
</div>
</div>
</button>
)
}
}
87 changes: 87 additions & 0 deletions components/DepositTokensButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { BigNumber } from 'bignumber.js'
import Button, { ButtonProps, SecondaryButton } from '@components/Button'
import BN from 'bn.js'
import useUserGovTokenAccountQuery from '@hooks/useUserGovTokenAccount'
import { useDepositCallback } from './GovernancePower/Vanilla/useDepositCallback'
import useWalletOnePointOh from '@hooks/useWalletOnePointOh'
import Modal from './Modal'
import { useState } from 'react'
import useGoverningTokenMint from '@hooks/selectedRealm/useGoverningTokenMint'
import { useMintInfoByPubkeyQuery } from '@hooks/queries/mintInfo'
import Input from './inputs/Input'

export const DepositTokensButton = ({
role,
as = 'secondary',
...props
}: { role: 'community' | 'council'; as?: 'primary' | 'secondary' } & Omit<
ButtonProps,
'onClick' | 'tooltipMessage'
>) => {
const wallet = useWalletOnePointOh()
const connected = !!wallet?.connected

const userAta = useUserGovTokenAccountQuery(role).data?.result
const depositAmount = userAta?.amount
? new BigNumber(userAta.amount.toString())
: new BigNumber(0)

const hasTokensInWallet = depositAmount.isGreaterThan(0)
const depositTooltipContent = !connected
? 'Connect your wallet to deposit'
: !hasTokensInWallet
? "You don't have any governance tokens in your wallet to deposit."
: undefined

const ButtonToUse = as === 'primary' ? Button : SecondaryButton
const [openModal, setOpenModal] = useState(false)
const [amount, setAmount] = useState('')
const mint = useGoverningTokenMint(role)
const mintInfo = useMintInfoByPubkeyQuery(mint).data?.result

const humanReadableMax =
mintInfo === undefined
? undefined
: depositAmount.shiftedBy(-mintInfo.decimals).toNumber()

const deposit = useDepositCallback(role)
return (
<>
<ButtonToUse
{...props}
onClick={() => setOpenModal(true)}
tooltipMessage={depositTooltipContent}
disabled={!connected || !hasTokensInWallet || props.disabled}
>
Deposit
</ButtonToUse>
{openModal && (
<Modal isOpen={openModal} onClose={() => setOpenModal(false)}>
<div className="flex flex-col gap-y-4">
<h2>Deposit tokens</h2>
<Input
placeholder={humanReadableMax?.toString() + ' (max)'}
type="number"
label="Amount to deposit"
value={amount}
onChange={(e) => setAmount(e.target.value)}
max={humanReadableMax}
/>
<Button
onClick={async () => {
if (mintInfo === undefined) throw new Error()
const nativeAmount = new BN(
new BigNumber(amount).shiftedBy(mintInfo.decimals).toString()
)
await deposit(nativeAmount)
setOpenModal(false)
}}
>
Confirm
</Button>
</div>
</Modal>
)}
</>
)
}
9 changes: 7 additions & 2 deletions components/GovernancePower/GovernancePowerForRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import LockedCommunityVotingPower from '@components/ProposalVotingPower/LockedCo
import NftVotingPower from '@components/ProposalVotingPower/NftVotingPower'
import LockedCommunityNFTRecordVotingPower from '@components/ProposalVotingPower/LockedCommunityNFTRecordVotingPower'
import VanillaVotingPower from './Vanilla/VanillaVotingPower'
import { Deposit } from './Vanilla/Deposit'

export default function GovernancePowerForRole({
role,
Expand Down Expand Up @@ -39,7 +40,9 @@ export default function GovernancePowerForRole({
<>
{role === 'community' ? (
kind === 'vanilla' ? (
<VanillaVotingPower role="community" {...props} />
<VanillaVotingPower role="community" {...props}>
<Deposit role="community" />
</VanillaVotingPower>
) : kind === 'VSR' ? (
<LockedCommunityVotingPower />
) : kind === 'NFT' ? (
Expand All @@ -48,7 +51,9 @@ export default function GovernancePowerForRole({
<LockedCommunityNFTRecordVotingPower />
) : null
) : kind === 'vanilla' ? (
<VanillaVotingPower role="council" {...props} />
<VanillaVotingPower role="council" {...props}>
<Deposit role="council" />
</VanillaVotingPower>
) : null}
</>
)
Expand Down
69 changes: 16 additions & 53 deletions components/GovernancePower/Vanilla/Deposit.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,38 @@
import { BigNumber } from 'bignumber.js'
import { SecondaryButton } from '@components/Button'
import useWalletOnePointOh from '@hooks/useWalletOnePointOh'
import { useRealmQuery } from '@hooks/queries/realm'
import { useConnection } from '@solana/wallet-adapter-react'
import { useAsync } from 'react-async-hook'
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
Token,
TOKEN_PROGRAM_ID,
} from '@solana/spl-token'
import BN from 'bn.js'
import { fetchMintInfoByPubkey } from '@hooks/queries/mintInfo'
import { fetchTokenAccountByPubkey } from '@hooks/queries/tokenAccount'
import { useDepositCallback } from './useDepositCallback'
import { useMintInfoByPubkeyQuery } from '@hooks/queries/mintInfo'
import { getMintMetadata } from '@components/instructions/programs/splToken'
import useUserGovTokenAccountQuery from '@hooks/useUserGovTokenAccount'
import { DepositTokensButton } from '@components/DepositTokensButton'

/** Contextual deposit, shows only if relevant */
export const Deposit = ({ role }: { role: 'community' | 'council' }) => {
const realm = useRealmQuery().data?.result
const wallet = useWalletOnePointOh()
const walletPk = wallet?.publicKey ?? undefined
const mint =
role === 'community'
? realm?.account.communityMint
: realm?.account.config.councilMint

const { connection } = useConnection()
const mintInfo = useMintInfoByPubkeyQuery(mint).data?.result
const userAta = useUserGovTokenAccountQuery(role).data?.result

const { result } = useAsync(async () => {
if (realm === undefined || walletPk === undefined) return undefined
const mint =
role === 'community'
? realm.account.communityMint
: realm.account.config.councilMint
if (mint === undefined) return undefined

const userAtaPk = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID
TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID
mint, // mint
walletPk // owner
)

const { result: userAta } = await fetchTokenAccountByPubkey(
connection,
userAtaPk
)
const { result: mintInfo } = await fetchMintInfoByPubkey(connection, mint)
return { mint, userAta, mintInfo } as const
}, [connection, realm, role, walletPk])

const depositAmount = result?.userAta?.amount
? new BigNumber(result.userAta.amount.toString())
const depositAmount = userAta?.amount
? new BigNumber(userAta.amount.toString())
: new BigNumber(0)

const tokenName =
getMintMetadata(result?.mint)?.name ?? realm?.account.name ?? ''

const deposit = useDepositCallback(role)
const tokenName = getMintMetadata(mint)?.name ?? realm?.account.name ?? ''

return !depositAmount.isGreaterThan(0) ? null : (
<>
<div className="mt-3 text-xs text-white/50">
You have{' '}
{result?.mintInfo
? depositAmount.shiftedBy(-result.mintInfo.decimals).toFormat()
{mintInfo
? depositAmount.shiftedBy(-mintInfo.decimals).toFormat()
: depositAmount.toFormat()}{' '}
more {tokenName} votes in your wallet. Do you want to deposit them to
increase your voting power in this Dao?
</div>
<SecondaryButton
className="mt-4 w-48"
onClick={() => deposit(new BN(depositAmount.toString()))}
>
Deposit
</SecondaryButton>
<DepositTokensButton className="mt-4 w-48" role={role} as="secondary" />
</>
)
}
59 changes: 29 additions & 30 deletions components/GovernancePower/Vanilla/VanillaVotingPower.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
} from '@hooks/queries/addresses/tokenOwnerRecord'
import { useAsync } from 'react-async-hook'
import BN from 'bn.js'
import { Deposit } from './Deposit'
import { getMintMetadata } from '@components/instructions/programs/splToken'
import VotingPowerPct from '@components/ProposalVotingPower/VotingPowerPct'
import { useSelectedDelegatorStore } from 'stores/useSelectedDelegatorStore'
Expand All @@ -25,11 +24,13 @@ interface Props {
className?: string
role: 'community' | 'council'
hideIfZero?: boolean
children?: React.ReactNode
}

export default function VanillaVotingPower({
role,
hideIfZero,
children,
...props
}: Props) {
const realm = useRealmQuery().data?.result
Expand Down Expand Up @@ -120,38 +121,36 @@ export default function VanillaVotingPower({
disabled && 'hidden'
)}
>
{
<div className={'p-3 rounded-md bg-bkg-1'}>
<div className="text-fgd-3 text-xs">
{tokenName}
{role === 'council' ? ' Council' : ''} Votes
</div>
<div className="flex items-center justify-between mt-1">
<div className=" flex flex-row gap-x-2">
<div className="text-xl font-bold text-fgd-1 hero-text">
{formattedTotal ?? 0}
</div>
<div className="text-xs text-fgd-3">
{selectedDelegator !== undefined ? (
// if we're acting as a specific delegator, show that instead of the delegator aggregation
<>(as {abbreviateAddress(selectedDelegator)})</>
) : formattedDelegatorsAmount !== undefined &&
formattedDelegatorsAmount !== '0' ? (
<>({formattedDelegatorsAmount} from delegators)</>
) : null}
</div>
<div className={'p-3 rounded-md bg-bkg-1'}>
<div className="text-fgd-3 text-xs">
{tokenName}
{role === 'council' ? ' Council' : ''} Votes
</div>
<div className="flex items-center justify-between mt-1">
<div className=" flex flex-row gap-x-2">
<div className="text-xl font-bold text-fgd-1 hero-text">
{formattedTotal ?? 0}
</div>
<div className="text-xs text-fgd-3">
{selectedDelegator !== undefined ? (
// if we're acting as a specific delegator, show that instead of the delegator aggregation
<>(as {abbreviateAddress(selectedDelegator)})</>
) : formattedDelegatorsAmount !== undefined &&
formattedDelegatorsAmount !== '0' ? (
<>({formattedDelegatorsAmount} from delegators)</>
) : null}
</div>

{mintInfo && (
<VotingPowerPct
amount={new BigNumber(totalAmount.toString())}
total={new BigNumber(mintInfo.supply.toString())}
/>
)}
</div>

{mintInfo && (
<VotingPowerPct
amount={new BigNumber(totalAmount.toString())}
total={new BigNumber(mintInfo.supply.toString())}
/>
)}
</div>
}
<Deposit role={role} />
</div>
{children}
</div>
)
}
Loading