diff --git a/src/components/contextual/pages/pools/StakedPoolsTable.vue b/src/components/contextual/pages/pools/StakedPoolsTable.vue index 2f0467dbf2..fbac6bdde0 100644 --- a/src/components/contextual/pages/pools/StakedPoolsTable.vue +++ b/src/components/contextual/pages/pools/StakedPoolsTable.vue @@ -18,6 +18,10 @@ import PortfolioSyncTip from '../vebal/cross-chain-boost/PortfolioSyncTip.vue'; */ const showUnstakeModal = ref(false); const poolToUnstake = ref(); + +const showRestakeModal = ref(false); +const poolToRestake = ref(); + const showProceedModal = ref(false); /** @@ -28,8 +32,13 @@ providePoolStaking(); /** * COMPOSABLES */ -const { stakedPools, poolBoostsMap, stakedShares, isLoading } = - useUserStaking(); +const { + stakedPools, + poolBoostsMap, + stakedShares, + isLoading, + hasNonPrefGaugesPoolsIds, +} = useUserStaking(); const { refetchAllUserPools } = useUserPools(); const { isWalletReady, isWalletConnecting } = useWeb3(); @@ -62,9 +71,15 @@ function handleUnstake(pool: Pool) { poolToUnstake.value = pool; } +function handleRestake(pool: Pool) { + showRestakeModal.value = true; + poolToRestake.value = pool; +} + function handleModalClose() { refetchAllUserPools(); showUnstakeModal.value = false; + showRestakeModal.value = false; } async function handleUnstakeSuccess() { @@ -77,6 +92,7 @@ async function handleUnstakeSuccess() {
{{ $t('staking.stakedPools') }} + {{ hasNonPrefGaugesPoolsIds }}
+ + + + + + (), { const emit = defineEmits<{ (e: 'click:stake', value: Pool): void; (e: 'click:unstake', value: Pool): void; + (e: 'click:restake', value: Pool): void; + (e: 'click:update', value: Pool): void; (e: 'click:migrate', value: Pool): void; }>(); /** @@ -58,6 +60,24 @@ const showVeBalLock = computed(() => isVeBalPool(props.pool.id)); > {{ $t('stake') }} + + {{ $t('restake') }} + + + {{ $t('Update') }} + ; + +/** + * Fetches all gauges for a given pool and specifies which gauge is the + * preferential gauge. + */ +export default function usePoolsGaugesQuery( + poolAddresses: Ref, + options: UseQueryOptions = {} +) { + /** + * QUERY KEY + */ + const queryKey = reactive(QUERY_KEYS.Pools.Gauges(poolAddresses)); + + /** + * COMPUTED + */ + const enabled = computed((): boolean => !!poolAddresses?.value?.length); + + const subgraphQuery = computed(() => ({ + __name: 'PoolGauges', + pools: { + __args: { + where: { + address_in: poolAddresses.value?.map(address => + address.toLowerCase() + ), + }, + }, + preferentialGauge: { + id: true, + }, + gauges: { + id: true, + relativeWeightCap: true, + }, + address: true, + }, + })); + + /** + * QUERY FUNCTION + */ + const queryFn = async () => { + try { + return await subgraphRequest({ + url: configService.network.subgraphs.gauge, + query: subgraphQuery.value, + }); + } catch (error) { + console.error( + `Failed to fetch pool gauge for pools: ${poolAddresses.value}`, + { + cause: error, + } + ); + throw error; + } + }; + + /** + * QUERY OPTIONS + */ + const queryOptions = reactive({ + enabled, + refetchOnWindowFocus: false, + ...options, + }); + + return useQuery(queryKey, queryFn, queryOptions as QueryOptions); +} diff --git a/src/composables/queries/useUserGaugeSharesQuery.ts b/src/composables/queries/useUserGaugeSharesQuery.ts index fe2ce2c7b9..03a659c02e 100644 --- a/src/composables/queries/useUserGaugeSharesQuery.ts +++ b/src/composables/queries/useUserGaugeSharesQuery.ts @@ -13,6 +13,7 @@ export type GaugeShare = { balance: string; gauge: { id: string; + poolAddress: string; poolId: string; totalSupply: string; }; @@ -76,6 +77,7 @@ export default function useUserGaugeSharesQuery( balance: true, gauge: { id: true, + poolAddress: true, poolId: true, totalSupply: true, }, diff --git a/src/constants/queryKeys.ts b/src/constants/queryKeys.ts index 90fe81664e..49170725f2 100644 --- a/src/constants/queryKeys.ts +++ b/src/constants/queryKeys.ts @@ -124,6 +124,11 @@ const QUERY_KEYS = { }, ], }, + Gauges: (poolAddresses: Ref) => [ + 'pools', + 'gauges', + { poolAddresses }, + ], }, Pool: { Gauges: (poolAddress: Ref) => [ diff --git a/src/providers/local/user-staking.provider.ts b/src/providers/local/user-staking.provider.ts index b2b778d9cd..a276981cbb 100644 --- a/src/providers/local/user-staking.provider.ts +++ b/src/providers/local/user-staking.provider.ts @@ -9,6 +9,8 @@ import { Pool } from '@/services/pool/types'; import { computed, InjectionKey, provide, reactive, ref } from 'vue'; import { safeInject } from '../inject'; import { useUserData } from '../user-data.provider'; +import usePoolsGaugesQuery from '@/composables/queries/usePoolsGaugesQuery'; +import { bnum, isSameAddress } from '@/lib/utils'; const provider = () => { /** @@ -27,10 +29,19 @@ const provider = () => { // Array of all the pools a user has staked BPT for. const stakedPoolIds = computed((): string[] => { if (!userGaugeShares.value) return []; - + console.log('userGaugeShares.value', userGaugeShares.value); return userGaugeShares.value.map(gaugeShare => gaugeShare.gauge.poolId); }); + // Array of all the pools addresses a user has staked BPT for. + const stakedPoolAddresses = computed((): string[] => { + if (!userGaugeShares.value) return []; + + return userGaugeShares.value.map( + gaugeShare => gaugeShare.gauge.poolAddress + ); + }); + const isPoolsQueryEnabled = computed( (): boolean => stakedPoolIds.value.length > 0 ); @@ -45,6 +56,37 @@ const provider = () => { pageSize: 999, } ); + + const { data: poolsGauges } = usePoolsGaugesQuery( + computed(() => stakedPoolAddresses.value) + ); + + // Map of user gauge addresses -> balance. + const userGaugeSharesMap = computed((): Record => { + if (!userGaugeShares.value) return {}; + + return userGaugeShares.value.reduce((acc, share) => { + acc[share.gauge.id] = share.balance; + return acc; + }, {} as Record); + }); + + const hasNonPrefGaugesPoolsIds = computed(() => { + return poolsGauges.value?.pools.reduce((acc: string[], pool) => { + const preferentialGaugeAddress = pool.preferentialGauge.id || ''; + const hasNonPregGauge = pool.gauges.some( + gaugeAddress => + !isSameAddress(gaugeAddress.id, preferentialGaugeAddress) && + bnum(userGaugeSharesMap.value[gaugeAddress.id] || '0').gt(0) + ); + + if (hasNonPregGauge) { + acc.push(pool.address); + } + return acc; + }, []); + }); + const { data: _stakedPools, refetch: refetchStakedPools } = stakedPoolsQuery; // Pool records for all the pools where a user has staked BPT. @@ -92,6 +134,8 @@ const provider = () => { isLoading, refetchStakedPools, stakedSharesFor, + hasNonPrefGaugesPoolsIds, + poolsGauges, }; };