diff --git a/index.html b/index.html index 77cc83da9..d1f3ea582 100644 --- a/index.html +++ b/index.html @@ -26,10 +26,11 @@ - + -
+
+
\ No newline at end of file diff --git a/index.template.html b/index.template.html index 028738aa7..c745ba505 100644 --- a/index.template.html +++ b/index.template.html @@ -26,10 +26,11 @@ - + -
+
+
\ No newline at end of file diff --git a/src/api/omnipool.ts b/src/api/omnipool.ts index 7303ebd98..c3563b8b6 100644 --- a/src/api/omnipool.ts +++ b/src/api/omnipool.ts @@ -1,19 +1,17 @@ -import { useQuery, useQueryClient } from "@tanstack/react-query" +import { useQuery } from "@tanstack/react-query" import { ApiPromise } from "@polkadot/api" -import { QUERY_KEYS, WS_QUERY_KEYS } from "utils/queryKeys" +import { QUERY_KEYS } from "utils/queryKeys" import { REFETCH_INTERVAL } from "utils/constants" import { useRpcProvider } from "providers/rpcProvider" -import { OMNIPOOL_ACCOUNT_ADDRESS } from "utils/api" -import { useAssets } from "providers/assets" -import { useEffect } from "react" -import { VoidFn } from "@polkadot/api/types" -import { arraysEqual } from "utils/helpers" + import { is_add_liquidity_allowed, is_buy_allowed, is_remove_liquidity_allowed, is_sell_allowed, } from "@galacticcouncil/math-omnipool" +import { PoolToken } from "@galacticcouncil/sdk" +import { useMemo } from "react" export type TOmnipoolAssetsData = Array<{ id: string @@ -26,102 +24,36 @@ export type TOmnipoolAssetsData = Array<{ }> export const useOmnipoolDataObserver = () => { - const { data, isLoading } = useQuery( - WS_QUERY_KEYS.omnipoolAssets, + const { data: omnipoolTokens, isLoading: isOmnipoolTokensLoading } = + useQuery(QUERY_KEYS.omnipoolTokens, { + enabled: false, + staleTime: Infinity, + }) + + const { data: hubToken, isLoading: isHubTokenLoading } = useQuery( + QUERY_KEYS.hubToken, { enabled: false, staleTime: Infinity, }, ) + const dataMap = useMemo( + () => + omnipoolTokens + ? new Map(omnipoolTokens.map((asset) => [asset.id, asset])) + : undefined, + [omnipoolTokens], + ) + return { - data, - dataMap: data ? new Map(data.map((asset) => [asset.id, asset])) : undefined, - isLoading, + data: omnipoolTokens, + hubToken, + dataMap, + isLoading: isHubTokenLoading || isOmnipoolTokensLoading, } } -export const useOmnipoolAssetsSubsciption = () => { - const { - native: { id: nativeId }, - } = useAssets() - const { api, isLoaded } = useRpcProvider() - const queryClient = useQueryClient() - - useEffect(() => { - if (!isLoaded) return undefined - - let unsubAssets: VoidFn | undefined - - const loadAssets = async () => { - const keys = (await api.query.omnipool.assets.keys()).map((key) => - key.args[0].toString(), - ) - const balanceKeys = keys.map((key) => [OMNIPOOL_ACCOUNT_ADDRESS, key]) - - unsubAssets = await api.query.omnipool.assets.multi( - keys, - async (assets) => { - const [nativeBalance, balancesRaw] = await Promise.all([ - api.query.system.account(OMNIPOOL_ACCOUNT_ADDRESS), - api.query.tokens.accounts.multi(balanceKeys), - ]) - - const balances = new Map( - balancesRaw.map((d, i) => { - const id = keys[i] - const balance = d.free - .toBigNumber() - .minus(d.frozen.toBigNumber()) - .toString() - - return [id, balance] - }), - ) - - const data: TOmnipoolAssetsData = assets.map((dataRaw, i) => { - const asset = dataRaw.unwrap() - const id = keys[i] - const balance = - id === nativeId - ? nativeBalance.data.free - .toBigNumber() - .minus(nativeBalance.data.frozen.toString()) - .toString() - : (balances.get(id) as string) - - return { - id, - hubReserve: asset.hubReserve.toString(), - shares: asset.shares.toString(), - protocolShares: asset.protocolShares.toString(), - cap: asset.cap.toString(), - bits: asset.tradable.bits.toNumber(), - balance, - } - }) - - const prevData: TOmnipoolAssetsData = - queryClient.getQueryData(WS_QUERY_KEYS.omnipoolAssets) ?? [] - - if (!arraysEqual(prevData, data)) { - queryClient.setQueryData(WS_QUERY_KEYS.omnipoolAssets, data) - } - }, - ) - } - - loadAssets() - - return () => { - if (unsubAssets) { - unsubAssets() - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [queryClient, isLoaded]) -} - export const useOmnipoolFee = () => { const { api } = useRpcProvider() return useQuery(QUERY_KEYS.omnipoolFee, getOmnipoolFee(api)) diff --git a/src/api/pools.ts b/src/api/pools.ts index 6411f357a..695caecc5 100644 --- a/src/api/pools.ts +++ b/src/api/pools.ts @@ -1,12 +1,17 @@ import { useMemo } from "react" import { useTotalIssuances } from "./totalIssuance" import { useRpcProvider } from "providers/rpcProvider" -import { useQuery } from "@tanstack/react-query" +import { useQuery, useQueryClient } from "@tanstack/react-query" import { QUERY_KEYS } from "utils/queryKeys" import { ApiPromise } from "@polkadot/api" import type { u32 } from "@polkadot/types" import { useAccountAssets } from "./deposits" import BN from "bignumber.js" +import { PoolToken, PoolType } from "@galacticcouncil/sdk" +import { OmniPoolToken } from "@galacticcouncil/sdk/build/types/pool/omni/OmniPool" +import { millisecondsInMinute } from "date-fns" +import { TOmnipoolAssetsData } from "./omnipool" +import { HUB_ID } from "utils/api" export const useShareOfPools = (assets: string[]) => { const totalIssuances = useTotalIssuances() @@ -45,13 +50,70 @@ export const useShareOfPools = (assets: string[]) => { export const useSDKPools = () => { const { isLoaded, tradeRouter } = useRpcProvider() + const queryClient = useQueryClient() return useQuery({ queryKey: QUERY_KEYS.pools, queryFn: async () => { - return await tradeRouter.getPools() + const pools = await tradeRouter.getPools() + + const omnipoolTokens = ( + pools.find((pool) => pool.type === PoolType.Omni) + ?.tokens as OmniPoolToken[] + ).map((token) => { + return { + ...token, + shares: token.shares?.toString(), + protocolShares: token.protocolShares?.toString(), + cap: token.cap?.toString(), + hubReserves: token.hubReserves?.toString(), + } + }) + + const { tokens, hub } = omnipoolTokens.reduce<{ + tokens: TOmnipoolAssetsData + hub: PoolToken + }>( + (acc, token) => { + if (token.id === HUB_ID) { + acc.hub = token + } else { + const { + id, + hubReserves, + cap, + protocolShares, + shares, + tradeable, + balance, + } = token + + acc.tokens.push({ + id, + hubReserve: hubReserves, + cap, + protocolShares, + shares, + bits: tradeable, + balance, + } as TOmnipoolAssetsData[number]) + } + + return acc + }, + { tokens: [], hub: {} as PoolToken }, + ) + + const xykPools = pools.filter((pool) => pool.type === PoolType.XYK) + + queryClient.setQueryData(QUERY_KEYS.omnipoolTokens, tokens) + queryClient.setQueryData(QUERY_KEYS.hubToken, hub) + queryClient.setQueryData(QUERY_KEYS.xykPools, xykPools) + + return false }, enabled: isLoaded, + staleTime: millisecondsInMinute, }) } diff --git a/src/api/provider.ts b/src/api/provider.ts index 1ae245a20..1af5110bf 100644 --- a/src/api/provider.ts +++ b/src/api/provider.ts @@ -5,7 +5,7 @@ import { persist } from "zustand/middleware" import { SubstrateApis } from "@galacticcouncil/xcm-core" import { useMemo } from "react" import { useShallow } from "hooks/useShallow" -import { pick } from "utils/rx" +import { pick, uniqBy } from "utils/rx" import { ApiPromise, WsProvider } from "@polkadot/api" import { useRpcProvider } from "providers/rpcProvider" import { @@ -17,7 +17,7 @@ import { } from "@galacticcouncil/sdk" import { useUserExternalTokenStore } from "sections/wallet/addToken/AddToken.utils" import { useAssetRegistry, useSettingsStore } from "state/store" -import { undefinedNoop } from "utils/helpers" +import { identity, undefinedNoop } from "utils/helpers" import { ExternalAssetCursor } from "@galacticcouncil/apps" import { getExternalId } from "utils/externalAssets" import { pingRpc } from "utils/rpc" @@ -33,9 +33,8 @@ export type ProviderProps = { } export type TFeatureFlags = { - referrals: boolean dispatchPermit: boolean -} +} & { [key: string]: boolean } export const PASEO_WS_URL = "paseo-rpc.play.hydration.cloud" @@ -107,13 +106,26 @@ export const PROVIDER_URLS = PROVIDER_LIST.map(({ url }) => url) export const isTestnetRpcUrl = (url: string) => PROVIDERS.find((provider) => provider.url === url)?.dataEnv === "testnet" +export async function pingAllProvidersAndSort() { + const fastestRpc = await Promise.race( + PROVIDER_URLS.map(async (url) => { + const time = await pingRpc(url) + return { url, time } + }), + ) + + const sortedRpcList = uniqBy(identity, [fastestRpc.url, ...PROVIDER_URLS]) + useProviderRpcUrlStore.getState().setRpcUrlList(sortedRpcList, Date.now()) +} + export const useProviderRpcUrlStore = create( persist<{ rpcUrl: string rpcUrlList: string[] autoMode: boolean + updatedAt: number setRpcUrl: (rpcUrl: string | undefined) => void - setRpcUrlList: (rpcUrlList: string[]) => void + setRpcUrlList: (rpcUrlList: string[], updatedAt: number) => void getDataEnv: () => TEnv setAutoMode: (state: boolean) => void _hasHydrated: boolean @@ -122,9 +134,10 @@ export const useProviderRpcUrlStore = create( (set, get) => ({ rpcUrl: import.meta.env.VITE_PROVIDER_URL, rpcUrlList: [], + updatedAt: 0, autoMode: true, setRpcUrl: (rpcUrl) => set({ rpcUrl }), - setRpcUrlList: (rpcUrlList) => set({ rpcUrlList }), + setRpcUrlList: (rpcUrlList, updatedAt) => set({ rpcUrlList, updatedAt }), setAutoMode: (state) => set({ autoMode: state }), getDataEnv: () => { const { rpcUrl } = get() @@ -138,7 +151,7 @@ export const useProviderRpcUrlStore = create( }), { name: "rpcUrl", - version: 3, + version: 3.1, onRehydrateStorage: () => (state) => { state?._setHasHydrated(true) }, @@ -254,8 +267,7 @@ export const useProviderData = () => { await poolService.syncRegistry(externalTokens[dataEnv]) - const [isReferralsEnabled, isDispatchPermitEnabled] = await Promise.all([ - api.query.referrals, + const [isDispatchPermitEnabled] = await Promise.all([ api.tx.multiTransactionPayment.dispatchPermit, tradeRouter.getPools(), ]) @@ -268,12 +280,12 @@ export const useProviderData = () => { poolService, balanceClient, featureFlags: { - referrals: !!isReferralsEnabled, dispatchPermit: !!isDispatchPermitEnabled, } as TFeatureFlags, } }, { + enabled: rpcUrlList.length > 0, refetchOnWindowFocus: false, retry: false, onSettled: (data) => { diff --git a/src/api/subscriptions.tsx b/src/api/subscriptions.tsx index 573384a6e..041e921b2 100644 --- a/src/api/subscriptions.tsx +++ b/src/api/subscriptions.tsx @@ -1,11 +1,11 @@ -import { useOmnipoolAssetsSubsciption } from "./omnipool" +import { useSDKPools } from "./pools" export const QuerySubscriptions = () => { return } const OmnipoolAssetsSubscription = () => { - useOmnipoolAssetsSubsciption() + useSDKPools() return null } diff --git a/src/api/xyk.ts b/src/api/xyk.ts index 803fda160..28faac1f2 100644 --- a/src/api/xyk.ts +++ b/src/api/xyk.ts @@ -5,8 +5,10 @@ import { QUERY_KEYS } from "utils/queryKeys" import { isNotNil, undefinedNoop } from "utils/helpers" import { useAssetRegistry } from "state/store" import { useActiveRpcUrlList, useProviderData } from "./provider" +import { PoolBase } from "@galacticcouncil/sdk" +import { millisecondsInMinute } from "date-fns" -const getXYKPools = (api: ApiPromise) => async () => { +const getAllXYKPools = (api: ApiPromise) => async () => { const res = await api.query.xyk.poolAssets.entries() const data = res.map(([key, data]) => { @@ -18,10 +20,20 @@ const getXYKPools = (api: ApiPromise) => async () => { return data } -export const useGetXYKPools = () => { - const { api } = useRpcProvider() +export const useAllXykPools = () => { + const { api, isLoaded } = useRpcProvider() - return useQuery(QUERY_KEYS.xykPools, getXYKPools(api)) + return useQuery(QUERY_KEYS.allXykPools, getAllXYKPools(api), { + enabled: isLoaded, + staleTime: millisecondsInMinute, + }) +} + +export const useXYKPools = () => { + return useQuery(QUERY_KEYS.xykPools, { + enabled: false, + staleTime: Infinity, + }) } export const useShareTokens = () => { diff --git a/src/components/AppLoader/AppLoader.styled.ts b/src/components/AppLoader/AppLoader.styled.ts deleted file mode 100644 index a6d47e4d7..000000000 --- a/src/components/AppLoader/AppLoader.styled.ts +++ /dev/null @@ -1,21 +0,0 @@ -import styled from "@emotion/styled" - -export const SContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - height: 100vh; - width: 100vw; - - position: fixed; - inset: 0; - - z-index: 1000; - - & > svg { - width: 240px; - height: auto; - } -` diff --git a/src/components/AppLoader/AppLoader.tsx b/src/components/AppLoader/AppLoader.tsx index 7aa64654b..c2c0973f9 100644 --- a/src/components/AppLoader/AppLoader.tsx +++ b/src/components/AppLoader/AppLoader.tsx @@ -1,12 +1,19 @@ -import HydraLogoFull from "assets/icons/HydraLogoFull.svg?react" -import { Spinner } from "components/Spinner/Spinner" -import { SContainer } from "./AppLoader.styled" +import { useEffect } from "react" +/** + * Used as fallback in Suspense. + * + * Removes initial static loader in index.html. + */ export const AppLoader = () => { - return ( - - - - - ) + useEffect(() => { + return () => { + const loader = window.document.querySelector(".loader-container") + if (loader) { + loader.remove() + } + } + }, []) + + return null } diff --git a/src/components/AppProviders/AppProviders.tsx b/src/components/AppProviders/AppProviders.tsx index 651280bb0..918441329 100644 --- a/src/components/AppProviders/AppProviders.tsx +++ b/src/components/AppProviders/AppProviders.tsx @@ -10,7 +10,6 @@ import * as Apps from "@galacticcouncil/apps" import { createComponent } from "@lit-labs/react" import { ProviderReloader } from "sections/provider/ProviderReloader" import { MigrationProvider } from "sections/migration/MigrationProvider" -import { ProviderBalancer } from "sections/provider/ProviderBalancer" const AppsContextProvider = createComponent({ tagName: "gc-context-provider", @@ -22,23 +21,21 @@ export const AppProviders: FC = ({ children }) => { return ( - - - - - - - {children} - - - - - - + + + + + + {children} + + + + + ) diff --git a/src/components/Layout/Header/banners/HeaderBanners.tsx b/src/components/Layout/Header/banners/HeaderBanners.tsx index b33b7b227..2363f6c51 100644 --- a/src/components/Layout/Header/banners/HeaderBanners.tsx +++ b/src/components/Layout/Header/banners/HeaderBanners.tsx @@ -2,37 +2,15 @@ import { WarningMessage } from "components/WarningMessage/WarningMessage" import { useWarningsStore } from "components/WarningMessage/WarningMessage.utils" import { useRpcProvider } from "providers/rpcProvider" import { useTranslation } from "react-i18next" -import { useLocation } from "react-use" -import { - MIGRATION_TARGET_DOMAIN, - MIGRATION_TRIGGER_DOMAIN, - useMigrationStore, -} from "sections/migration/MigrationProvider.utils" -import { MigrationWarning } from "sections/migration/components/MigrationWarning" import { NewFarmsBanner } from "sections/pools/components/NewFarmsBanner" export const HeaderBanners = () => { const { t } = useTranslation() const { isLoaded } = useRpcProvider() const warnings = useWarningsStore() - const { migrationCompleted, setMigrationCompleted } = useMigrationStore() - const location = useLocation() - - const shouldShowMigrationWarning = - isLoaded && MIGRATION_TARGET_DOMAIN === location.host && !migrationCompleted return ( <> - {shouldShowMigrationWarning && ( - - (window.location.href = `https://${MIGRATION_TRIGGER_DOMAIN}?from=${MIGRATION_TARGET_DOMAIN}`) - } - onClose={() => { - setMigrationCompleted(new Date().toISOString()) - }} - /> - )} {warnings.warnings.hdxLiquidity.visible && ( ({ - default: (await import("sections/referrals/ReferralsConnectWrapper")) - .ReferralsConnectWrapper, +const ReferralsConnect = lazy(async () => ({ + default: (await import("sections/referrals/ReferralsConnect")) + .ReferralsConnect, })) const Transactions = lazy(async () => ({ @@ -103,7 +103,6 @@ export const Page = ({ className }: Props) => { const matchRoute = useMatchRoute() const ref = useControlScroll() const subHeaderComponent = useSubheaderComponent() - const flippedBg = !!matchRoute({ to: LINKS.memepad }) return ( @@ -122,7 +121,7 @@ export const Page = ({ className }: Props) => { - + diff --git a/src/i18n/locales/en/translations.json b/src/i18n/locales/en/translations.json index 97cf7987e..01dc7cb98 100644 --- a/src/i18n/locales/en/translations.json +++ b/src/i18n/locales/en/translations.json @@ -22,6 +22,7 @@ "hours": "Hours", "minutes": "Minutes", "seconds": "Seconds", + "connecting": "Connecting", "duration.left": "{{duration}} left", "duration.ago": "{{duration}} ago", "toast.counter": "{{ index }} of {{ count }}", diff --git a/src/providers/rpcProvider.tsx b/src/providers/rpcProvider.tsx index 1b044c557..049e9f5f1 100644 --- a/src/providers/rpcProvider.tsx +++ b/src/providers/rpcProvider.tsx @@ -4,13 +4,20 @@ import { type BalanceClient, } from "@galacticcouncil/sdk" import { ApiPromise } from "@polkadot/api" -import { TFeatureFlags, useProviderAssets, useProviderData } from "api/provider" -import { ReactNode, createContext, useContext, useMemo } from "react" +import { + pingAllProvidersAndSort, + TFeatureFlags, + useProviderAssets, + useProviderData, + useProviderRpcUrlStore, +} from "api/provider" +import { ReactNode, createContext, useContext, useEffect, useMemo } from "react" import { useWindowFocus } from "hooks/useWindowFocus" import { useAssetRegistry } from "state/store" import { useDisplayAssetStore } from "utils/displayAsset" import { useShareTokens } from "api/xyk" import { AssetsProvider } from "./assets" +import { differenceInHours } from "date-fns" type TProviderContext = { api: ApiPromise @@ -31,6 +38,8 @@ const ProviderContext = createContext({ export const useRpcProvider = () => useContext(ProviderContext) +const RPC_PING_HOUR_INTERVAL = 4 + export const RpcProvider = ({ children }: { children: ReactNode }) => { const { assets } = useAssetRegistry.getState() const isAssets = !!assets.length @@ -39,6 +48,19 @@ export const RpcProvider = ({ children }: { children: ReactNode }) => { useProviderAssets() useShareTokens() + useEffect(() => { + const { rpcUrlList, updatedAt } = useProviderRpcUrlStore.getState() + + const hourDiff = differenceInHours(new Date(), updatedAt) + + const shouldPing = + hourDiff >= RPC_PING_HOUR_INTERVAL || rpcUrlList.length === 0 + + if (shouldPing) { + pingAllProvidersAndSort() + } + }, []) + useWindowFocus({ onFocus: () => { const provider = providerData?.api @@ -94,7 +116,6 @@ export const RpcProvider = ({ children }: { children: ReactNode }) => { tradeRouter: {} as TradeRouter, balanceClient: {} as BalanceClient, featureFlags: { - referrals: true, dispatchPermit: true, } as TProviderContext["featureFlags"], poolService: {} as TProviderContext["poolService"], diff --git a/src/sections/lending/ui/reserve-overview/chart/InterestRateModelChart.tsx b/src/sections/lending/ui/reserve-overview/chart/InterestRateModelChart.tsx index 8b6fcdde4..be6d05def 100644 --- a/src/sections/lending/ui/reserve-overview/chart/InterestRateModelChart.tsx +++ b/src/sections/lending/ui/reserve-overview/chart/InterestRateModelChart.tsx @@ -156,6 +156,8 @@ export const InterestRateModelChart = ({ 50 ? 85 : -3} + yOffset={5} title="Optimal" color={theme.colors[tickField.lineColor ?? "brightBlue300"]} /> @@ -166,13 +168,13 @@ export const InterestRateModelChart = ({ stroke={theme.colors[tickField.lineColor ?? "brightBlue300"]} strokeDasharray="4 2" shapeRendering="crispEdges" - height={80} label={(props) => ( 50 ? 85 : -3} + yOffset={25} title="Current" - offset={-10} color={theme.colors[tickField.lineColor ?? "brightBlue300"]} /> )} @@ -186,18 +188,21 @@ export const InterestRateModelChart = ({ const ReferenceLineLabel = (props: { value: number title: string - offset?: number + yOffset?: number + xOffset?: number color: string viewBox: { x: number y: number } }) => { + const yOffset = props.yOffset ?? 0 + const xOffset = props.xOffset ?? 0 return (
void - onClose?: () => void -} - -export const MigrationWarning: React.FC = ({ - onClick, - onClose, -}) => { - const { t } = useTranslation() - - const { account } = useAccount() - const { balanceTotal, isLoading } = useWalletAssetsTotals({ - address: account?.address, - }) - - if (isLoading || BigNumber(balanceTotal).isZero()) return null - - return ( - - - - -
- {t("migration.warning.text")} - - - {t("stats.tiles.link")} - - -
-
- - { - e.stopPropagation() - onClose?.() - }} - /> - -
- ) -} diff --git a/src/sections/pools/PoolsPage.utils.ts b/src/sections/pools/PoolsPage.utils.ts index c81e1917c..72349f35e 100644 --- a/src/sections/pools/PoolsPage.utils.ts +++ b/src/sections/pools/PoolsPage.utils.ts @@ -99,7 +99,7 @@ export const usePools = () => { (sp) => sp?.tokenIn === asset.id, )?.spotPrice - const tradability = getTradabilityFromBits(asset.bits) + const tradability = getTradabilityFromBits(asset.bits ?? 0) const apiSpotPrice = spotPrices.data?.find( (sp) => sp?.tokenIn === stableCoinId, diff --git a/src/sections/pools/farms/position/FarmingPosition.utils.tsx b/src/sections/pools/farms/position/FarmingPosition.utils.tsx index 19fe744f2..3a9f3fdeb 100644 --- a/src/sections/pools/farms/position/FarmingPosition.utils.tsx +++ b/src/sections/pools/farms/position/FarmingPosition.utils.tsx @@ -3,11 +3,11 @@ import { TDeposit, useAccountAssets } from "api/deposits" import { useMemo } from "react" import { TLPData, useLiquidityPositionData } from "utils/omnipool" import { BN_0 } from "utils/constants" -import { useSDKPools } from "api/pools" import { useDisplayShareTokenPrice } from "utils/displayAsset" import { TShareToken, useAssets } from "providers/assets" import { scaleHuman } from "utils/balance" import { useTotalIssuances } from "api/totalIssuance" +import { useXYKPools } from "api/xyk" type TokenAmount = { id: string @@ -89,10 +89,10 @@ export const useAllXYKDeposits = (address?: string) => { const issuances = useTotalIssuances() const shareTokeSpotPrices = useDisplayShareTokenPrice(uniqAssetIds) - const pools = useSDKPools() + const { data: xykPools, isLoading: isXykPoolsLoading } = useXYKPools() const isLoading = - pools.isInitialLoading || + isXykPoolsLoading || issuances.isInitialLoading || shareTokeSpotPrices.isInitialLoading @@ -103,7 +103,7 @@ export const useAllXYKDeposits = (address?: string) => { const { asset, depositNft } = deposit const shareTokenIssuance = issuances.data?.get(asset.id) - const pool = pools.data?.find( + const pool = xykPools?.find( (pool) => pool.address === asset.poolAddress, ) @@ -145,7 +145,7 @@ export const useAllXYKDeposits = (address?: string) => { }, {}, ), - [depositNftsData, issuances.data, pools.data, shareTokeSpotPrices.data], + [depositNftsData, issuances.data, xykPools, shareTokeSpotPrices.data], ) return { data, isLoading } diff --git a/src/sections/pools/modals/AddLiquidity/AddLiquidity.utils.ts b/src/sections/pools/modals/AddLiquidity/AddLiquidity.utils.ts index a5c17a3b1..6cd0376ad 100644 --- a/src/sections/pools/modals/AddLiquidity/AddLiquidity.utils.ts +++ b/src/sections/pools/modals/AddLiquidity/AddLiquidity.utils.ts @@ -3,7 +3,6 @@ import { calculate_shares, verify_asset_cap, } from "@galacticcouncil/math-omnipool" -import { useTokenBalance } from "api/balances" import { useMaxAddLiquidityLimit } from "api/consts" import { useOmnipoolFee, @@ -13,7 +12,6 @@ import { } from "api/omnipool" import BigNumber from "bignumber.js" import { useMemo } from "react" -import { OMNIPOOL_ACCOUNT_ADDRESS } from "utils/api" import { useDisplayPrice } from "utils/displayAsset" import { useRpcProvider } from "providers/rpcProvider" import { useTranslation } from "react-i18next" @@ -27,7 +25,7 @@ import { ApiPromise } from "@polkadot/api" import { useXYKConsts } from "api/xyk" import { useEstimatedFees } from "api/transaction" import { usePoolData } from "sections/pools/pool/Pool" -import { TAsset, useAssets } from "providers/assets" +import { TAsset } from "providers/assets" import { useAccountAssets } from "api/deposits" export const getAddToOmnipoolFee = ( @@ -115,7 +113,6 @@ export const useAddToOmnipoolZod = ( ) => { const { t } = useTranslation() const { pool } = usePoolData() - const { hub } = useAssets() const { decimals, symbol } = pool.meta @@ -126,12 +123,8 @@ export const useAddToOmnipoolZod = ( const omnipoolAssets = useOmnipoolDataObserver() const omnipoolAsset = omnipoolAssets.dataMap?.get(assetId) + const hubBalance = omnipoolAssets.hubToken?.balance - const { data: hubBalance } = useTokenBalance(hub.id, OMNIPOOL_ACCOUNT_ADDRESS) - const { data: poolBalance } = useTokenBalance( - assetId, - OMNIPOOL_ACCOUNT_ADDRESS, - ) const { data: maxAddLiquidityLimit } = useMaxAddLiquidityLimit() const isFarms = farms.length @@ -161,16 +154,15 @@ export const useAddToOmnipoolZod = ( minPoolLiquidity === undefined || omnipoolAsset === undefined || hubBalance === undefined || - poolBalance === undefined || maxAddLiquidityLimit === undefined ) return undefined - const assetReserve = poolBalance.balance.toString() + const assetReserve = omnipoolAsset.balance const assetHubReserve = omnipoolAsset.hubReserve const assetShares = omnipoolAsset.shares const assetCap = omnipoolAsset.cap - const totalHubReserve = hubBalance.total.toString() + const totalHubReserve = hubBalance const circuitBreakerLimit = maxAddLiquidityLimit .multipliedBy(assetReserve) diff --git a/src/sections/pools/modals/CreateXYKPool/CreateXYKPool.tsx b/src/sections/pools/modals/CreateXYKPool/CreateXYKPool.tsx index 7ad62507f..eac76a03c 100644 --- a/src/sections/pools/modals/CreateXYKPool/CreateXYKPool.tsx +++ b/src/sections/pools/modals/CreateXYKPool/CreateXYKPool.tsx @@ -1,4 +1,4 @@ -import { useGetXYKPools } from "api/xyk" +import { useXYKPools } from "api/xyk" import { useMemo, useState } from "react" import { AssetsModalContent } from "sections/assets/AssetsModal" import { CreateXYKPoolForm } from "./CreateXYKPoolForm" @@ -44,13 +44,16 @@ export const CreateXYKPool = ({ onAssetSelectClose, children, }: CreateXYKPoolProps) => { - const { data: xykPools } = useGetXYKPools() + const { data: xykPools } = useXYKPools() const allowedAssets = useAllowedXYKPoolAssets() const allowedAssetIds = allowedAssets.map(({ id }) => id) const poolExclusivityMap = useMemo( - () => createPoolExclusivityMap(xykPools?.map(({ assets }) => assets) ?? []), + () => + createPoolExclusivityMap( + xykPools?.map(({ tokens }) => tokens.map((token) => token.id)) ?? [], + ), [xykPools], ) diff --git a/src/sections/pools/pool/capacity/PoolCapacity.utils.ts b/src/sections/pools/pool/capacity/PoolCapacity.utils.ts index 134f7c197..42776f936 100644 --- a/src/sections/pools/pool/capacity/PoolCapacity.utils.ts +++ b/src/sections/pools/pool/capacity/PoolCapacity.utils.ts @@ -1,6 +1,4 @@ import { useOmnipoolDataObserver } from "api/omnipool" -import { useTokenBalance } from "api/balances" -import { OMNIPOOL_ACCOUNT_ADDRESS } from "utils/api" import { useMemo } from "react" import { BN_NAN } from "utils/constants" import BN from "bignumber.js" @@ -9,19 +7,19 @@ import { OmniMath } from "@galacticcouncil/sdk" import { useAssets } from "providers/assets" export const usePoolCapacity = (id: string) => { - const { hub, getAssetWithFallback } = useAssets() + const { getAssetWithFallback } = useAssets() const omnipoolAssets = useOmnipoolDataObserver() - const hubBalance = useTokenBalance(hub.id, OMNIPOOL_ACCOUNT_ADDRESS) + const hubBalance = omnipoolAssets.hubToken?.balance - const isLoading = omnipoolAssets.isLoading || hubBalance.isLoading + const isLoading = omnipoolAssets.isLoading const data = useMemo(() => { - if (!omnipoolAssets.dataMap || !hubBalance.data) return undefined + if (!omnipoolAssets.dataMap || !hubBalance) return undefined const asset = omnipoolAssets.dataMap.get(id) - if (!asset || !hubBalance?.data) + if (!asset || !hubBalance) return { capacity: BN_NAN, filled: BN_NAN, @@ -34,7 +32,7 @@ export const usePoolCapacity = (id: string) => { const assetReserve = asset.balance const assetHubReserve = asset.hubReserve const assetCap = asset.cap - const totalHubReserve = hubBalance.data.total.toString() + const totalHubReserve = hubBalance const capDifference = OmniMath.calculateCapDifference( assetReserve, @@ -59,7 +57,7 @@ export const usePoolCapacity = (id: string) => { const filledPercent = filled.div(capacity).times(100) return { capacity, filled, filledPercent, symbol } - }, [getAssetWithFallback, hubBalance.data, id, omnipoolAssets.dataMap]) + }, [getAssetWithFallback, hubBalance, id, omnipoolAssets.dataMap]) return { data, isLoading } } diff --git a/src/sections/provider/ProviderBalancer.tsx b/src/sections/provider/ProviderBalancer.tsx deleted file mode 100644 index 14111da48..000000000 --- a/src/sections/provider/ProviderBalancer.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { PROVIDER_URLS, useProviderRpcUrlStore } from "api/provider" -import { AppLoader } from "components/AppLoader/AppLoader" -import { useShallow } from "hooks/useShallow" -import { PropsWithChildren, useEffect } from "react" -import { identity } from "utils/helpers" -import { pingRpc } from "utils/rpc" -import { pick, uniqBy } from "utils/rx" - -async function pingAllAndSort() { - const fastestRpc = await Promise.race( - PROVIDER_URLS.map(async (url) => { - const time = await pingRpc(url) - return { url, time } - }), - ) - - const sortedRpcList = uniqBy(identity, [fastestRpc.url, ...PROVIDER_URLS]) - useProviderRpcUrlStore.getState().setRpcUrlList(sortedRpcList) -} - -export const ProviderBalancer: React.FC = ({ children }) => { - const { rpcUrlList } = useProviderRpcUrlStore( - useShallow((state) => pick(state, ["rpcUrlList"])), - ) - - const shouldPing = rpcUrlList.length === 0 - - useEffect(() => { - if (shouldPing) { - pingAllAndSort() - } - }, [shouldPing]) - return shouldPing ? : children -} diff --git a/src/sections/provider/ProviderSelectModal.styled.ts b/src/sections/provider/ProviderSelectModal.styled.ts index 804b12511..540dbcad4 100644 --- a/src/sections/provider/ProviderSelectModal.styled.ts +++ b/src/sections/provider/ProviderSelectModal.styled.ts @@ -37,3 +37,26 @@ export const SSwitchContainer = styled.div` margin: 0 -24px; } ` + +export const SSWitchContent = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +` + +export const SAutoModeActiveContainer = styled.div` + border-radius: 8px; + background: rgba(${theme.rgbColors.primaryA06}, 0.06); + position: relative; + + pointer-events: none; + + & > button { + position: absolute; + inset: 0; + border: 0; + box-shadow: none; + height: 100%; + } +` diff --git a/src/sections/provider/ProviderSelectModal.tsx b/src/sections/provider/ProviderSelectModal.tsx index 3a6565284..45be411bb 100644 --- a/src/sections/provider/ProviderSelectModal.tsx +++ b/src/sections/provider/ProviderSelectModal.tsx @@ -1,4 +1,4 @@ -import { useProviderRpcUrlStore } from "api/provider" +import { useActiveProvider, useProviderRpcUrlStore } from "api/provider" import { Modal } from "components/Modal/Modal" import { useState } from "react" @@ -7,19 +7,28 @@ import { Text } from "components/Typography/Text/Text" import { useTranslation } from "react-i18next" import { ProviderSelectForm } from "sections/provider/ProviderSelectForm" import { useRpcStore } from "state/store" -import { SSwitchContainer } from "./ProviderSelectModal.styled" +import { + SAutoModeActiveContainer, + SSwitchContainer, + SSWitchContent, +} from "./ProviderSelectModal.styled" import { DeleteModal } from "./components/DeleteModal/DeleteModal" import { Button } from "components/Button/Button" +import { ProviderItem } from "sections/provider/components/ProviderItem/ProviderItem" +import { useRpcProvider } from "providers/rpcProvider" export function ProviderSelectModal(props: { open: boolean onClose: () => void }) { + const { isLoaded } = useRpcProvider() const { setRpcUrl, autoMode, setAutoMode } = useProviderRpcUrlStore() const [removeRpcUrl, setRemoveRpcUrl] = useState() const { t } = useTranslation() const { removeRpc } = useRpcStore() + const activeProvider = useActiveProvider() + return ( <> - -
- - {t("rpc.change.modal.autoMode.title")} - - - {t("rpc.change.modal.autoMode.desc")} - -
-
- - {autoMode - ? t("rpc.change.modal.autoMode.enabled") - : t("rpc.change.modal.autoMode.disabled")} - - -
+ + +
+ + {t("rpc.change.modal.autoMode.title")} + + + {t("rpc.change.modal.autoMode.desc")} + +
+
+ + {autoMode + ? t("rpc.change.modal.autoMode.enabled") + : t("rpc.change.modal.autoMode.disabled")} + + +
+
+ {autoMode && activeProvider.url && ( + + + {!isLoaded && ( + + )} + + )}
diff --git a/src/sections/provider/components/ProviderItem/ProviderItem.tsx b/src/sections/provider/components/ProviderItem/ProviderItem.tsx index 9ff124dc3..9a1edbf12 100644 --- a/src/sections/provider/components/ProviderItem/ProviderItem.tsx +++ b/src/sections/provider/components/ProviderItem/ProviderItem.tsx @@ -19,8 +19,9 @@ type ProviderItemProps = { url: string isActive?: boolean custom?: boolean - onClick: () => void + onClick?: () => void onRemove?: (id: string) => void + className?: string } export const ProviderItem = ({ @@ -30,6 +31,7 @@ export const ProviderItem = ({ custom, onClick, onRemove, + className, }: ProviderItemProps) => { const [isEdit, setIsEdit] = useState(false) const store = useProviderRpcUrlStore() @@ -47,7 +49,7 @@ export const ProviderItem = ({ ) return ( - +
{ - const { featureFlags } = useRpcProvider() - return featureFlags.referrals ? : null -} diff --git a/src/sections/referrals/ReferralsPage.tsx b/src/sections/referrals/ReferralsPage.tsx index 5dd458d1c..190c72068 100644 --- a/src/sections/referrals/ReferralsPage.tsx +++ b/src/sections/referrals/ReferralsPage.tsx @@ -1,4 +1,3 @@ -import { Navigate } from "@tanstack/react-location" import { CodeForm } from "./components/CodeForm/CodeForm" import { ReferralsTableTableWrapper } from "./components/ReferralsTable/ReferralsTableWrapper" import { FaqAccordion } from "./components/FaqAccordion/FaqAccordion" @@ -13,12 +12,10 @@ import { getChainSpecificAddress } from "utils/formatting" import { ReferralsSkeleton } from "./ReferralsSkeleton" export const ReferralsWrapper = () => { - const { isLoaded, featureFlags } = useRpcProvider() + const { isLoaded } = useRpcProvider() if (!isLoaded) return - if (!featureFlags.referrals) return - return } diff --git a/src/sections/stats/sections/LRNA/components/Distribution/Distribution.tsx b/src/sections/stats/sections/LRNA/components/Distribution/Distribution.tsx index be7d5138a..87a8b530a 100644 --- a/src/sections/stats/sections/LRNA/components/Distribution/Distribution.tsx +++ b/src/sections/stats/sections/LRNA/components/Distribution/Distribution.tsx @@ -10,13 +10,13 @@ import { ChartLabel } from "./ChartLabel" import { DoughnutChart } from "sections/stats/components/DoughnutChart/DoughnutChart" import { makePercent } from "./Distribution.utils" import { DistributionSliceLabel } from "./DistributionSliceLabel" -import { DEPOSIT_CLASS_ID, OMNIPOOL_ACCOUNT_ADDRESS } from "utils/api" +import { DEPOSIT_CLASS_ID } from "utils/api" import { SContainerVertical } from "sections/stats/StatsPage.styled" import { useTotalIssuances } from "api/totalIssuance" -import { useTokenBalance } from "api/balances" import { BN_0 } from "utils/constants" import { useAssets } from "providers/assets" import BN from "bignumber.js" +import { useOmnipoolDataObserver } from "api/omnipool" export const Distribution = () => { const { t } = useTranslation() @@ -28,29 +28,21 @@ export const Distribution = () => { const { data: issuances, isLoading: isIssuanceLoading } = useTotalIssuances() const issuance = issuances?.get(meta.id) - const omnipoolBalanceQuery = useTokenBalance( - meta.id, - OMNIPOOL_ACCOUNT_ADDRESS, - ) - - const symbol = meta?.symbol + const omnipoolAssets = useOmnipoolDataObserver() + const hubBalance = omnipoolAssets.hubToken?.balance const [activeSection, setActiveSection] = useState<"overview" | "chart">( "overview", ) - const isLoading = omnipoolBalanceQuery.isLoading + const isLoading = omnipoolAssets.isLoading const outsideOmnipool = - issuance && omnipoolBalanceQuery.data - ? issuance.minus(omnipoolBalanceQuery.data.total) - : undefined + issuance && hubBalance ? issuance.minus(hubBalance) : undefined const outsidePercent = makePercent(outsideOmnipool, issuance ?? BN_0) const insidePercent = makePercent( - omnipoolBalanceQuery.data?.total - ? BN(omnipoolBalanceQuery.data.total) - : BN_0, + hubBalance ? BN(hubBalance) : BN_0, issuance ?? BN_0, ) @@ -71,8 +63,8 @@ export const Distribution = () => { > { label: ( ), @@ -113,7 +105,7 @@ export const Distribution = () => { label: ( ), diff --git a/src/sections/transaction/ReviewTransactionForm.utils.tsx b/src/sections/transaction/ReviewTransactionForm.utils.tsx index af9785e9a..42c471f35 100644 --- a/src/sections/transaction/ReviewTransactionForm.utils.tsx +++ b/src/sections/transaction/ReviewTransactionForm.utils.tsx @@ -42,7 +42,7 @@ export const useTransactionValues = ({ } tx: SubmittableExtrinsic<"promise"> }) => { - const { api, featureFlags } = useRpcProvider() + const { api } = useRpcProvider() const { native, getAsset } = useAssets() const { account } = useAccount() const bestNumber = useBestNumber() @@ -58,12 +58,9 @@ export const useTransactionValues = ({ /* REFERRALS */ - const referrer = useUserReferrer( - featureFlags.referrals ? account?.address : undefined, - ) + const referrer = useUserReferrer(account?.address) - const isLinkedAccount = - featureFlags.referrals && !xcallMeta ? !!referrer.data?.length : true + const isLinkedAccount = !xcallMeta ? !!referrer.data?.length : true const storedReferralCodes = useReferralCodesStore() const storedReferralCode = account?.address @@ -71,7 +68,6 @@ export const useTransactionValues = ({ : undefined const boundedTx = - featureFlags.referrals && !isLinkedAccount && storedReferralCode && tx.method.method !== "linkCode" && diff --git a/src/sections/wallet/addToken/modal/components/TokenInfo/TokenInfo.tsx b/src/sections/wallet/addToken/modal/components/TokenInfo/TokenInfo.tsx index b7012d3f9..67681852d 100644 --- a/src/sections/wallet/addToken/modal/components/TokenInfo/TokenInfo.tsx +++ b/src/sections/wallet/addToken/modal/components/TokenInfo/TokenInfo.tsx @@ -9,7 +9,7 @@ import { Text } from "components/Typography/Text/Text" import { useTranslation } from "react-i18next" import { TExternalAsset } from "sections/wallet/addToken/AddToken.utils" import { useMemo } from "react" -import { useGetXYKPools } from "api/xyk" +import { useAllXykPools } from "api/xyk" import { DisplayValue } from "components/DisplayValue/DisplayValue" import { BN_0 } from "utils/constants" import { useExternalXYKVolume } from "./TokenInfo.utils" @@ -45,7 +45,7 @@ export const TokenInfo = ({ const { setIsWhiteListed } = useUserExternalTokenStore() const refetchProvider = useRefetchProviderData() const parachains = useParachainAmount(externalAsset.id) - const xykPools = useGetXYKPools() + const { data: xykPools } = useAllXykPools() const { totalSupplyInternal, totalSupplyExternal } = rugCheckData ?? {} const externalAssetRegistry = useExternalAssetRegistry() const refetchAssetHub = externalAssetRegistry[assethub.parachainId].refetch @@ -72,13 +72,13 @@ export const TokenInfo = ({ }) const { isXYKPool, pools } = useMemo(() => { - if (!isChainStored || !xykPools.data) + if (!isChainStored || !xykPools) return { isXYKPool: false, pools: undefined } const chainAsset = getExternalByExternalId(externalAsset.id) if (chainAsset) { - const filteredXykPools = xykPools.data.filter((shareToken) => + const filteredXykPools = xykPools.filter((shareToken) => shareToken.assets.includes(chainAsset.id), ) @@ -90,7 +90,7 @@ export const TokenInfo = ({ } return { isXYKPool: false, pools: undefined } - }, [externalAsset.id, getExternalByExternalId, isChainStored, xykPools.data]) + }, [externalAsset.id, getExternalByExternalId, isChainStored, xykPools]) const warningFlags = Object.fromEntries( rugCheckData?.warnings.map(({ type, diff }) => { diff --git a/src/utils/navigation.ts b/src/utils/navigation.ts index 38e41b5f4..af753fb65 100644 --- a/src/utils/navigation.ts +++ b/src/utils/navigation.ts @@ -161,7 +161,7 @@ export const MENU_ITEMS = [ mobVisible: false, tabVisible: true, mobOrder: 6, - asyncEnabled: true, + asyncEnabled: false, }, { key: "memepad", diff --git a/src/utils/queryKeys.ts b/src/utils/queryKeys.ts index eb941d121..276b2632e 100644 --- a/src/utils/queryKeys.ts +++ b/src/utils/queryKeys.ts @@ -50,6 +50,8 @@ export const QUERY_KEYS = { ids.join("."), ], pools: [QUERY_KEY_PREFIX, "pools"], + omnipoolTokens: ["omnipoolTokens"], + hubToken: ["hubToken"], poolShareToken: (poolId: AccountId32 | string) => [ QUERY_KEY_PREFIX, "poolShareToken", @@ -266,7 +268,8 @@ export const QUERY_KEYS = { pool, block, ], - xykPools: ["xykPools"], //TODO: refresh each block?? + xykPools: ["xykPools"], + allXykPools: ["allXykPools"], xykConsts: ["xykConsts"], shareTokens: (rpc: string) => ["shareTokens", rpc], totalXYKLiquidity: (address?: string) => [ @@ -344,7 +347,3 @@ export const QUERY_KEYS = { ) => ["xcmTransfer", asset, srcAddr, srcChain, dstAddr, dstChain], externalApi: (chain: string) => ["externalApi", chain], } as const - -export const WS_QUERY_KEYS = { - omnipoolAssets: ["omnipoolAssets_"], -}