Skip to content

Conversation

@0xAlunara
Copy link
Collaborator

@0xAlunara 0xAlunara commented Dec 12, 2025

Depends on #1757

The Zustand rot is real; like 80% of this PR is just me passing a wagmi config to all the imperative Zustand functions, just to be able to use the imperative versions of the new wagmi native token balance hooks.

Please don't judge too hardly about that plumbing, the main focus should be on:

  1. token-balance.query.ts
  2. usePoolTokenDepositBalances.ts
  3. usePoolTokenBalances.ts

The harsh reality here is that there's no automated tests for now, so all we can do is test manually 😭

@vercel
Copy link

vercel bot commented Dec 12, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
curve-dapp Ready Ready Preview Comment Dec 12, 2025 7:02pm
curve-dapp-storybook Ready Ready Preview Comment Dec 12, 2025 7:02pm

}

/** Hook to fetch balances for multiple tokens */
export function useTokenBalances(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I let Claude vibe code this by looking at useTokenUsdRates from token-usd-rates.ts 🙈

export function useTokenBalance({ chainId, userAddress, tokenAddress }: FieldsOf<TokenBalanceQuery>) {
export function useTokenBalance(
{ chainId, userAddress, tokenAddress }: FieldsOf<TokenBalanceQuery>,
enabled: boolean = true,
Copy link
Collaborator Author

@0xAlunara 0xAlunara Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we normally use validation suites rather than enabled, but not using queryFactory (because of wagmi) makes it a big more complex and I wanted to know if my refactor was working at all. So I opted for this (anti?)-pattern that we've used for some other hooks as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disabling a query is fine, but IMO we can also add a validation suite here such that enabled=enabled&&valid

get()[sliceKey].setStateByActiveKey('userShare', userPoolActiveKey, userShare)
get()[sliceKey].setStateByActiveKey('userLiquidityUsd', userPoolActiveKey, liquidityUsd)

return fetchedWalletBalances
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return value was never used

enabled = true,
) {
const { curveApi, isHydrated } = useCurve()
const pool = chainId && userAddress && poolId && curveApi && isHydrated ? curveApi.getPool(poolId) : undefined
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hydration is required, because if it isn't getPool will throw an error it can't find the poolId

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a lot of checks that aren't really related to the pool, they should be handled by the query's validation not manually here imo

  • chainId: is not used here
  • userAddress: has nothing to do with pool
  • poolId: can it really be undefined after hydration?
  • curveApi: cannot be undefined when isHydrated
Suggested change
const pool = chainId && userAddress && poolId && curveApi && isHydrated ? curveApi.getPool(poolId) : undefined
const pool = useMemo(() => isHydrated && curveApi.getPool(poolId), [curveApi, isHydrated, poolId])

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [chainId, poolId, signerAddress, formValues, formStatus, slippage.isHighSlippage, slippageConfirmed, maxSlippage])
}, [
config,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of times exhaustive deps was turned off. I didn't dare to remove it, afraid of breaking anything. I just added config and called it a day.

balance={balLpToken}
balanceLoading={balancesLoading}
hasError={haveSigner ? new BigNumber(formValues.lpToken).isGreaterThan(balLpToken as string) : false}
balance={lpTokenBalance ?? '0'}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it was there already, but omg it looks so bad when your balance is 0 during loading.

const userToBalance = userPoolBalances?.[formValues.toAddress]
const config = useConfig()
const { address: userAddress } = useConnection()
const { data: userFromBalance, isLoading: userFromBalanceLoading } = useTokenBalance({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of a version that allows an array or tuple of addresses via useQueries?

balance={haveSigner ? balGauge : ''}
hasError={+formValues.stakedLpToken > +balGauge}
balanceLoading={gaugeTokenLoading}
balance={gaugeTokenBalance ?? '0'}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the existing code did not have ?? 0 but as string

!seed.loaded ||
walletBalancesLoading

const hasWalletBalances = Object.keys(wrappedCoinsBalances).length && Object.keys(underlyingCoinsBalances).length
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use usePoolTokenBalances(..).error for this, I don't see an use case for generic error

const { curveApi, isHydrated } = useCurve()
const pool = chainId && userAddress && poolId && curveApi && isHydrated ? curveApi.getPool(poolId) : undefined

const { data: wrappedCoinsBalances, isLoading: wrappedCoinsLoading } = useTokenBalances(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be extracting the error here, too

Comment on lines +53 to +61
export function fetchPoolLpTokenBalance(config: Config, curve: CurveApi, poolId: string) {
const pool = requireLib('curveApi').getPool(poolId)

return fetchTokenBalance(config, {
chainId: curve?.chainId,
userAddress: curve.signerAddress as Address,
tokenAddress: pool.lpToken as Address,
})
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why call requireLib if you are passing curve here? 😅

Suggested change
export function fetchPoolLpTokenBalance(config: Config, curve: CurveApi, poolId: string) {
const pool = requireLib('curveApi').getPool(poolId)
return fetchTokenBalance(config, {
chainId: curve?.chainId,
userAddress: curve.signerAddress as Address,
tokenAddress: pool.lpToken as Address,
})
}
export const fetchPoolLpTokenBalance = async (config: Config, curve: CurveApi, poolId: string) =>
await fetchTokenBalance(config, {
chainId: curve.chainId,
userAddress: curve.signerAddress as Address,
tokenAddress: curve.getPool(poolId).lpToken as Address,
})

export function useTokenBalance({ chainId, userAddress, tokenAddress }: FieldsOf<TokenBalanceQuery>) {
export function useTokenBalance(
{ chainId, userAddress, tokenAddress }: FieldsOf<TokenBalanceQuery>,
enabled: boolean = true,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disabling a query is fine, but IMO we can also add a validation suite here such that enabled=enabled&&valid


/** Get query options for a token balance (handles both native and ERC-20) */
const getTokenBalanceQueryOptions = (config: Config, query: TokenBalanceQuery) => {
if (isNative(query)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ternary + inline arrow would be nice

const config = useConfig()

const isEnabled = enabled && chainId != null && userAddress != null
const uniqueAddresses = useMemo(() => Array.from(new Set(tokenAddresses)), [tokenAddresses])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't wagmi unify them already?

) {
const config = useConfig()

const isEnabled = enabled && chainId != null && userAddress != null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use the validation group here, too

Base automatically changed from refactor/user-balances-tanstack-query to main December 17, 2025 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants