diff --git a/apps/main/src/dex/components/PagePool/components/TransferActions.tsx b/apps/main/src/dex/components/PagePool/components/TransferActions.tsx index c25f320c6d..8f3d673029 100644 --- a/apps/main/src/dex/components/PagePool/components/TransferActions.tsx +++ b/apps/main/src/dex/components/PagePool/components/TransferActions.tsx @@ -3,7 +3,6 @@ import { useConnection } from 'wagmi' import FormConnectWallet from '@/dex/components/FormConnectWallet' import AlertSeedAmounts from '@/dex/components/PagePool/components/AlertSeedAmounts' import type { TransferProps } from '@/dex/components/PagePool/types' -import { useSignerAddress } from '@/dex/entities/signer' import { usePoolIdByAddressOrId } from '@/dex/hooks/usePoolIdByAddressOrId' import { usePoolTokenBalances } from '@/dex/hooks/usePoolTokenBalances' import useTokenAlert from '@/dex/hooks/useTokenAlert' @@ -23,7 +22,7 @@ const TransferActions = ({ loading?: boolean children: ReactNode } & Pick) => { - const { data: signerAddress } = useSignerAddress() + const { address: signerAddress } = useConnection() const { rChainId, rPoolIdOrAddress } = routerParams const poolId = usePoolIdByAddressOrId({ chainId: rChainId, poolIdOrAddress: rPoolIdOrAddress }) const alert = useTokenAlert(poolData?.tokenAddressesAll ?? []) diff --git a/apps/main/src/dex/entities/gauge/model/estimate-gas-options.ts b/apps/main/src/dex/entities/gauge/model/estimate-gas-options.ts index c4a72a057a..58513ce127 100644 --- a/apps/main/src/dex/entities/gauge/model/estimate-gas-options.ts +++ b/apps/main/src/dex/entities/gauge/model/estimate-gas-options.ts @@ -10,13 +10,14 @@ import { gaugeKeys } from './query-keys' import { depositRewardAvailable, depositRewardIsApproved } from './query-options' export const estimateGasDepositRewardApprove = queryFactory({ - queryKey: ({ rewardTokenId, amount, ...gaugeParams }: DepositRewardApproveParams) => + queryKey: ({ rewardTokenId, amount, userBalance, ...gaugeParams }: DepositRewardApproveParams) => [ ...rootKeys.gauge({ ...gaugeParams }), ...gaugeKeys.estimateGas(), 'depositRewardApprove', { rewardTokenId }, { amount }, + { userBalance }, ] as const, queryFn: api.queryEstimateGasDepositRewardApprove, refetchInterval: '1m', @@ -43,7 +44,7 @@ export const estimateGasAddRewardToken = queryFactory({ }) export const estimateGasDepositReward = queryFactory({ - queryKey: ({ rewardTokenId, amount, epoch, ...gaugeParams }: DepositRewardParams) => + queryKey: ({ rewardTokenId, amount, epoch, userBalance, ...gaugeParams }: DepositRewardParams) => [ ...rootKeys.gauge({ ...gaugeParams }), ...gaugeKeys.estimateGas(), @@ -51,6 +52,7 @@ export const estimateGasDepositReward = queryFactory({ { rewardTokenId }, { amount }, { epoch }, + { userBalance }, ] as const, queryFn: api.queryEstimateGasDepositReward, refetchInterval: '1m', diff --git a/apps/main/src/dex/entities/gauge/model/gauge-validation.ts b/apps/main/src/dex/entities/gauge/model/gauge-validation.ts index 4f215fe472..440a5914d6 100644 --- a/apps/main/src/dex/entities/gauge/model/gauge-validation.ts +++ b/apps/main/src/dex/entities/gauge/model/gauge-validation.ts @@ -1,5 +1,4 @@ import { enforce, group, test } from 'vest' -import useStore from '@/dex/store/useStore' import { formatNumber } from '@ui/utils' import { t } from '@ui-kit/lib/i18n' import { TIME_FRAMES } from '@ui-kit/lib/model' @@ -15,20 +14,23 @@ import { AddRewardParams, DepositRewardApproveParams, DepositRewardParams } from export const gaugeAddRewardValidationGroup = ({ distributorId, rewardTokenId }: AddRewardParams) => group('gaugeAddRewardValidationGroup', () => { test('distributorId', () => addressValidationFn(distributorId)) - test('rewardTokenId', () => tokenIdValidationFn(rewardTokenId)) }) -export const gaugeDepositRewardApproveValidationGroup = ({ rewardTokenId, amount }: DepositRewardApproveParams) => +export const gaugeDepositRewardApproveValidationGroup = ({ + rewardTokenId, + amount, + userBalance, +}: DepositRewardApproveParams) => group('gaugeDepositRewardApproveValidationGroup', () => { test('rewardTokenId', () => tokenIdValidationFn(rewardTokenId)) - test('amount', () => validateAmount({ rewardTokenId, amount })) + test('amount', () => validateAmount({ rewardTokenId, amount, userBalance })) }) -export const gaugeDepositRewardValidationGroup = ({ rewardTokenId, amount, epoch }: DepositRewardParams) => +export const gaugeDepositRewardValidationGroup = ({ rewardTokenId, amount, epoch, userBalance }: DepositRewardParams) => group('gaugeDepositRewardValidationGroup', () => { test('rewardTokenId', () => tokenIdValidationFn(rewardTokenId)) - test('amount', () => validateAmount({ rewardTokenId, amount })) + test('amount', () => validateAmount({ rewardTokenId, amount, userBalance })) test('epoch', () => { enforce(epoch) .message(t`Epoch is required`) @@ -55,18 +57,17 @@ export const gaugeDepositRewardValidationSuite = createValidationSuite((data: De gaugeDepositRewardValidationGroup(data) }) -function validateAmount({ rewardTokenId, amount }: DepositRewardApproveParams) { +function validateAmount({ rewardTokenId, amount, userBalance }: DepositRewardApproveParams) { amountValidationFn(amount) if (!rewardTokenId || !amount) return - const state = useStore.getState() - const userBalancesMapper = state.userBalances.userBalancesMapper - const tokenBalance = userBalancesMapper[rewardTokenId] - - if (!tokenBalance) return + enforce(userBalance).condition((userBalance) => ({ + pass: userBalance != null && +userBalance > 0, + message: t`Wallet balance is zero for the selected reward token`, + })) enforce(amount).condition((amount) => ({ - pass: +amount < +tokenBalance, - message: t`Amount ${formatNumber(amount, { decimals: 5 })} > wallet balance ${formatNumber(tokenBalance, { decimals: 5 })}`, + pass: +amount <= +userBalance!, + message: t`Amount ${formatNumber(amount, { decimals: 5 })} > wallet balance ${formatNumber(userBalance, { decimals: 5 })}`, })) } diff --git a/apps/main/src/dex/entities/gauge/types.ts b/apps/main/src/dex/entities/gauge/types.ts index 3e07a24898..84392b2789 100644 --- a/apps/main/src/dex/entities/gauge/types.ts +++ b/apps/main/src/dex/entities/gauge/types.ts @@ -1,6 +1,7 @@ import type { Address } from 'viem' import { GaugeQuery } from '@ui-kit/lib/model/query' import { FieldsOf } from '@ui-kit/lib/validation' +import type { Decimal } from '@ui-kit/utils' export type AddReward = { rewardTokenId: Address @@ -13,6 +14,7 @@ export type AddRewardMutation = FieldsOf export type DepositRewardApprove = { rewardTokenId: Address amount: number | string + userBalance?: Decimal } export type DepositRewardApproveQuery = GaugeQuery & DepositRewardApprove export type DepositRewardApproveParams = FieldsOf diff --git a/apps/main/src/dex/entities/signer/index.ts b/apps/main/src/dex/entities/signer/index.ts deleted file mode 100644 index 99b4096879..0000000000 --- a/apps/main/src/dex/entities/signer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './lib' diff --git a/apps/main/src/dex/entities/signer/lib.ts b/apps/main/src/dex/entities/signer/lib.ts deleted file mode 100644 index c7139a3bbb..0000000000 --- a/apps/main/src/dex/entities/signer/lib.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useMemo } from 'react' -import type { Address } from 'viem' -import useStore from '@/dex/store/useStore' -import { useWallet } from '@ui-kit/features/connect-wallet' - -export const useSignerAddress = (): { data: Address | undefined } => { - const { wallet } = useWallet.getState() - const signerAddress = wallet?.account?.address - return { data: signerAddress } -} - -export const useIsSignerConnected = () => { - const { data: signerAddress } = useSignerAddress() - return { data: !!signerAddress } -} - -export const useTokensBalances = ( - tokens: (Address | undefined)[], -): { data: (string | undefined)[]; isLoading: boolean } => { - const userBalancesMapper = useStore((state) => state.userBalances.userBalancesMapper) - const userBalancesLoading = useStore((state) => state.userBalances.loading) - - const tokensKey = JSON.stringify(tokens) - - const balances = useMemo(() => { - if (userBalancesLoading) return tokens.map(() => undefined) - return tokens.map((token) => (token ? userBalancesMapper[token] : undefined)) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [tokensKey, userBalancesMapper, userBalancesLoading]) - - return { data: balances, isLoading: userBalancesLoading } -} diff --git a/apps/main/src/dex/features/add-gauge-reward-token/ui/AddRewardToken.tsx b/apps/main/src/dex/features/add-gauge-reward-token/ui/AddRewardToken.tsx index 7c9e0faac5..43e9a13a96 100644 --- a/apps/main/src/dex/features/add-gauge-reward-token/ui/AddRewardToken.tsx +++ b/apps/main/src/dex/features/add-gauge-reward-token/ui/AddRewardToken.tsx @@ -1,10 +1,10 @@ import { useCallback } from 'react' import { FormProvider, useForm } from 'react-hook-form' import { zeroAddress } from 'viem' +import { useConnection } from 'wagmi' import AlertFormError from '@/dex/components/AlertFormError' import { useAddRewardToken, useGaugeRewardsDistributors, useIsDepositRewardAvailable } from '@/dex/entities/gauge' import { useNetworkByChain } from '@/dex/entities/networks' -import { useSignerAddress } from '@/dex/entities/signer' import { addGaugeRewardTokenValidationSuite } from '@/dex/features/add-gauge-reward-token/model' import type { AddRewardFormValues, AddRewardTokenProps } from '@/dex/features/add-gauge-reward-token/types' import { @@ -22,7 +22,7 @@ import { t } from '@ui-kit/lib/i18n' import { formDefaultOptions } from '@ui-kit/lib/model/form' export const AddRewardToken = ({ chainId, poolId }: AddRewardTokenProps) => { - const { data: signerAddress } = useSignerAddress() + const { address: signerAddress } = useConnection() const { isFetching: isFetchingGaugeRewardsDistributors } = useGaugeRewardsDistributors({ chainId, poolId }) diff --git a/apps/main/src/dex/features/deposit-gauge-reward/model/default-values.ts b/apps/main/src/dex/features/deposit-gauge-reward/model/default-values.ts index b9b453599d..8aa790bda0 100644 --- a/apps/main/src/dex/features/deposit-gauge-reward/model/default-values.ts +++ b/apps/main/src/dex/features/deposit-gauge-reward/model/default-values.ts @@ -5,6 +5,7 @@ import { TIME_FRAMES } from '@ui-kit/lib/model' export const DepositRewardDefaultValues: DepositRewardFormValues = { rewardTokenId: zeroAddress, amount: '', + userBalance: undefined, epoch: TIME_FRAMES.WEEK, step: DepositRewardStep.APPROVAL, } as const diff --git a/apps/main/src/dex/features/deposit-gauge-reward/types/index.ts b/apps/main/src/dex/features/deposit-gauge-reward/types/index.ts index e854d084e4..6ee0fff494 100644 --- a/apps/main/src/dex/features/deposit-gauge-reward/types/index.ts +++ b/apps/main/src/dex/features/deposit-gauge-reward/types/index.ts @@ -1,4 +1,5 @@ -import { Address } from 'viem' +import type { Address } from 'viem' +import type { Decimal } from '@ui-kit/utils' export enum DepositRewardStep { APPROVAL = 'APPROVAL', @@ -9,6 +10,7 @@ export enum DepositRewardStep { export interface DepositRewardFormValues { rewardTokenId?: Address amount?: string + userBalance?: Decimal epoch?: number step: DepositRewardStep } diff --git a/apps/main/src/dex/features/deposit-gauge-reward/ui/ActionsStepper.tsx b/apps/main/src/dex/features/deposit-gauge-reward/ui/ActionsStepper.tsx index 9e110f6ca6..fc0e07382c 100644 --- a/apps/main/src/dex/features/deposit-gauge-reward/ui/ActionsStepper.tsx +++ b/apps/main/src/dex/features/deposit-gauge-reward/ui/ActionsStepper.tsx @@ -32,6 +32,7 @@ export const DepositStepper = ({ chainId, poolId }: { chainId: ChainId; poolId: const amount = watch('amount') const rewardTokenId = watch('rewardTokenId') const step = watch('step') + const userBalance = watch('userBalance') const { mutate: depositRewardApprove, isPending: isPendingDepositRewardApprove } = useDepositRewardApprove({ chainId, @@ -59,6 +60,7 @@ export const DepositStepper = ({ chainId, poolId }: { chainId: ChainId; poolId: { rewardTokenId: getValues('rewardTokenId'), amount: getValues('amount'), + userBalance: getValues('userBalance'), }, { onSuccess: onApproveSuccess, onError: onApproveError }, ) @@ -70,6 +72,7 @@ export const DepositStepper = ({ chainId, poolId }: { chainId: ChainId; poolId: rewardTokenId: getValues('rewardTokenId'), amount: getValues('amount'), epoch: getValues('epoch'), + userBalance: getValues('userBalance'), }, { onSuccess: (data: string) => { @@ -91,6 +94,7 @@ export const DepositStepper = ({ chainId, poolId }: { chainId: ChainId; poolId: poolId, rewardTokenId, amount, + userBalance, }) useLayoutEffect(() => { diff --git a/apps/main/src/dex/features/deposit-gauge-reward/ui/AmountTokenInput.tsx b/apps/main/src/dex/features/deposit-gauge-reward/ui/AmountTokenInput.tsx index b79ae0a8d7..36750e5a1b 100644 --- a/apps/main/src/dex/features/deposit-gauge-reward/ui/AmountTokenInput.tsx +++ b/apps/main/src/dex/features/deposit-gauge-reward/ui/AmountTokenInput.tsx @@ -2,13 +2,13 @@ import { MouseEvent, useCallback, useMemo } from 'react' import { useFormContext } from 'react-hook-form' import { Address, isAddressEqual } from 'viem' import { ethAddress } from 'viem' +import { useConnection } from 'wagmi' import { useDepositRewardApproveIsMutating, useDepositRewardIsMutating, useGaugeRewardsDistributors, } from '@/dex/entities/gauge' import { useNetworkByChain } from '@/dex/entities/networks' -import { useIsSignerConnected, useSignerAddress, useTokensBalances } from '@/dex/entities/signer' import { type DepositRewardFormValues, DepositRewardStep } from '@/dex/features/deposit-gauge-reward/types' import { FlexItemAmount, @@ -26,6 +26,7 @@ import { formatNumber } from '@ui/utils' import { type TokenOption, TokenSelector } from '@ui-kit/features/select-token' import { t } from '@ui-kit/lib/i18n' import { useTokenUsdRates } from '@ui-kit/lib/model/entities/token-usd-rate' +import { useTokenBalances } from '@ui-kit/queries/token-balance.query' export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId: string }) => { const { setValue, getValues, formState, watch } = useFormContext() @@ -33,19 +34,12 @@ export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId const amount = watch('amount') const epoch = watch('epoch') - const { data: signerAddress } = useSignerAddress() - const { data: haveSigner } = useIsSignerConnected() + const { address: signerAddress } = useConnection() const isMaxLoading = useStore((state) => state.quickSwap.isMaxLoading) const { data: { networkId }, } = useNetworkByChain({ chainId }) - const userBalancesMapper = useStore((state) => state.userBalances.userBalancesMapper) - const userTokens = Object.entries(userBalancesMapper) - .filter(([, balance]) => parseFloat(balance ?? '0') > 0) - .map(([address]) => address) - const { data: tokenPrices } = useTokenUsdRates({ chainId, tokenAddresses: userTokens }) - const { tokensMapper } = useTokensMapper(chainId) const { data: rewardDistributors, isPending: isPendingRewardDistributors } = useGaugeRewardsDistributors({ @@ -56,11 +50,6 @@ export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId const isMutatingDepositRewardApprove = useDepositRewardApproveIsMutating({ chainId, poolId, rewardTokenId, amount }) const isMutatingDepositReward = useDepositRewardIsMutating({ chainId, poolId, rewardTokenId, amount, epoch }) - const { - data: [tokenBalance], - isLoading: isTokenBalancesLoading, - } = useTokensBalances([rewardTokenId]) - const filteredTokens = useMemo(() => { if (isPendingRewardDistributors || !rewardDistributors || !signerAddress) return [] @@ -89,6 +78,19 @@ export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId }, [isPendingRewardDistributors, rewardDistributors, signerAddress, tokensMapper, getValues, networkId, setValue]) const token = filteredTokens.find((x) => x.address === rewardTokenId) + const tokenAddresses = filteredTokens.map((t) => t.address) + + const { data: tokenPrices } = useTokenUsdRates({ chainId, tokenAddresses }) + const { data: tokenBalances, isLoading: isTokenBalancesLoading } = useTokenBalances({ + chainId, + userAddress: signerAddress, + tokenAddresses, + }) + + const rewardTokenBalance = useMemo( + () => rewardTokenId && tokenBalances && tokenBalances[rewardTokenId], + [rewardTokenId, tokenBalances], + ) const onChangeAmount = useCallback( (amount: string) => { @@ -109,10 +111,10 @@ export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId const onMaxButtonClick = useCallback( (e?: MouseEvent) => { e?.preventDefault() - if (!tokenBalance) return - setValue('amount', tokenBalance, { shouldValidate: true }) + if (!rewardTokenBalance) return + setValue('amount', rewardTokenBalance, { shouldValidate: true }) }, - [tokenBalance, setValue], + [rewardTokenBalance, setValue], ) const isDisabled = isMutatingDepositReward || isMutatingDepositRewardApprove @@ -129,10 +131,10 @@ export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId id="deposit-amount" type="number" labelProps={ - haveSigner && { + signerAddress && { label: t`Avail.`, descriptionLoading: isTokenBalancesLoading, - description: formatNumber(tokenBalance, { decimals: 5 }), + description: formatNumber(rewardTokenBalance), } } testId="deposit-amount" @@ -154,7 +156,7 @@ export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId selectedToken={token} tokens={filteredTokens} disabled={isDisabled} - balances={userBalancesMapper} + balances={tokenBalances} tokenPrices={tokenPrices} onToken={onChangeToken} /> diff --git a/apps/main/src/dex/features/deposit-gauge-reward/ui/DepositReward.tsx b/apps/main/src/dex/features/deposit-gauge-reward/ui/DepositReward.tsx index 96c02352e0..45812f027f 100644 --- a/apps/main/src/dex/features/deposit-gauge-reward/ui/DepositReward.tsx +++ b/apps/main/src/dex/features/deposit-gauge-reward/ui/DepositReward.tsx @@ -1,4 +1,6 @@ +import { useEffect } from 'react' import { FormProvider, useForm } from 'react-hook-form' +import { useConnection } from 'wagmi' import AlertFormError from '@/dex/components/AlertFormError' import { useGaugeRewardsDistributors } from '@/dex/entities/gauge' import { DepositRewardDefaultValues, depositRewardValidationSuite } from '@/dex/features/deposit-gauge-reward/model' @@ -16,6 +18,7 @@ import { FormErrorsDisplay } from '@ui/FormErrorsDisplay' import { BlockSkeleton } from '@ui/skeleton' import { FormContainer, FormFieldsContainer, GroupedFieldsContainer } from '@ui/styled-containers' import { formDefaultOptions } from '@ui-kit/lib/model/form' +import { useTokenBalance } from '@ui-kit/queries/token-balance.query' export const DepositReward = ({ chainId, poolId }: { chainId: ChainId; poolId: string }) => { const { isPending: isPendingRewardDistributors } = useGaugeRewardsDistributors({ @@ -29,6 +32,15 @@ export const DepositReward = ({ chainId, poolId }: { chainId: ChainId; poolId: s defaultValues: DepositRewardDefaultValues, }) + const rewardTokenId = methods.watch('rewardTokenId') + const { address: userAddress } = useConnection() + const { data: userBalance } = useTokenBalance({ chainId, userAddress, tokenAddress: rewardTokenId }) + + // Sync userBalance from query into form for validation + useEffect(() => { + methods.setValue('userBalance', userBalance, { shouldValidate: true }) + }, [userBalance, methods]) + if (isPendingRewardDistributors) { return } diff --git a/apps/main/src/dex/features/deposit-gauge-reward/ui/GasEstimation.tsx b/apps/main/src/dex/features/deposit-gauge-reward/ui/GasEstimation.tsx index 3664abc520..0eed8de11b 100644 --- a/apps/main/src/dex/features/deposit-gauge-reward/ui/GasEstimation.tsx +++ b/apps/main/src/dex/features/deposit-gauge-reward/ui/GasEstimation.tsx @@ -14,13 +14,14 @@ export const GasEstimation = ({ chainId, poolId }: { chainId: ChainId; poolId: s const amount = watch('amount') const epoch = watch('epoch') const step = watch('step') + const userBalance = watch('userBalance') const { data: estimatedGasDepositRewardApprove, isLoading: isLoadingGasEstimateDepositRewardApprove, isFetching: isFetchingGasEstimateDepositRewardApprove, } = useEstimateGasDepositRewardApprove( - { chainId, poolId, rewardTokenId, amount }, + { chainId, poolId, rewardTokenId, amount, userBalance }, step === DepositRewardStep.APPROVAL && isValid, ) @@ -35,6 +36,7 @@ export const GasEstimation = ({ chainId, poolId }: { chainId: ChainId; poolId: s rewardTokenId, amount, epoch, + userBalance, }, step === DepositRewardStep.DEPOSIT && isValid, ) diff --git a/apps/main/src/dex/widgets/manage-gauge/ui/ManageGauge.tsx b/apps/main/src/dex/widgets/manage-gauge/ui/ManageGauge.tsx index c16dc30fae..65158715c6 100644 --- a/apps/main/src/dex/widgets/manage-gauge/ui/ManageGauge.tsx +++ b/apps/main/src/dex/widgets/manage-gauge/ui/ManageGauge.tsx @@ -1,7 +1,7 @@ import { useMemo, useState } from 'react' import { isAddressEqual, type Address } from 'viem' +import { useConnection } from 'wagmi' import { useGaugeManager, useGaugeRewardsDistributors } from '@/dex/entities/gauge' -import { useSignerAddress } from '@/dex/entities/signer' import AddRewardToken from '@/dex/features/add-gauge-reward-token' import DepositReward from '@/dex/features/deposit-gauge-reward' import { ChainId } from '@/dex/types/main.types' @@ -11,7 +11,7 @@ import { t } from '@ui-kit/lib/i18n' import { TabsSwitcher, type TabOption } from '@ui-kit/shared/ui/TabsSwitcher' const ManageGauge = ({ poolId, chainId }: { poolId: string; chainId: ChainId }) => { - const { data: signerAddress } = useSignerAddress() + const { address: signerAddress } = useConnection() const { data: gaugeManager } = useGaugeManager({ chainId, poolId }) const { data: rewardDistributors } = useGaugeRewardsDistributors({ chainId, poolId })