Skip to content

Commit

Permalink
fix: fetch vaults
Browse files Browse the repository at this point in the history
  • Loading branch information
Majorfi committed Nov 10, 2024
1 parent 7f51e54 commit 36aa4d9
Show file tree
Hide file tree
Showing 5 changed files with 388 additions and 348 deletions.
36 changes: 20 additions & 16 deletions apps/common/components/ImageWithFallback.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
import React, {useState} from 'react';
import Image from 'next/image';
import {cl} from '@builtbymom/web3/utils';
import {useUpdateEffect} from '@react-hookz/web';

import type {ImageProps} from 'next/image';
import type {CSSProperties, ReactElement} from 'react';

type TImageWithFallback = ImageProps & {
smWidth?: number;
smHeight?: number;
};
export function ImageWithFallback(props: TImageWithFallback): ReactElement {
const {alt, src, smWidth, smHeight, ...rest} = props;
const [imageSrc, set_imageSrc] = useState(`${src}?fallback=true`);
function ImageWithFallback(props: ImageProps & {altSrc?: string}): ReactElement {
const {alt, src, altSrc, ...rest} = props;
const [imageSrc, set_imageSrc] = useState(altSrc ? src : `${src}?fallback=true`);
const [imageStyle, set_imageStyle] = useState<CSSProperties>({});

useUpdateEffect((): void => {
set_imageSrc(`${src}?fallback=true`);
set_imageSrc(altSrc ? src : `${src}?fallback=true`);
set_imageStyle({});
}, [src]);

return (
<Image
alt={alt}
src={imageSrc}
style={imageStyle}
className={cl(
`w-[${smWidth ?? rest.width}px] min-w-[${smWidth ?? rest.width}px]`,
`h-[${smHeight ?? rest.height}px] min-h-[${smHeight ?? rest.height}px]`,
`md:w-[${rest.width}px] md:h-[${rest.height}px]`,
`md:min-w-[${rest.width}px] md:min-h-[${rest.height}px]`
)}
loading={'eager'}
className={'animate-fadeIn'}
style={{
minWidth: props.width,
minHeight: props.height,
maxWidth: props.width,
maxHeight: props.height,
...imageStyle
}}
onError={(): void => {
if (altSrc && imageSrc !== `${altSrc}?fallback=true`) {
console.warn('using placeholder');
set_imageSrc(`${altSrc}?fallback=true`);
return;
}
set_imageSrc('/placeholder.png');
set_imageStyle({filter: 'opacity(0.2)'});
}}
{...rest}
/>
);
}

export {ImageWithFallback};
168 changes: 103 additions & 65 deletions apps/common/hooks/useFetchYearnVaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,122 +9,160 @@ import type {KeyedMutator} from 'swr';
import type {TYDaemonVault, TYDaemonVaults} from '@yearn-finance/web-lib/utils/schemas/yDaemonVaultsSchemas';
import type {TDict} from '@builtbymom/web3/types';

/******************************************************************************
** The useFetchYearnVaults hook is used to fetch the vaults from the yDaemon
** API.
** It will fetch 3 kinds of vaults:
** - The active vaults
** - The vaults that are in the migration process
** - The retired vaults
*****************************************************************************/
function useFetchYearnVaults(chainIDs?: number[] | undefined): {
/************************************************************************************************
** Constants and Types for the useFetchYearnVaults hook
** These values are used to configure the pagination and API requests
************************************************************************************************/
const ITEMS_PER_PAGE = 200;
const DEFAULT_CHAIN_IDS = [1, 10, 137, 250, 8453, 42161];

type TUseFetchYearnVaultsProps = {
chainIDs?: number[];
shouldFetchMigrations?: boolean;
shouldFetchRetired?: boolean;
};

type TUseFetchYearnVaultsReturn = {
vaults: TDict<TYDaemonVault>;
vaultsMigrations: TDict<TYDaemonVault>;
vaultsRetired: TDict<TYDaemonVault>;
isLoading: boolean;
error: Error | null;
mutate: KeyedMutator<TYDaemonVaults>;
} {
const {yDaemonBaseUri: yDaemonBaseUriWithoutChain} = useYDaemonBaseURI();
};

/************************************************************************************************
** Helper function to create the URL parameters for the vaults API request
** This ensures consistency in how we build our API URLs
************************************************************************************************/
function getVaultsURLParams({chainIDs, page, limit}: {chainIDs: number[]; page: number; limit: number}): string {
return new URLSearchParams({
hideAlways: 'true',
orderBy: 'featuringScore',
orderDirection: 'desc',
strategiesDetails: 'withDetails',
strategiesCondition: 'inQueue',
chainIDs: chainIDs.join(','),
limit: limit.toString(),
page: page.toString()
}).toString();
}

/************************************************************************************************
** The useFetchYearnVaults hook fetches vault data from the yDaemon API
** It handles pagination and provides access to active, migrating, and retired vaults
** The hook now includes proper error handling and optional fetching of migrations/retired vaults
************************************************************************************************/
function useFetchYearnVaults({
chainIDs = DEFAULT_CHAIN_IDS,
shouldFetchMigrations = true,
shouldFetchRetired = true
}: TUseFetchYearnVaultsProps = {}): TUseFetchYearnVaultsReturn {
const {yDaemonBaseUri: baseUri} = useYDaemonBaseURI();
const [allVaults, set_allVaults] = useState<TYDaemonVaults>([]);
const [currentPage, set_currentPage] = useState<number>(1);
const limit = 200;
const [error, set_error] = useState<Error | null>(null);
const [isLoadingMore, set_isLoadingMore] = useState<boolean>(false);

// Fetch active vaults with pagination
const {
data: vaults,
isLoading,
mutate
data: currentPageVaults,
isLoading: isLoadingCurrentPage,
mutate,
error: currentPageError
} = useFetch<TYDaemonVaults>({
endpoint: `${yDaemonBaseUriWithoutChain}/vaults?${new URLSearchParams({
hideAlways: 'true',
orderBy: 'featuringScore',
orderDirection: 'desc',
strategiesDetails: 'withDetails',
strategiesCondition: 'inQueue',
chainIDs: chainIDs ? chainIDs.join(',') : [1, 10, 137, 250, 8453, 42161].join(','),
limit: limit.toString(),
page: currentPage.toString()
endpoint: `${baseUri}/vaults?${getVaultsURLParams({
chainIDs,
page: currentPage,
limit: ITEMS_PER_PAGE
})}`,
schema: yDaemonVaultsSchema
});

// Handle pagination and vault accumulation
useEffect(() => {
let hasMore = true;
if (vaults) {
if (vaults.length < limit) {
hasMore = false;
}
set_allVaults(prev => [...prev, ...vaults]);
if (hasMore) {
set_currentPage(prev => prev + 1);
}
if (currentPageError) {
set_error(currentPageError);
return;
}
}, [vaults]);

// const vaultsMigrations: TYDaemonVaults = useMemo(() => [], []);
if (!currentPageVaults || isLoadingMore) {
return;
}

set_isLoadingMore(true);
set_allVaults(prev => [...prev, ...currentPageVaults]);

const hasMore = currentPageVaults.length === ITEMS_PER_PAGE;
if (hasMore) {
set_currentPage(prev => prev + 1);
}
set_isLoadingMore(false);
}, [currentPageVaults, currentPageError, isLoadingMore]);

// Fetch migration vaults if enabled
const {data: vaultsMigrations} = useFetch<TYDaemonVaults>({
endpoint: `${yDaemonBaseUriWithoutChain}/vaults?${new URLSearchParams({
chainIDs: chainIDs ? chainIDs.join(',') : [1, 10, 137, 250, 8453, 42161].join(','),
migratable: 'nodust'
})}`,
endpoint: shouldFetchMigrations
? `${baseUri}/vaults?${new URLSearchParams({
chainIDs: chainIDs.join(','),
migratable: 'nodust'
})}`
: null,
schema: yDaemonVaultsSchema
});

// const vaultsRetired: TYDaemonVaults = useMemo(() => [], []);
// Fetch retired vaults if enabled
const {data: vaultsRetired} = useFetch<TYDaemonVaults>({
endpoint: `${yDaemonBaseUriWithoutChain}/vaults/retired`,
endpoint: shouldFetchRetired ? `${baseUri}/vaults/retired` : null,
schema: yDaemonVaultsSchema
});

// Process active vaults into dictionary
const vaultsObject = useDeepCompareMemo((): TDict<TYDaemonVault> => {
if (!allVaults) {
if (!allVaults?.length) {
return {};
}
const _vaultsObject = (allVaults || []).reduce((acc: TDict<TYDaemonVault>, vault): TDict<TYDaemonVault> => {
return allVaults.reduce((acc: TDict<TYDaemonVault>, vault): TDict<TYDaemonVault> => {
if (!vault.migration.available) {
acc[toAddress(vault.address)] = vault;
}
return acc;
}, {});
return _vaultsObject;
}, [allVaults]);

// Process migration vaults into dictionary
const vaultsMigrationsObject = useDeepCompareMemo((): TDict<TYDaemonVault> => {
if (!vaultsMigrations) {
if (!vaultsMigrations?.length) {
return {};
}
const _migratableVaultsObject = (vaultsMigrations || []).reduce(
(acc: TDict<TYDaemonVault>, vault): TDict<TYDaemonVault> => {
if (toAddress(vault.address) !== toAddress(vault.migration.address)) {
acc[toAddress(vault.address)] = vault;
}
return acc;
},
{}
);
return _migratableVaultsObject;
return vaultsMigrations.reduce((acc: TDict<TYDaemonVault>, vault): TDict<TYDaemonVault> => {
if (toAddress(vault.address) !== toAddress(vault.migration.address)) {
acc[toAddress(vault.address)] = vault;
}
return acc;
}, {});
}, [vaultsMigrations]);

// Process retired vaults into dictionary
const vaultsRetiredObject = useDeepCompareMemo((): TDict<TYDaemonVault> => {
if (!vaultsRetired) {
if (!vaultsRetired?.length) {
return {};
}
const _retiredVaultsObject = (vaultsRetired || []).reduce(
(acc: TDict<TYDaemonVault>, vault): TDict<TYDaemonVault> => {
acc[toAddress(vault.address)] = vault;
return acc;
},
{}
);
return _retiredVaultsObject;
return vaultsRetired.reduce((acc: TDict<TYDaemonVault>, vault): TDict<TYDaemonVault> => {
acc[toAddress(vault.address)] = vault;
return acc;
}, {});
}, [vaultsRetired]);

return {
vaults: vaultsObject,
vaultsMigrations: vaultsMigrationsObject,
vaultsRetired: vaultsRetiredObject,
isLoading,
isLoading: isLoadingCurrentPage || isLoadingMore,
error,
mutate
};
}

export {useFetchYearnVaults};
export type {TUseFetchYearnVaultsProps, TUseFetchYearnVaultsReturn};
Binary file modified bun.lockb
Binary file not shown.
Loading

0 comments on commit 36aa4d9

Please sign in to comment.