Skip to content

Commit

Permalink
fix: extract useStake hook
Browse files Browse the repository at this point in the history
try-catch check token
  • Loading branch information
swkatmask committed May 27, 2024
1 parent 781103f commit f9fe6bc
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 96 deletions.
4 changes: 2 additions & 2 deletions src/components/StakeMaskStatusCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export const StakeMaskStatusCard: ComponentType<StakeMaskStatusCardProps> = ({ .
<VStack spacing={0} color="neutrals.8" align="start">
{rss3 ? (
<Box fontSize="24px" lineHeight="32px" fontWeight={700}>
{formatNumber(+rss3.amount)}
{formatNumber(+rss3.amount, 0)}
</Box>
) : (
<Skeleton height="24px" width="80px" my="4px" />
Expand All @@ -174,7 +174,7 @@ export const StakeMaskStatusCard: ComponentType<StakeMaskStatusCardProps> = ({ .
<VStack spacing={0} color="neutrals.8" align="start">
{ton ? (
<Box fontSize="24px" lineHeight="32px" fontWeight={700}>
{formatNumber(+ton.amount)}
{formatNumber(+ton.amount, 0)}
</Box>
) : (
<Skeleton height="24px" width="80px" my="4px" />
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/formatNumber.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export function formatNumber(num: number | undefined, digits = 2) {
if (!num) return num
return num.toLocaleString('en-US', {
minimumFractionDigits: 2,
minimumFractionDigits: Math.min(2, digits),
maximumFractionDigits: digits,
})
}
5 changes: 3 additions & 2 deletions src/hooks/useCheckStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FIREFLY_API_ROOT } from '@/constants/api'
import { fetchJSON } from '@/helpers/fetchJSON'
import { useLogin } from '@/hooks/useLogin'
import { useAccountStore } from '@/store/accountStore'
import { TwitterAuthorizeResponse } from '@/types/api'
import { CheckTokenResponse } from '@/types/api'

export function useCheckStats() {
const account = useAccount()
Expand All @@ -24,11 +24,12 @@ export function useCheckStats() {
}

const url = urlcat(FIREFLY_API_ROOT, 'v1/mask_stake/check_token')
await fetchJSON<TwitterAuthorizeResponse>(url, {
const res = await fetchJSON<CheckTokenResponse>(url, {
headers: {
Authorization: `Bearer ${jwtToken}`,
},
})
return res.data
} catch {
//
}
Expand Down
121 changes: 121 additions & 0 deletions src/hooks/useStake.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useToast } from '@chakra-ui/react'
import { t } from '@lingui/macro'
import { useMutation } from '@tanstack/react-query'
import { noop } from 'lodash-es'
import { useState } from 'react'
import { TransactionExecutionError, UserRejectedRequestError } from 'viem'
import { useChainId, useConfig, useSwitchChain, useWriteContract } from 'wagmi'
import { waitForTransactionReceipt } from 'wagmi/actions'

import { StakeManagerABI } from '@/abis/stakeManager'
import { TxToastDescription } from '@/components/TxToastDescription'
import { queryClient } from '@/configs/queryClient'
import { formatNumber } from '@/helpers/formatNumber'
import { resolveTxLink } from '@/helpers/resolveTxLink'
import { useCheckStats } from '@/hooks/useCheckStats'
import { useHandleError } from '@/hooks/useHandleError'
import { resultModal } from '@/modals/ResultModal'
import { usePoolStore } from '@/store/poolStore'
import { sleep } from '@/utils/sleep'

interface Params {
amount: bigint
rawAmount: string
}

export function useStake() {
const { chainId, maskTokenAddress, stakeManagerAddress } = usePoolStore()
const config = useConfig()
const currentChainId = useChainId()
const { switchChainAsync, isPending: isSwitchingChain } = useSwitchChain()
const { writeContractAsync, isPending: isStaking } = useWriteContract()

const [waiting, setWaiting] = useState(false)
const [updating, setUpdating] = useState(false)

const toast = useToast({ title: t`Stake` })
const handleError = useHandleError()
const checkStats = useCheckStats()

const mutation = useMutation({
mutationFn: async ({ amount, rawAmount }: Params) => {
if (!maskTokenAddress || !stakeManagerAddress) {
toast({
status: 'error',
title: t`Can not get determination MASK token`,
})
return
}
try {
if (currentChainId !== chainId) {
await switchChainAsync({ chainId })
}
toast({
status: 'loading',
description: t`Confirm this transaction in your wallet.`,
})
const hash = await writeContractAsync({
abi: StakeManagerABI,
address: stakeManagerAddress,
functionName: 'depositAndLock',
args: [amount],
})
const txLink = resolveTxLink(chainId, hash)

toast({
status: 'loading',
description: <TxToastDescription link={txLink} text={t`Transaction submitted!`} color="primary.4" />,
})
setWaiting(true)
const receipt = await waitForTransactionReceipt(config, {
hash,
confirmations: 1,
})
setWaiting(false)
if (receipt.status === 'reverted') {
toast({
status: 'error',
description: <TxToastDescription link={txLink} text={t`Transaction failed.`} />,
})
throw new Error('The transaction gets reverted!')
} else {
toast({
status: 'success',
description: <TxToastDescription link={txLink} text={t`Successfully staked MASK Tokens.`} />,
})
setUpdating(true)
await Promise.allSettled([
checkStats()
.then(async (res) => {
if (res && BigInt(res.height) < receipt.blockNumber) await sleep(6000)
})
.catch(noop)
.finally(() => setUpdating(false)),
resultModal.show({
title: t`Stake`,
message: t`Stake Successfully`,
description: t`You have successfully staked ${formatNumber(+rawAmount, 4)} MASK.`,
}),
])
queryClient.refetchQueries({ queryKey: ['pool-info'] })
queryClient.refetchQueries({ queryKey: ['user-info'] })
}
} catch (err) {
const cause = err instanceof TransactionExecutionError ? err.cause : err
if (cause instanceof UserRejectedRequestError) {
toast({
status: 'error',
description: t`Your wallet cancelled the transaction.`,
})
return
} else if (handleError(err)) return
throw err
} finally {
setWaiting(false)
setUpdating(false)
}
},
})

return [{ updating, waiting, isSwitchingChain, isStaking }, mutation] as const
}
103 changes: 12 additions & 91 deletions src/modals/StakeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,51 +22,40 @@ import { t, Trans } from '@lingui/macro'
import { useConnectModal } from '@rainbow-me/rainbowkit'
import { Link } from '@tanstack/react-router'
import { useLayoutEffect, useMemo, useState } from 'react'
import { parseUnits, TransactionExecutionError, UserRejectedRequestError } from 'viem'
import { useAccount, useBalance, useChainId, useConfig, useSwitchChain, useToken, useWriteContract } from 'wagmi'
import { waitForTransactionReceipt } from 'wagmi/actions'
import { parseUnits } from 'viem'
import { useAccount, useBalance, useToken } from 'wagmi'

import { StakeManagerABI } from '@/abis/stakeManager.ts'
import { StakeRequirementBoundary } from '@/components/StakeRequirementBoundary/index.tsx'
import { StepIcon } from '@/components/StepIcon'
import { TokenIcon } from '@/components/TokenIcon'
import { Tooltip } from '@/components/Tooltip.tsx'
import { TwitterAvatar } from '@/components/TwitterAvatar.tsx'
import { TxToastDescription } from '@/components/TxToastDescription.tsx'
import { queryClient } from '@/configs/queryClient'
import { ZERO } from '@/constants/misc.ts'
import { formatNumber } from '@/helpers/formatNumber'
import { formatSeconds } from '@/helpers/formatSeconds.ts'
import { resolveTxLink } from '@/helpers/resolveTxLink.ts'
import { useCheckStats } from '@/hooks/useCheckStats'
import { useHandleError } from '@/hooks/useHandleError.ts'
import { useLinkTwitter } from '@/hooks/useLinkTwitter'
import { useMaskAllowance } from '@/hooks/useMaskAllowance.ts'
import { usePoolInfo } from '@/hooks/usePoolInfo'
import { useToast } from '@/hooks/useToast.tsx'
import { useStake } from '@/hooks/useStake'
import { useUserInfo } from '@/hooks/useUserInfo.ts'
import { BaseModal } from '@/modals/BaseModal'
import { profileModal } from '@/modals/ProfileModal.tsx'
import { resultModal } from '@/modals/ResultModal.tsx'
import { createUITaskManager } from '@/modals/UITaskManager.tsx'
import { verifyModal } from '@/modals/VerifyModal.tsx'
import { usePoolStore } from '@/store/poolStore'

export function StakeModal(props: ModalProps) {
const account = useAccount()
const config = useConfig()
const { openConnectModal } = useConnectModal()
const { data: pool } = usePoolInfo()
const currentChainId = useChainId()
const { chainId, maskTokenAddress, stakeManagerAddress } = usePoolStore()
const { chainId, maskTokenAddress } = usePoolStore()
const [rawAmount, setRawAmount] = useState('')
const balance = useBalance({ chainId, address: account.address, token: maskTokenAddress })
const allowance = useMaskAllowance()
const maskToken = useToken({ chainId, address: maskTokenAddress })
const [{ loading: linkingTwitter }, linkTwitter] = useLinkTwitter()
const { data: userInfo, isLoading: isLoadingUserInfo } = useUserInfo()
const linkedTwitter = !!userInfo?.twitter_id
const [waiting, setWaiting] = useState(false)
const isHiddenTokenName = useBreakpointValue({ base: true, md: false, lg: false, sm: true })

const decimals = maskToken.data?.decimals || 18
Expand Down Expand Up @@ -99,13 +88,9 @@ export function StakeModal(props: ModalProps) {
}
}, [account.isConnected, isLoadingUserInfo, linkTwitter, linkedTwitter])

const toast = useToast({ title: t`Stake` })
const { switchChainAsync, isPending: isSwitchingChain } = useSwitchChain()
const { writeContractAsync, isPending } = useWriteContract()
const handleError = useHandleError()
const checkStats = useCheckStats()
const [{ updating, waiting, isSwitchingChain, isStaking }, mutation] = useStake()

const loading = allowance.isLoading || isPending || waiting || isSwitchingChain
const loading = allowance.isLoading || isStaking || waiting || updating || isSwitchingChain
const disabled = allowance.isLoading || amount === ZERO
return (
<BaseModal title={t`Stake`} width={572} {...props}>
Expand Down Expand Up @@ -312,80 +297,16 @@ export function StakeModal(props: ModalProps) {
<ScaleFade in initialScale={0.5} key="stake-button">
<Button
isLoading={loading}
loadingText={waiting ? t`Waiting for transaction confirmation` : ''}
loadingText={waiting ? t`Waiting for transaction confirmation` : updating ? 'Updating' : ''}
w="100%"
className="purple-gradient-button"
rounded={50}
isDisabled={disabled}
onClick={async () => {
if (!maskTokenAddress || !stakeManagerAddress) {
toast({
status: 'error',
title: t`Can not get determination MASK token`,
})
return
}
try {
if (currentChainId !== chainId) {
await switchChainAsync({ chainId })
}
toast({
status: 'loading',
description: t`Confirm this transaction in your wallet.`,
})
const hash = await writeContractAsync({
abi: StakeManagerABI,
address: stakeManagerAddress,
functionName: 'depositAndLock',
args: [amount],
})
const txLink = resolveTxLink(chainId, hash)

toast({
status: 'loading',
description: (
<TxToastDescription link={txLink} text={t`Transaction submitted!`} color="primary.4" />
),
})
setWaiting(true)
const receipt = await waitForTransactionReceipt(config, {
hash,
confirmations: 1,
})
if (receipt.status === 'reverted') {
toast({
status: 'error',
description: <TxToastDescription link={txLink} text={t`Transaction failed.`} />,
})
throw new Error('The transaction gets reverted!')
} else {
toast({
status: 'success',
description: <TxToastDescription link={txLink} text={t`Successfully staked MASK Tokens.`} />,
})
await checkStats()
queryClient.refetchQueries({ queryKey: ['pool-info'] })
queryClient.refetchQueries({ queryKey: ['user-info'] })
await resultModal.show({
title: t`Stake`,
message: t`Stake Successfully`,
description: t`You have successfully staked ${formatNumber(+rawAmount, 4)} MASK.`,
})
props.onClose()
}
} catch (err) {
const cause = err instanceof TransactionExecutionError ? err.cause : err
if (cause instanceof UserRejectedRequestError) {
toast({
status: 'error',
description: t`Your wallet cancelled the transaction.`,
})
return
} else if (handleError(err)) return
throw err
} finally {
setWaiting(false)
}
onClick={() => {
mutation.mutate({
amount,
rawAmount,
})
}}
>
{account.isConnected ? t`Stake` : t`Please connect first`}
Expand Down
5 changes: 5 additions & 0 deletions src/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,8 @@ export interface AddressSecurity {
}

export type AddressSecurityResponse = Response<AddressSecurity>

export type CheckTokenResponse = Response<{
address: string
height: number
}>

0 comments on commit f9fe6bc

Please sign in to comment.