Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/wagmi vaults #208

Merged
merged 5 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Auto Generated PWA files
public/sw*
public/workbox*
ipfs/*
ipfs/*
public/worker*
4 changes: 1 addition & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: Test

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

Expand All @@ -25,4 +23,4 @@ jobs:
run: yarn --prefer-offline

- name: Test project
run: yarn node --experimental-vm-modules $(yarn bin jest)
run: yarn test
4 changes: 2 additions & 2 deletions apps/common/components/AmountInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ function AmountInput({
min={0}
aria-label={label}
value={displayedAmount}
onChange={onAmountChange ? (e) => onAmountChange(e.target.value) : undefined}
onChange={onAmountChange ? (e): void => onAmountChange(e.target.value) : undefined}
placeholder={loading ? '' : placeholder ?? '0'}
disabled={disabled}
/>
<Renderable shouldRender={!!maxAmount && !disabled}>
<button
onClick={onMaxClick ? () => onMaxClick() : undefined}
onClick={onMaxClick ? (): void => onMaxClick() : undefined}
className={'absolute right-2 ml-2 h-6 cursor-pointer border-none bg-neutral-900 px-2 py-1 text-xs text-neutral-0 transition-colors hover:bg-neutral-700'}>
{'Max'}
</button>
Expand Down
32 changes: 21 additions & 11 deletions apps/common/components/AppHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Link from 'next/link';
import {useRouter} from 'next/router';
import {AnimatePresence} from 'framer-motion';
import {Popover, Transition} from '@headlessui/react';
import {useIsMounted} from '@react-hookz/web';
import {VaultsHeader} from '@vaults/components/header/VaultsHeader';
import {VeYfiHeader} from '@veYFI/components/header/VeYfiHeader';
import Header from '@yearn-finance/web-lib/components/Header';
Expand All @@ -11,6 +12,7 @@ import {useWeb3} from '@yearn-finance/web-lib/contexts/useWeb3';
import BalanceReminderPopover from '@common/components/BalanceReminderPopover';
import {useMenu} from '@common/contexts/useMenu';
import LogoYearn from '@common/icons/LogoYearn';
import {YBalHeader} from '@yBal/components/header/YBalHeader';
import {YBribeHeader} from '@yBribe/components/header/YBribeHeader';
import {YCrvHeader} from '@yCRV/components/header/YCrvHeader';

Expand All @@ -20,12 +22,13 @@ import {MotionDiv} from './MotionDiv';
import type {ReactElement} from 'react';
import type {TMenu} from '@yearn-finance/web-lib/components/Header';

function Logo(): ReactElement {
const {pathname} = useRouter();
function Logo(): ReactElement {
const {pathname} = useRouter();

return (
<>
<YCrvHeader pathname={pathname} />
<YBalHeader pathname={pathname} />
<VaultsHeader pathname={pathname} />
<VeYfiHeader pathname={pathname} />
<YBribeHeader pathname={pathname} />
Expand All @@ -40,7 +43,7 @@ function Logo(): ReactElement {

}

function LogoPopover(): ReactElement {
function LogoPopover(): ReactElement {
const [isShowing, set_isShowing] = useState(false);

return (
Expand Down Expand Up @@ -94,17 +97,22 @@ function LogoPopover(): ReactElement {
);
}

export function AppHeader(): ReactElement {
const {pathname} = useRouter();
const {isActive} = useWeb3();
const {onOpenMenu} = useMenu();
const menu = useMemo((): TMenu[] => {
export function AppHeader(): ReactElement {
const isMounted = useIsMounted();
const {pathname} = useRouter();
const {isActive} = useWeb3();
const {onOpenMenu} = useMenu();
const menu = useMemo((): TMenu[] => {
const HOME_MENU = {path: '/', label: 'Home'};

if (pathname.startsWith('/ycrv')) {
return [HOME_MENU, ...APPS[AppName.YCRV].menu];
}

if (pathname.startsWith('/ybal')) {
return [HOME_MENU, ...APPS[AppName.YBAL].menu];
}

if (pathname.startsWith('/vaults')) {
return [HOME_MENU, ...APPS[AppName.VAULTS].menu];
}
Expand All @@ -124,8 +132,10 @@ export function AppHeader(): ReactElement {
];
}, [pathname]);

const supportedNetworks = useMemo((): number[] => {
if (pathname.startsWith('/ycrv') || pathname.startsWith('/veyfi') || pathname.startsWith('/ybribe')) {

const supportedNetworks = useMemo((): number[] => {
const ethereumOnlyPaths = ['/ycrv', '/ybal', '/veyfi', '/ybribe'];
if (ethereumOnlyPaths.some((path): boolean => pathname.startsWith(path))) {
return [1];
}

Expand All @@ -145,7 +155,7 @@ export function AppHeader(): ReactElement {
</AnimatePresence>
)}
extra={
<Renderable shouldRender={isActive}>
<Renderable shouldRender={isActive && isMounted()}>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is used to prevent some hydratation issue

<div className={'ml-4'}>
<BalanceReminderPopover />
</div>
Expand Down
20 changes: 19 additions & 1 deletion apps/common/components/Apps.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import Image from 'next/image';
import vaultsManifest from 'public/apps/vaults-manifest.json';
import veyfiManifest from 'public/apps/veyfi-manifest.json';
import yBalManifest from 'public/apps/ybal-manifest.json';
import ybribeManifest from 'public/apps/ybribe-manifest.json';
import ycrvManifest from 'public/apps/ycrv-manifest.json';
import {VAULTS_MENU} from '@vaults/constants/menu';
import {VEYFI_MENU} from '@veYFI/constants/menu';
import {YCRV_TOKEN_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
import {YBAL_TOKEN_ADDRESS, YCRV_TOKEN_ADDRESS} from '@yearn-finance/web-lib/utils/constants';
import LogoYearn from '@common/icons/LogoYearn';
import {YBAL_MENU} from '@yBal/constants/menu';
import {YBRIBE_MENU} from '@yBribe/constants/menu';
import {YCRV_MENU} from '@yCRV/constants/menu';

Expand All @@ -17,6 +19,7 @@ import type {TMetaFile} from './Meta';
export enum AppName {
VAULTS = 'Vaults',
YCRV = 'yCRV',
YBAL = 'yBal',
VEYFI = 'veYFI',
YBRIBE = 'yBribe'
}
Expand Down Expand Up @@ -55,6 +58,21 @@ export const APPS: { [key in AppName]: TApp } = {
priority />
)
},
yBal: {
name: AppName.YBAL,
href: '/ybal',
menu: YBAL_MENU,
manifest: yBalManifest,
icon: (
<Image
alt={'yBal'}
width={32}
height={32}
src={`${process.env.BASE_YEARN_ASSETS_URI}/1/${YBAL_TOKEN_ADDRESS}/logo-128.png`}
loading={'eager'}
priority />
)
},
veYFI: {
name: AppName.VEYFI,
menu: VEYFI_MENU,
Expand Down
37 changes: 16 additions & 21 deletions apps/common/components/BalanceReminderPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {useChainID} from '@yearn-finance/web-lib/hooks/useChainID';
import IconAddToMetamask from '@yearn-finance/web-lib/icons/IconAddToMetamask';
import IconCross from '@yearn-finance/web-lib/icons/IconCross';
import IconWallet from '@yearn-finance/web-lib/icons/IconWallet';
import {toAddress, toWagmiAddress, truncateHex} from '@yearn-finance/web-lib/utils/address';
import {toAddress, truncateHex} from '@yearn-finance/web-lib/utils/address';
import {toBigInt} from '@yearn-finance/web-lib/utils/format.bigNumber';
import {formatAmount} from '@yearn-finance/web-lib/utils/format.number';
import {useWallet} from '@common/contexts/useWallet';
Expand All @@ -26,10 +26,10 @@ type TBalanceReminderElement = {
symbol: string,
}

function TokenItem({element}: {element: TBalanceReminderElement}): ReactElement {
const {provider} = useWeb3();
const {safeChainID} = useChainID();
const balance = useBalance(element.address);
function TokenItem({element}: {element: TBalanceReminderElement}): ReactElement {
const {provider} = useWeb3();
const {safeChainID} = useChainID();
const balance = useBalance(element.address);

async function addTokenToMetamask(address: string, symbol: string, decimals: number, image: string): Promise<void> {
if (!provider) {
Expand All @@ -40,12 +40,7 @@ function TokenItem({element}: {element: TBalanceReminderElement}): ReactElement
const walletClient = await provider.getWalletClient();
await walletClient.watchAsset({
type: 'ERC20',
options: {
address: toWagmiAddress(address),
decimals,
symbol,
image
}
options: {address: toAddress(address), decimals, symbol, image}
});
} catch (error) {
captureException(error);
Expand Down Expand Up @@ -93,12 +88,12 @@ function TokenItem({element}: {element: TBalanceReminderElement}): ReactElement
}

export default function BalanceReminderPopover(): ReactElement {
const {balances, isLoading} = useWallet();
const {address, ens, isActive, onDesactivate} = useWeb3();
const {vaults} = useYearn();
const {balances, isLoading} = useWallet();
const {address, ens, isActive, onDesactivate} = useWeb3();
const {vaults} = useYearn();

const nonNullBalances = useMemo((): TDict<TBalanceData> => {
const nonNullBalances = Object.entries(balances).reduce((acc: TDict<TBalanceData>, [address, balance]): TDict<TBalanceData> => {
const nonNullBalances = useMemo((): TDict<TBalanceData> => {
const nonNullBalances = Object.entries(balances).reduce((acc: TDict<TBalanceData>, [address, balance]): TDict<TBalanceData> => {
if (toBigInt(balance?.raw) > 0n) {
acc[toAddress(address)] = balance;
}
Expand All @@ -107,8 +102,8 @@ export default function BalanceReminderPopover(): ReactElement {
return nonNullBalances;
}, [balances]);

const nonNullBalancesForVault = useMemo((): TBalanceReminderElement[] => {
const nonNullBalancesForVault = Object.entries(nonNullBalances).reduce((acc: TBalanceReminderElement[], [address, balance]): TBalanceReminderElement[] => {
const nonNullBalancesForVault = useMemo((): TBalanceReminderElement[] => {
const nonNullBalancesForVault = Object.entries(nonNullBalances).reduce((acc: TBalanceReminderElement[], [address, balance]): TBalanceReminderElement[] => {
const currentVault = vaults?.[toAddress(address)];
if (currentVault) {
acc.push({
Expand All @@ -123,7 +118,7 @@ export default function BalanceReminderPopover(): ReactElement {
return nonNullBalancesForVault;
}, [nonNullBalances, vaults]);

function renderNoTokenFallback(isLoading: boolean): ReactElement {
function renderNoTokenFallback(isLoading: boolean): ReactElement {
if (isLoading) {
return (
<div className={'py-4 text-center text-sm text-neutral-400'}>
Expand Down Expand Up @@ -153,7 +148,7 @@ export default function BalanceReminderPopover(): ReactElement {
leave={'transition ease-in duration-150'}
leaveFrom={'opacity-100 translate-y-0'}
leaveTo={'opacity-0 translate-y-1'}>
<Popover.Panel className={'yearn--shadow absolute right-0 top-6 z-[1000] mt-3 w-screen max-w-xs md:top-4 md:-right-4'}>
<Popover.Panel className={'yearn--shadow absolute right-0 top-6 z-[1000] mt-3 w-screen max-w-xs md:-right-4 md:top-4'}>
<div className={'overflow-hidden'}>
<div className={'relative bg-neutral-0 p-0'}>
<div className={'flex items-center justify-center border-b border-neutral-300 py-4 text-center'}>
Expand All @@ -165,7 +160,7 @@ export default function BalanceReminderPopover(): ReactElement {
) : 'Connect wallet'}
</b>
</div>
<div className={'absolute top-4 right-4'}>
<div className={'absolute right-4 top-4'}>
<button
onClick={onDesactivate}
className={'flex h-6 w-6 items-center justify-center rounded-full bg-neutral-200/50'}>
Expand Down
2 changes: 1 addition & 1 deletion apps/common/components/HeroTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type TProps = {
endTime?: TSeconds;
}

function HeroTimer({endTime}: TProps): ReactElement {
function HeroTimer({endTime}: TProps): ReactElement {
const time = useTimer({endTime});

return (
Expand Down
21 changes: 11 additions & 10 deletions apps/common/components/ImageWithFallback.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import React, {useState} from 'react';
import Image from 'next/image';
import performBatchedUpdates from '@yearn-finance/web-lib/utils/performBatchedUpdates';

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

export type TImageWithFallbackProps = ImageProps & {
onCatchError?: () => void;
};
function ImageWithFallback(props: TImageWithFallbackProps): ReactElement {
function ImageWithFallback(props: ImageProps & {onCatchError?: VoidFunction}): ReactElement {
const {alt, src, onCatchError, ...rest} = props;
const [imageSrc, set_imageSrc] = useState(src);
const [imageStyle, set_imageStyle] = useState<CSSProperties>({});

return (
<Image
alt={alt}
src={imageSrc}
loading={'eager'}
style={imageStyle}
onError={(): void => {
set_imageSrc('/placeholder.png');
onCatchError?.();
performBatchedUpdates((): void => {
set_imageSrc('/placeholder.png');
set_imageStyle({filter: 'opacity(0.2)'});
onCatchError?.();
});
}}
{...rest}
/>
{...rest} />
);
}

Expand Down
17 changes: 10 additions & 7 deletions apps/common/components/ListHead.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {useCallback} from 'react';
import {cl} from '@yearn-finance/web-lib/utils/cl';
import IconChevronPlain from '@common/icons/IconChevronPlain';

import type {ReactElement} from 'react';
Expand All @@ -12,19 +13,21 @@ export type TListHead = {
className?: string
}[],
dataClassName?: string,
wrapperClassName?: string,
tokenClassName?: string,
sortBy: string,
sortDirection: TSortDirection,
onSort: (sortBy: string, sortDirection: TSortDirection) => void
}

function ListHead({items, dataClassName, sortBy, sortDirection, onSort}: TListHead): ReactElement {
function ListHead({items, dataClassName, wrapperClassName, tokenClassName, sortBy, sortDirection, onSort}: TListHead): ReactElement {
const toggleSortDirection = (newSortBy: string): TSortDirection => {
return sortBy === newSortBy ? (
sortDirection === '' ? 'desc' : sortDirection === 'desc' ? 'asc' : 'desc'
) : 'desc';
};

const renderChevron = useCallback((shouldSortBy: boolean): ReactElement => {
const renderChevron = useCallback((shouldSortBy: boolean): ReactElement => {
if (shouldSortBy && sortDirection === 'desc') {
return <IconChevronPlain className={'yearn--sort-chevron'} />;
}
Expand All @@ -34,11 +37,11 @@ function ListHead({items, dataClassName, sortBy, sortDirection, onSort}: TListHe
return <IconChevronPlain className={'yearn--sort-chevron--off text-neutral-300 group-hover:text-neutral-500'} />;
}, [sortDirection]);

const [first, ...rest] = items;
const [first, ...rest] = items;
return (
<div className={'mt-4 grid w-full grid-cols-1 md:mt-0'}>
<div className={'yearn--table-head-wrapper'}>
<div className={'yearn--table-head-token-section'}>
<div className={cl('yearn--table-head-wrapper', wrapperClassName)}>
<div className={cl('yearn--table-head-token-section', tokenClassName)}>
<button
onClick={(): void => onSort(first.value, toggleSortDirection(first.value))}
className={'yearn--table-head-label-wrapper group'}>
Expand All @@ -49,13 +52,13 @@ function ListHead({items, dataClassName, sortBy, sortDirection, onSort}: TListHe
</button>
</div>

<div className={`yearn--table-head-data-section ${dataClassName || ''}`}>
<div className={cl('yearn--table-head-data-section', dataClassName)}>
{rest.map((item, index): ReactElement => (
<button
key={`${index}_${item.value}`}
onClick={(): void => onSort(item.value, toggleSortDirection(item.value))}
disabled={!item.sortable}
className={`yearn--table-head-label-wrapper group ${item.className}`}
className={cl('yearn--table-head-label-wrapper group', item.className)}
datatype={'number'}>
<p className={'yearn--table-head-label'}>
&nbsp;{item.label}
Expand Down
Loading