Skip to content

Commit

Permalink
feat: risk
Browse files Browse the repository at this point in the history
  • Loading branch information
Majorfi committed Aug 28, 2024
1 parent 924d82a commit 11e6a62
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 22 deletions.
29 changes: 18 additions & 11 deletions apps/vaults-v3/components/details/VaultDetailsTabsWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {IconLinkOut} from '@yearn-finance/web-lib/icons/IconLinkOut';
import {getNetwork} from '@yearn-finance/web-lib/utils/wagmi/utils';
import {IconChevron} from '@common/icons/IconChevron';

import {VaultRiskInfo} from './tabs/VaultRiskInfo';

import type {ReactElement} from 'react';
import type {TYDaemonVault} from '@yearn-finance/web-lib/utils/schemas/yDaemonVaultsSchemas';

Expand All @@ -25,6 +27,7 @@ type TTabsOptions = {
};
type TTabs = {
hasStrategies: boolean;
hasRisk: boolean;
selectedAboutTabIndex: number;
set_selectedAboutTabIndex: (arg0: number) => void;
};
Expand All @@ -34,22 +37,21 @@ type TExplorerLinkProps = {
currentVaultAddress: string;
};

function Tabs({hasStrategies, selectedAboutTabIndex, set_selectedAboutTabIndex}: TTabs): ReactElement {
function Tabs({hasStrategies, hasRisk, selectedAboutTabIndex, set_selectedAboutTabIndex}: TTabs): ReactElement {
const router = useRouter();

const tabs: TTabsOptions[] = useMemo((): TTabsOptions[] => {
const tabs = [{value: 0, label: 'About', slug: 'about'}];
if (hasStrategies) {
return [
{value: 0, label: 'About', slug: 'about'},
{value: 1, label: 'Vaults', slug: 'vaults'},
{value: 2, label: 'Info', slug: 'info'}
];
tabs.push({value: 1, label: 'Vaults', slug: 'vaults'});
}
tabs.push({value: 2, label: 'Info', slug: 'info'});
if (hasRisk) {
tabs.push({value: 3, label: 'Risk', slug: 'risk'});
}
return [
{value: 0, label: 'About', slug: 'about'},
{value: 2, label: 'Info', slug: 'info'}
];
}, [hasStrategies]);

return tabs;
}, [hasStrategies, hasRisk]);

useEffect((): void => {
const tab = tabs.find((tab): boolean => tab.slug === router.query.tab);
Expand Down Expand Up @@ -212,6 +214,7 @@ export function VaultDetailsTabsWrapper({currentVault}: {currentVault: TYDaemonV
<div className={'relative flex w-full flex-row items-center justify-between px-4 pt-4 md:px-8'}>
<Tabs
hasStrategies={hasStrategies}
hasRisk={true}
selectedAboutTabIndex={selectedAboutTabIndex}
set_selectedAboutTabIndex={set_selectedAboutTabIndex}
/>
Expand All @@ -238,6 +241,10 @@ export function VaultDetailsTabsWrapper({currentVault}: {currentVault: TYDaemonV
<Renderable shouldRender={currentVault && selectedAboutTabIndex === 2}>
<VaultInfo currentVault={currentVault} />
</Renderable>

<Renderable shouldRender={currentVault && selectedAboutTabIndex === 3}>
<VaultRiskInfo currentVault={currentVault} />
</Renderable>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {parseMarkdown} from '@yearn-finance/web-lib/utils/helpers';
import type {ReactElement} from 'react';
import type {TYDaemonVault} from '@yearn-finance/web-lib/utils/schemas/yDaemonVaultsSchemas';

function YearnFeesLineItem({children, label, tooltip}: any): ReactElement {
export function YearnFeesLineItem({children, label, tooltip}: any): ReactElement {
return (
<div className={'flex flex-col space-y-0 md:space-y-2'}>
<p className={'text-xxs text-neutral-600 md:text-xs'}>{label}</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export function VaultDetailsStrategies({currentVault}: {currentVault: TYDaemonVa
}}
items={[
{label: 'Vault', value: 'name', sortable: true, className: 'col-span-2'},
{label: 'Score', value: 'score', sortable: true, className: 'col-span-1'},
{label: 'Risk Level', value: 'score', sortable: true, className: 'col-span-1'},
{label: 'Est. APR', value: 'estAPR', sortable: true, className: 'col-span-2'},
{label: 'Hist. APR', value: 'apr', sortable: true, className: 'col-span-2'},
{label: 'Available', value: 'available', sortable: true, className: 'col-span-2'},
Expand Down
175 changes: 175 additions & 0 deletions apps/vaults-v3/components/details/tabs/VaultRiskInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import {type ReactElement, useMemo} from 'react';
import {cl} from '@builtbymom/web3/utils';

import type {TYDaemonVault} from '@yearn-finance/web-lib/utils/schemas/yDaemonVaultsSchemas';

export function VaultRiskInfo({currentVault}: {currentVault: TYDaemonVault}): ReactElement {
const hasRiskScore = useMemo(() => {
let sum = 0;
currentVault.info.riskScore?.forEach(score => {
sum += score;
});
return sum;
}, [currentVault.info.riskScore]);

return (
<div className={'grid grid-cols-1 gap-4 p-4 md:grid-cols-12 md:gap-10 md:p-8'}>
<div className={'col-span-12 mt-6 w-full md:mt-0'}>
<div className={'mb-4 md:mb-10'}>
<div
className={cl(
'grid w-full grid-cols-12 items-center gap-6',
hasRiskScore ? 'border-b border-neutral-900/20 pb-10 mb-10' : ''
)}>
<div className={'col-span-10'}>
<b className={'block text-neutral-900'}>{'Risk Level'}</b>
<small className={'mt-1 block w-3/4 text-xs text-neutral-900/40'}>
{
"This is an indicator of the security of the vault, calculated based on multiple factors including the strategy's complexity, exposure to potential losses, and reliance on external protocols. A score of 1 represents the highest security, while 5 indicates the lowest."
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<span>
<b className={'font-number text-xl text-neutral-900'}>{currentVault.info.riskLevel}</b>
<span className={'text-neutral-900/40'}>{' / 5'}</span>
</span>
</div>
</div>

<div className={cl('grid w-full grid-cols-12 items-center gap-6', hasRiskScore ? '' : 'hidden')}>
<div className={'col-span-10'}>
<p>{'Review'}</p>
<small className={'text-xs text-neutral-900/40'}>
{'The risk review score from Yearn security. 1 is for high trust to 5 low trust'}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[0]}</p>
</div>

<div className={'col-span-10'}>
<p>{'Testing'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
'The testing coverage of the strategy being evaluated. 5 -> 80% or less; 1 -> 100% or higher'
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[1]}</p>
</div>

<div className={'col-span-10'}>
<p>{'Complexity'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
'The sLOC count of the strategy being evaluated. 5 -> 750+ sLOC; 4 -> 450-600 sLOC; 3 -> 300-450 sLOC; 2 -> 150-300 sLOC; 1 -> 0-150 sLOC'
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[2]}</p>
</div>

<div className={'col-span-10'}>
<p>{'Risk Exposure'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
'This score aims to find out how much and how often a strategy can be subject to losses. 5 -> Loss of funds or non recoverable funds up to 70-100% (Example, Leveraging cross assets and got liquidated, adding liquidity to volatile pairs single sided); 4 -> Loss of funds or non recoverable funds up to 15-70% (Example, adding liquidity to single sided curve stable pools); 3 -> Loss of funds or non recoverable funds up to 10-15% (Example, Protocol specific IL exposure, very high deposit/withdrawal fees); 2 -> Loss of funds or non recoverable funds up to 0-10% (Example, deposit/withdrawal fees or anything protocol specific); 1 -> Strategy has no lossable cases, only gains, up only.'
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[3]}</p>
</div>

<div className={'col-span-10'}>
<p>{'Protocol Integration'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
'The protocols that are integrated into the strategy that is being evaluated. 5 -> Strategy interacts with 5 external protocols; 4 -> Strategy interacts with 4 external protocols; 3 -> Strategy interacts with 3 external protocols; 2 -> Strategy interacts with 2 external protocols; 1 -> Strategy interacts with 1 external protocol'
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[4]}</p>
</div>

<div className={'col-span-10'}>
<p>{'Centralization Risk'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
'The centralization score of the strategy that is being evaluated. 5 -> Strategy heavily relies on off-chain management, potentially exposing user funds to rug possibilities by admins; 4 -> Strategy frequently depends on off-chain management but has safeguards against rug possibilities by admins; 3 -> Strategy involves privileged roles but less frequently and with less risk of rug possibilities; 2 -> Strategy has privileged roles but they are not vital for operations and pose minimal risk of rug possibilities; 1 -> Strategy operates without dependency on any privileged roles, ensuring full permissionlessness.'
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[5]}</p>
</div>

<div className={'col-span-10'}>
<p>{'External Protocol Audit'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
'The public audits count of the external protocols. 5 -> No audit conducted by a trusted firm or security researcher; 4 -> Audit conducted by 1 trusted firm or security researcher conducted; 3 -> Audit conducted by 2 trusted firm or security researcher conducted; 2 -> Audit conducted by 3 trusted firm or security researcher conducted; 1 -> Audit conducted by 4 or more trusted firm or security researcher conducted'
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[6]}</p>
</div>

<div className={'col-span-10'}>
<p>{'External Protocol Centralisation'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
"Measurement of the centralization score of the external protocols. 5 -> Contracts owner is an EOA or a multisig with less than 4 members OR Contracts are not verified OR Contracts owner can harm our strategy completely; 4 -> Contracts owner is a multisig but the addresses are not known/hidden OR Contracts owner can harm our strategy by setting parameters in external protocol contracts up to some degree; 3 -> Contracts owner is a multisig with known people but multisig threshold is very low; 2 -> Contracts owner is a multisig with known trusted people; 1 -> Contracts owner is a multisig with known trusted people with Timelock OR Contracts are governanceless, immutable OR Contracts owner can't do any harm to our strategy by setting parameters in external protocol contracts"
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[7]}</p>
</div>

<div className={'col-span-10'}>
<p>{'External Protocol TVL'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
'The active TVL that the external protocol holds. 5 -> TVL of $10M or less; 4 -> TVL between $10M and $40M;3 -> TVL between $40M and $120M; 2 -> TVL; between $120M and $480M; 1 -> TVL of $480M or more'
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[8]}</p>
</div>

<div className={'col-span-10'}>
<p>{'External Protocol Longevity'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
'How long the external protocol contracts in scope have been deployed alive. 5 -> Less than 6 months; 4 -> Between 6 and 12 months; 3 -> Between 12 and 18 months; 2 -> Between 18 and 24 months; 1 -> 24 months or more'
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[9]}</p>
</div>

<div className={'col-span-10'}>
<p>{'External Protocol Type'}</p>
<small className={'text-xs text-neutral-900/40'}>
{
"This is a rough estimate of evaluating a protocol's purpose. 5 -> The main expertise of the protocol lies in off-chain operations, such as RWA protocols; 4 -> Cross-chain applications, like cross-chain bridges, cross-chain yield aggregators, and cross-chain lending/borrowing protocols; 3 -> AMM lending/borrowing protocols that are not forks of blue-chip protocols, leveraged farming protocols, as well as newly conceptualized protocols; 2 -> Slightly modified forked blue-chip protocols; 1 -> Blue-chip protocols such as AAVE, Compound, Uniswap, Curve, Convex, and Balancer."
}
</small>
</div>
<div className={'col-span-2 flex items-center justify-center font-bold'}>
<p>{currentVault.info.riskScore[10]}</p>
</div>
</div>
</div>
</div>
</div>
);
}
27 changes: 20 additions & 7 deletions apps/vaults-v3/components/list/VaultsV3ListRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import type {TNormalizedBN} from '@builtbymom/web3/types';
function APRSubline({hasPendleArbRewards}: {hasPendleArbRewards: boolean}): ReactElement {
if (hasPendleArbRewards) {
return (
<small className={cl('whitespace-nowrap text-xs text-neutral-800 self-end -mb-4')}>
{`+ 2500 ARB per week 🚀`}
<small className={cl('whitespace-nowrap text-xs text-neutral-800 self-end -mb-5 pt-1 -mr-1')}>
{`+ 2500 ARB/week 🚀`}
</small>
);
}
Expand Down Expand Up @@ -449,12 +449,12 @@ function VaultRiskScoreTag({riskLevel}: {riskLevel: number}): ReactElement {
const level = riskLevel < 0 ? 0 : riskLevel > 5 ? 5 : riskLevel;
const riskColor = [`transparent`, `#63C532`, `#F8A908`, `#F8A908`, `#C73203`, `#C73203`];
return (
<div className={'col-span-2 flex flex-row justify-center md:col-span-1 md:flex-col'}>
<div className={'col-span-2 flex flex-row justify-center md:col-span-2 md:flex-col'}>
<p className={'inline whitespace-nowrap text-start text-xs text-neutral-800/60 md:hidden'}>
{'Risk Score'}
</p>
<div className={'flex w-full items-center justify-end gap-4 md:justify-center'}>
<div className={'mt-[6px] h-3 w-6 min-w-6 rounded-sm border-2 border-good-ol-grey-700 p-[2px]'}>
<div className={cl('flex w-fit items-center justify-end gap-4 md:justify-center', 'tooltip relative z-50')}>
<div className={'mt-[6px] h-3 w-10 min-w-10 rounded-sm border-2 border-neutral-400 p-[2px]'}>
<div
className={'h-1 rounded-[1px]'}
style={{
Expand All @@ -463,7 +463,20 @@ function VaultRiskScoreTag({riskLevel}: {riskLevel: number}): ReactElement {
}}
/>
</div>
<p className={'border-b border-dashed font-mono text-base text-white'}>{level}</p>
<span
suppressHydrationWarning
className={'tooltiptext top-full mt-1'}
style={{marginRight: 'calc(-94px + 50%)'}}>
<div
className={
'font-number relative border border-neutral-300 bg-neutral-100 p-1 px-2 text-center text-xxs text-neutral-900'
}>
<p>
<b className={'text-xs font-semibold'}>{`${level} / 5 :`}</b>
{` This reflects the vault's security, with 1 being most secure and 5 least secure, based on strategy complexity, loss exposure, and external dependencies.`}
</p>
</div>
</span>
</div>
</div>
);
Expand Down Expand Up @@ -595,7 +608,7 @@ export function VaultsV3ListRow({currentVault}: {currentVault: TYDaemonVault}):

<div className={'col-span-1'} />

<div className={cl('col-span-7 z-10', 'grid grid-cols-2 md:grid-cols-11 gap-1', 'mt-4 md:mt-0')}>
<div className={cl('col-span-7 z-10', 'grid grid-cols-2 md:grid-cols-12 gap-4', 'mt-4 md:mt-0')}>
<VaultRiskScoreTag riskLevel={currentVault.info.riskLevel} />

<div
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@tailwindcss/typography": "^0.5.13",
"@tanstack/react-query": "^5.51.11",
"@wagmi/core": "^2.11.7",
"@yearn-finance/web-lib": "^4.0.10",
"@yearn-finance/web-lib": "^4.1.1",
"axios": "^1.7.2",
"ethers": "5.7.2",
"framer-motion": "^11.2.10",
Expand Down
7 changes: 6 additions & 1 deletion pages/v3/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,12 @@ function ListOfVaults(): ReactElement {
}}
items={[
{label: 'Vault', value: 'name', sortable: true, className: 'col-span-2'},
{label: 'Score', value: 'score', sortable: true, className: 'col-span-1'},
{
label: 'Risk Level',
value: 'score',
sortable: true,
className: 'col-span-1 whitespace-nowrap'
},
{label: 'Est. APR', value: 'estAPR', sortable: true, className: 'col-span-2'},
{label: 'Hist. APR', value: 'apr', sortable: true, className: 'col-span-2'},
{label: 'Available', value: 'available', sortable: true, className: 'col-span-2'},
Expand Down

0 comments on commit 11e6a62

Please sign in to comment.