Skip to content

Commit

Permalink
feat: veYFI test (#383)
Browse files Browse the repository at this point in the history
* feat: veYFI test

* fix: lock

* fix: bump lib and enforce chainID

* fix: yfi addr

* chores: rename

* fix: remove more hardcoded chainID

* fix: some names

* fix: deps

* fix: rename async

* fix: path

* feat: remove claim all

* fix: remove deprecated wagmi config

* fix: incorrect use of AmountInput

* fix: redeem

* fix: dYFI price usage

* fix: price

* feat: prep mainnet

* feat: table and apr

* fix: reflow

* fix: copy

* fix: copy

* fix: copy

* fix: copy

* fix: change copy order

* fix: swap vote order

* fix: change order

* fix: change order

* feat: add discount

* fix: copy

---------

Co-authored-by: Majorfi <[email protected]>
  • Loading branch information
Majorfi and Majorfi authored Oct 18, 2023
1 parent 1133fd6 commit 8d32478
Show file tree
Hide file tree
Showing 69 changed files with 2,812 additions and 4,775 deletions.
9 changes: 7 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
'extends': ['./node_modules/@yearn-finance/web-lib/.eslintrc.cjs'],
'extends': ['./node_modules/@yearn-finance/web-lib/.eslintrc.cjs', 'plugin:react-hooks/recommended'],
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaFeatures': {
Expand All @@ -16,6 +16,11 @@ module.exports = {
'@typescript-eslint/indent': ['error', 'tab'],
'no-multi-spaces': ['error', {ignoreEOLComments: false}],
'no-mixed-spaces-and-tabs': 'error',
'react/jsx-max-props-per-line': 'off'
'react/jsx-max-props-per-line': 'off',
'react-hooks/exhaustive-deps': [
'warn', {
'additionalHooks': '^useAsyncTrigger$'
}
]
}
};
23 changes: 8 additions & 15 deletions apps/common/components/AmountInput.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {Renderable} from '@yearn-finance/web-lib/components/Renderable';
import {isZero} from '@yearn-finance/web-lib/utils/isZero';

import type {ReactElement} from 'react';
import type {TNormalizedBN} from '@common/types/types';

type TAmountInputProps = {
amount: string | number;
maxAmount?: string | number;
amount: TNormalizedBN;
maxAmount?: TNormalizedBN;
maxLabel?: string;
label?: string;
placeholder?: string;
legend?: string;
legend?: string | ReactElement;
error?: string;
disabled?: boolean;
loading?: boolean;
Expand All @@ -31,13 +31,6 @@ export function AmountInput({
onLegendClick,
onMaxClick
}: TAmountInputProps): ReactElement {
let displayedAmount = amount.toString();
if (isZero(displayedAmount) && !disabled) {
displayedAmount = '';
}
if (isZero(displayedAmount) && disabled) {
displayedAmount = '0';
}
return (
<div className={'w-full'}>
{label && (
Expand All @@ -49,10 +42,10 @@ export function AmountInput({
<div className={'relative flex w-full items-center justify-center'}>
<input
className={`h-10 w-full p-2 font-mono text-base font-normal outline-none ${maxAmount && !disabled ? 'pr-12' : null} ${error ? 'border border-solid border-[#EA5204] focus:border-[#EA5204]' : 'border-0 border-none'} ${disabled ? 'bg-neutral-300 text-neutral-600' : 'bg-neutral-0'}`}
type={'number'}
min={0}
type={'text'}
autoComplete={'off'}
aria-label={label}
value={displayedAmount}
value={amount.normalized}
onChange={onAmountChange ? (e): void => onAmountChange(e.target.value) : undefined}
placeholder={loading ? '' : placeholder ?? '0'}
disabled={disabled}
Expand All @@ -70,7 +63,7 @@ export function AmountInput({
role={onLegendClick ? 'button' : 'text'}
onClick={onLegendClick}
suppressHydrationWarning
className={`mt-1 pl-2 text-xs md:mr-0 ${error ? 'text-[#EA5204]' : 'text-neutral-600'}`}>
className={`mt-1 pl-1 text-xs md:mr-0 ${error ? 'text-[#EA5204]' : 'text-neutral-600'}`}>
{error ?? legend}
</legend>
</Renderable>
Expand Down
8 changes: 3 additions & 5 deletions apps/common/components/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const DropdownOption = (option: TDropdownOption): ReactElement => {
};

const DropdownEmpty = ({isSearching}: {isSearching: boolean}): ReactElement => {
if(isSearching) {
if (!isSearching) {
return (
<div className={'relative flex h-14 flex-col items-center justify-center px-4 text-center'}>
<div className={'flex h-10 items-center justify-center'}>
Expand Down Expand Up @@ -170,10 +170,8 @@ export const Dropdown = ({selected, options, onChange, label, legend, isDisabled
leaveFrom={'transform scale-100 opacity-100'}
leaveTo={'transform scale-95 opacity-0'}
afterLeave={(): void => {
performBatchedUpdates((): void => {
set_isOpen(false);
set_search('');
});
set_isOpen(false);
set_search('');
}}>
<Combobox.Options className={'yearn--dropdown-menu z-50'}>
{filteredOptions.length === 0 ? (
Expand Down
28 changes: 20 additions & 8 deletions apps/common/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
import {cl} from '@yearn-finance/web-lib/utils/cl';

import type {ChangeEvent, ReactElement} from 'react';

export type TSearchBar = {
searchPlaceholder: string;
searchValue: string;
set_searchValue: (searchValue: string) => void;
}
className?: string;
};

export function SearchBar({searchPlaceholder, searchValue, set_searchValue}: TSearchBar): ReactElement {
export function SearchBar({searchPlaceholder, searchValue, set_searchValue, className}: TSearchBar): ReactElement {
return (
<>
<div className={'mt-1 flex h-10 w-full max-w-md items-center border border-neutral-0 bg-neutral-0 p-2 md:w-2/3'}>
<div
className={cl(
className,
'mt-1 flex h-10 w-full max-w-md items-center border border-neutral-0 bg-neutral-0 p-2 md:w-2/3'
)}>
<div className={'relative flex h-10 w-full flex-row items-center justify-between'}>
<input
id={'search'}
suppressHydrationWarning
className={'h-10 w-full overflow-x-scroll border-none bg-transparent px-0 py-2 text-base outline-none scrollbar-none placeholder:text-neutral-400'}
className={
'h-10 w-full overflow-x-scroll border-none bg-transparent px-0 py-2 text-base outline-none scrollbar-none placeholder:text-neutral-400'
}
type={'text'}
placeholder={searchPlaceholder}
value={searchValue}
onChange={(e: ChangeEvent<HTMLInputElement>): void => {
if (set_searchValue) {
set_searchValue(e.target.value);
}
}} />
}}
/>
<div className={'absolute right-0 text-neutral-400'}>
<svg
width={'20'}
Expand All @@ -33,11 +43,13 @@ export function SearchBar({searchPlaceholder, searchValue, set_searchValue}: TSe
<path
fillRule={'evenodd'}
clipRule={'evenodd'}
d={'M10 1C5.02972 1 1 5.02972 1 10C1 14.9703 5.02972 19 10 19C12.1249 19 14.0779 18.2635 15.6176 17.0318L21.2929 22.7071C21.6834 23.0976 22.3166 23.0976 22.7071 22.7071C23.0976 22.3166 23.0976 21.6834 22.7071 21.2929L17.0318 15.6176C18.2635 14.0779 19 12.1249 19 10C19 5.02972 14.9703 1 10 1ZM3 10C3 6.13428 6.13428 3 10 3C13.8657 3 17 6.13428 17 10C17 13.8657 13.8657 17 10 17C6.13428 17 3 13.8657 3 10Z'}
fill={'currentcolor'}/>
d={
'M10 1C5.02972 1 1 5.02972 1 10C1 14.9703 5.02972 19 10 19C12.1249 19 14.0779 18.2635 15.6176 17.0318L21.2929 22.7071C21.6834 23.0976 22.3166 23.0976 22.7071 22.7071C23.0976 22.3166 23.0976 21.6834 22.7071 21.2929L17.0318 15.6176C18.2635 14.0779 19 12.1249 19 10C19 5.02972 14.9703 1 10 1ZM3 10C3 6.13428 6.13428 3 10 3C13.8657 3 17 6.13428 17 10C17 13.8657 13.8657 17 10 17C6.13428 17 3 13.8657 3 10Z'
}
fill={'currentcolor'}
/>
</svg>
</div>

</div>
</div>
</>
Expand Down
48 changes: 39 additions & 9 deletions apps/common/components/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import {useCallback, useMemo, useState} from 'react';
import {sort} from '@veYFI/utils';
import {cl} from '@yearn-finance/web-lib/utils/cl';
import {isZero} from '@yearn-finance/web-lib/utils/isZero';
import {Pagination} from '@common/components/Pagination';
import {usePagination} from '@common/hooks/usePagination';
import {IconChevronPlain} from '@common/icons/IconChevronPlain';
Expand All @@ -25,6 +24,7 @@ type TMetadata<T> = {
sortable?: boolean;
fullWidth?: boolean;
columnSpan?: number;
isDisabled?: (item: T) => boolean;
format?: (item: T) => string | number;
transform?: (item: T) => ReactElement;
}
Expand All @@ -36,9 +36,10 @@ type TTableProps<T> = {
initialSortBy?: Extract<keyof T, string>;
onRowClick?: (item: T) => void;
itemsPerPage?: number;
isLoading?: boolean;
}

export function Table<T>({metadata, data, columns, initialSortBy, onRowClick, itemsPerPage}: TTableProps<T>): ReactElement {
export function Table<T>({metadata, data, columns, initialSortBy, onRowClick, itemsPerPage, isLoading}: TTableProps<T>): ReactElement {
const [{sortedBy, order}, set_state] = useState<TState<T>>({sortedBy: initialSortBy, order: 'desc'});

const sortedData = useMemo((): T[] => {
Expand All @@ -65,10 +66,11 @@ export function Table<T>({metadata, data, columns, initialSortBy, onRowClick, it
9: 'md:grid-cols-9',
10: 'md:grid-cols-10',
11: 'md:grid-cols-11',
12: 'md:grid-cols-12'
12: 'md:grid-cols-12',
13: 'md:grid-cols-13'
};

const numberOfColumns = Math.min(columns ?? (metadata.length), 12) as keyof typeof gridColsVariants;
const numberOfColumns = Math.min(columns ?? (metadata.length), 13) as keyof typeof gridColsVariants;

const colSpanVariants = {
1: 'md:col-span-1',
Expand All @@ -82,7 +84,8 @@ export function Table<T>({metadata, data, columns, initialSortBy, onRowClick, it
9: 'md:col-span-9',
10: 'md:col-span-10',
11: 'md:col-span-11',
12: 'md:col-span-12'
12: 'md:col-span-12',
13: 'md:col-span-13'
};

return (
Expand All @@ -103,7 +106,7 @@ export function Table<T>({metadata, data, columns, initialSortBy, onRowClick, it
className || ''
)}
>
<p className={'text-xs font-bold text-neutral-400'}>
<p className={'text-xs text-neutral-500'}>
{label}
</p>
{sortable && sortedBy === key && <IconChevronPlain className={`yearn--sort-chevron ${order === 'asc' ? 'rotate-180' : ''}`} />}
Expand All @@ -112,6 +115,25 @@ export function Table<T>({metadata, data, columns, initialSortBy, onRowClick, it
))}
</div>

{currentItems.length === 0 && isLoading ?
(
<div className={'flex h-96 w-full flex-col items-center justify-center px-10 py-2'}>
<b className={'text-lg'}>{'Fetching gauge data'}</b>
<p className={'text-neutral-600'}>{'We are retrieving the gauges. Please wait.'}</p>
<div className={'flex h-10 items-center justify-center'}>
<span className={'loader'} />
</div>
</div>
) : currentItems.length === 0 && !isLoading ?
(
<div className={'flex h-96 w-full flex-col items-center justify-center px-10 py-2'}>
<b className={'text-lg'}>{'No Gauges'}</b>
<p className={'text-neutral-600'}>
{'No gauges available.'}
</p>
</div>
) : null
}
{currentItems.map((item, rowIndex): ReactElement => (
<div
key={`row_${rowIndex}`}
Expand All @@ -122,8 +144,14 @@ export function Table<T>({metadata, data, columns, initialSortBy, onRowClick, it
)}
onClick={(): void => onRowClick?.(item)}
>
{metadata.map(({key, label, className, fullWidth, columnSpan, format, transform}): ReactElement => {
const isNumber = !isNaN(item[key] as number);
{metadata.map(({key, label, className, fullWidth, columnSpan, format, transform, isDisabled}): ReactElement => {
let isNumberLike = false;
if (typeof item[key] === 'bigint') {
isNumberLike = true;
} else {
isNumberLike = !isNaN(Number(item[key]));
}
const isNumber = isNumberLike;

return (
<div
Expand All @@ -137,7 +165,9 @@ export function Table<T>({metadata, data, columns, initialSortBy, onRowClick, it
{!fullWidth && <label className={'inline text-start text-sm text-neutral-500 md:hidden'}>{label}</label>}
<div
className={cl(
isZero(item[key] as number) ? 'text-neutral-400' : 'text-neutral-900',
(
isDisabled && isDisabled?.(item)
) ? 'text-neutral-400' : 'text-neutral-900',
isNumber ? 'font-number' : 'font-aeonik',
fullWidth ? 'w-full' : undefined
)}>
Expand Down
3 changes: 2 additions & 1 deletion apps/common/contexts/useWallet.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {createContext, memo, useCallback, useContext, useEffect, useMemo, useState} from 'react';
import {useChainId} from 'wagmi';
import {OPT_YVSTERN_ERN_STAKING_CONTRACT, OPT_YVAGEUR_USDC_STAKING_CONTRACT, OPT_YVALETH_FRXETH_STAKING_CONTRACT, OPT_YVALETH_WETH_STAKING_CONTRACT, OPT_YVALUSD_FRAX_STAKING_CONTRACT, OPT_YVALUSD_USDC_STAKING_CONTRACT, OPT_YVDAI_STAKING_CONTRACT, OPT_YVDOLA_USDC_STAKING_CONTRACT, OPT_YVDOLAUSDC_STAKING_CONTRACT, OPT_YVERN_DOLA_STAKING_CONTRACT, OPT_YVERN_LUSD_STAKING_CONTRACT, OPT_YVETH_STAKING_CONTRACT, OPT_YVEXA_WETH_STAKING_CONTRACT, OPT_YVFRAX_DOLA_STAKING_CONTRACT, OPT_YVIB_WETH_STAKING_CONTRACT, OPT_YVLDO_WSTETH_STAKING_CONTRACT, OPT_YVLUSD_WETH_STAKING_CONTRACT, OPT_YVMAI_ALUSD_STAKING_CONTRACT, OPT_YVMAI_DOLA_STAKING_CONTRACT, OPT_YVMAI_STAKING_CONTRACT, OPT_YVMAI_USDC_STAKING_CONTRACT, OPT_YVMAIUSDC_STAKING_CONTRACT, OPT_YVMIM_USDC_STAKING_CONTRACT, OPT_YVMTA_USDC_STAKING_CONTRACT, OPT_YVOP_USDC_STAKING_CONTRACT, OPT_YVOP_VELO_STAKING_CONTRACT, OPT_YVOP_WETH_STAKING_CONTRACT, OPT_YVSNX_USDC_STAKING_CONTRACT, OPT_YVSUSCUSDC_STAKING_CONTRACT, OPT_YVTBTC_WBTC_STAKING_CONTRACT, OPT_YVTBTC_WETH_STAKING_CONTRACT, OPT_YVUSDC_STAKING_CONTRACT, OPT_YVUSDT_STAKING_CONTRACT, OPT_YVVELO_USDC_STAKING_CONTRACT, OPT_YVWUSDR_USDC_STAKING_CONTRACT, OPT_YVWUSDRV2_USDC_STAKING_CONTRACT, STACKING_TO_VAULT} from '@vaults/constants/optRewards';
import {OPT_YVAGEUR_USDC_STAKING_CONTRACT, OPT_YVALETH_FRXETH_STAKING_CONTRACT, OPT_YVALETH_WETH_STAKING_CONTRACT, OPT_YVALUSD_FRAX_STAKING_CONTRACT, OPT_YVALUSD_USDC_STAKING_CONTRACT, OPT_YVDAI_STAKING_CONTRACT, OPT_YVDOLA_USDC_STAKING_CONTRACT, OPT_YVDOLAUSDC_STAKING_CONTRACT, OPT_YVERN_DOLA_STAKING_CONTRACT, OPT_YVERN_LUSD_STAKING_CONTRACT, OPT_YVETH_STAKING_CONTRACT, OPT_YVEXA_WETH_STAKING_CONTRACT, OPT_YVFRAX_DOLA_STAKING_CONTRACT, OPT_YVIB_WETH_STAKING_CONTRACT, OPT_YVLDO_WSTETH_STAKING_CONTRACT, OPT_YVLUSD_WETH_STAKING_CONTRACT, OPT_YVMAI_ALUSD_STAKING_CONTRACT, OPT_YVMAI_DOLA_STAKING_CONTRACT, OPT_YVMAI_STAKING_CONTRACT, OPT_YVMAI_USDC_STAKING_CONTRACT, OPT_YVMAIUSDC_STAKING_CONTRACT, OPT_YVMIM_USDC_STAKING_CONTRACT, OPT_YVMTA_USDC_STAKING_CONTRACT, OPT_YVOP_USDC_STAKING_CONTRACT, OPT_YVOP_VELO_STAKING_CONTRACT, OPT_YVOP_WETH_STAKING_CONTRACT, OPT_YVSNX_USDC_STAKING_CONTRACT, OPT_YVSTERN_ERN_STAKING_CONTRACT, OPT_YVSUSCUSDC_STAKING_CONTRACT, OPT_YVTBTC_WBTC_STAKING_CONTRACT, OPT_YVTBTC_WETH_STAKING_CONTRACT, OPT_YVUSDC_STAKING_CONTRACT, OPT_YVUSDT_STAKING_CONTRACT, OPT_YVVELO_USDC_STAKING_CONTRACT, OPT_YVWUSDR_USDC_STAKING_CONTRACT, OPT_YVWUSDRV2_USDC_STAKING_CONTRACT, STACKING_TO_VAULT} from '@vaults/constants/optRewards';
import {useUI} from '@yearn-finance/web-lib/contexts/useUI';
import {useBalances} from '@yearn-finance/web-lib/hooks/useBalances';
import {toAddress} from '@yearn-finance/web-lib/utils/address';
Expand Down Expand Up @@ -214,6 +214,7 @@ export const WalletContextApp = memo(function WalletContextApp({children}: {chil
refresh: onRefresh
}), [balances, cumulatedValueInVaults, isLoading, onRefresh, nonce]);


return (
<WalletContext.Provider value={contextValue}>
{children}
Expand Down
19 changes: 19 additions & 0 deletions apps/common/hooks/useAsyncEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-disable react-hooks/exhaustive-deps */

import {type DependencyList, useCallback, useEffect} from 'react';

//Should be useAsyncEffect, but exhaustive-deps is messing with this.
function useAsyncTrigger(effect: () => Promise<void>, deps: DependencyList): () => Promise<void> {
const asyncEffectInCallback = useCallback(async (): Promise<void> => {
effect();
}, [...deps]);

useEffect((): void => {
asyncEffectInCallback();
}, [asyncEffectInCallback]);

return asyncEffectInCallback;
}


export {useAsyncTrigger};
1 change: 0 additions & 1 deletion apps/common/hooks/useTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ type TProps = {
endTime?: TSeconds;
}

// TODO Check if we can use `getTimeUntil` from the web lib for this computation
export function computeTimeLeft({endTime}: {endTime?: TSeconds}): number {
if (!endTime) {
return 0;
Expand Down
6 changes: 3 additions & 3 deletions apps/common/hooks/useTokenPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import type {TAddress} from '@yearn-finance/web-lib/types';
export function useTokenPrice(address: TAddress): number {
const {prices} = useYearn();

const tokenPrice = useMemo((): number => (
formatToNormalizedValue(toBigInt(prices?.[address] || 0), 6)
), [address, prices]);
const tokenPrice = useMemo((): number => {
return formatToNormalizedValue(toBigInt(prices?.[address] || 0), 6);
}, [address, prices]);

return tokenPrice;
}
2 changes: 1 addition & 1 deletion apps/common/schemas/yDaemonVaultsSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const yDaemonVaultTokenSchema = z.object({

export const yDaemonVaultSchema = z.object({
address: addressSchema,
type: z.literal('Automated').or(z.literal('Standard').or(z.literal('Experimental'))),
type: z.literal('Automated').or(z.literal('Standard').or(z.literal('Experimental').or(z.literal('Automated Yearn Vault').or(z.literal('Yearn Vault'))))),
symbol: z.string(),
display_symbol: z.string(),
formated_symbol: z.string(),
Expand Down
29 changes: 29 additions & 0 deletions apps/common/utils/QueryParamsProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {useMemo} from 'react';
import {usePathname, useRouter, useSearchParams} from 'next/navigation';

import type {ReactElement} from 'react';
import type {PartialLocation, QueryParamAdapterComponent} from 'use-query-params';

export const NextQueryParamAdapter: QueryParamAdapterComponent = ({children}): ReactElement | null => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();

const adapter = useMemo((): any => {
return {
replace(location: PartialLocation): void {
router.replace(pathname + location.search);
},
push(location: PartialLocation): void {
router.push(pathname + location.search);
},
get location(): {search: string} {
return {
search: searchParams.toString()
};
}
};
}, [router, pathname, searchParams]);

return children(adapter);
};
2 changes: 2 additions & 0 deletions apps/common/utils/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ export async function isApprovedERC20(
******************************************************************************/
type TAllowanceOf = {
connector: Connector | undefined,
chainID: number,
tokenAddress: TAddress,
spenderAddress: TAddress
}
export async function allowanceOf(props: TAllowanceOf): Promise<bigint> {
const wagmiProvider = await toWagmiProvider(props.connector);
const result = await readContract({
...wagmiProvider,
chainId: props.chainID,
abi: erc20ABI,
address: props.tokenAddress,
functionName: 'allowance',
Expand Down
Loading

1 comment on commit 8d32478

@vercel
Copy link

@vercel vercel bot commented on 8d32478 Oct 18, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.