Skip to content

Commit

Permalink
Feat/risk (#590)
Browse files Browse the repository at this point in the history
* WIP: risk score

* feat: risk

* feat: factor copies

* feat: factor copies

* feat: change risk score copy

* chore: risk description update (#588)

* fix: merge conflicts

---------

Co-authored-by: spalen0 <116267321+spalen0@users.noreply.github.com>
  • Loading branch information
Majorfi and spalen0 committed Sep 25, 2024
1 parent 4621c0f commit acbd713
Show file tree
Hide file tree
Showing 14 changed files with 464 additions and 162 deletions.
11 changes: 6 additions & 5 deletions apps/common/components/ListHead.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,9 @@ export function ListHead({
const [chain, token, ...rest] = items;
return (
<div className={'mt-4 grid w-full grid-cols-1 md:mt-0'}>
<div className={cl('yearn--table-head-wrapper', wrapperClassName)}>
<p className={'yearn--table-head-label max-w-[32px]'}>{chain.label}</p>

<div className={cl('yearn--table-head-token-section -ml-4', tokenClassName)}>
<div className={cl('mb-2 hidden w-full px-10 md:grid md:grid-cols-12', wrapperClassName)}>
<div className={cl('col-span-4 flex gap-6', tokenClassName)}>
<p className={'yearn--table-head-label max-w-[32px]'}>{chain.label}</p>
<button
onClick={(): void => onSort(token.value, toggleSortDirection(token.value))}
className={'yearn--table-head-label-wrapper group'}>
Expand All @@ -71,7 +70,9 @@ export function ListHead({
</button>
</div>

<div className={cl('yearn--table-head-data-section', dataClassName)}>
<div />

<div className={cl('col-span-7 grid grid-cols-10 gap-x-7 pl-6', dataClassName)}>
{rest.map(
(item, index): ReactElement => (
<button
Expand Down
2 changes: 2 additions & 0 deletions apps/common/components/RenderAmount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ export function RenderAmount(props: TAmount & {shouldHideTooltip?: boolean; shou
!isZero(props.value) &&
((props.value < 0.001 && props.symbol !== 'percent') || (props.value < 0.0001 && props.symbol === 'percent'));

const value = formatTAmount(props);
return (
<span
title={value}
suppressHydrationWarning
className={cl(
shouldShowTooltip
Expand Down
2 changes: 1 addition & 1 deletion apps/vaults-v3/components/VaultChainTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function VaultChainTag({chainID}: {chainID: number}): ReactElement {
<div className={'w-fit'}>
<div
style={{background: 'linear-gradient(244deg, #7B3FE4 5.89%, #A726C1 94.11%)'}}
className={'rounded-2xl px-3.5 py-1 text-neutral-900'}>
className={'rounded-2xl px-3.5 py-1 text-xs text-neutral-900'}>
{'Polygon PoS'}
</div>
</div>
Expand Down
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,6 +91,7 @@ export function VaultDetailsStrategies({currentVault}: {currentVault: TYDaemonVa
}}
items={[
{label: 'Vault', value: 'name', sortable: true, className: 'col-span-2'},
{label: 'Risk Level', value: 'score', sortable: true, className: 'col-span-1'},
{label: 'Est. APY', value: 'estAPY', sortable: true, className: 'col-span-2'},
{label: 'Hist. APY', value: 'APY', sortable: true, className: 'col-span-2'},
{label: 'Available', value: 'available', sortable: true, className: 'col-span-2'},
Expand Down
177 changes: 177 additions & 0 deletions apps/vaults-v3/components/details/tabs/VaultRiskInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'To have a unified reviewScore for both internal and external strategies, we assume that the strategist writer itself is either a Source of Trust (internal) or not (external). So all internal strategies always includes that 1 additional source of trust in addition. Together with all other Sources of Trust (SoTs) in this list: internal strategist wrote the strategy, peer reviews, expert peer reviews, ySec security reviews and ySec recurring security review. Each item accounts for 1 SoT point, and any combinations of these gives the number of SoTs a strategy has and thus gives the associated review score: \n\t\t1 -> 5 SoT \n\t\t2 -> 4 SoT\n\t\t3 -> 3 SoT\n\t\t4 -> 2 SoT\n\t\t5 -> 1 SoT'
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'The testing coverage of the strategy being evaluated. \n\t\t1 -> 95%+\n\t\t2 -> 90%+\n\t\t3 -> 80%+\n\t\t4 -> 70%+\n\t\t5 -> below 70%'
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'The sLOC count of the strategy being evaluated. \n\t\t1 -> 0-150 sLOC\n\t\t2 -> 150-300 sLOC\n\t\t3 -> 300-450 sLOC\n\t\t4 -> 450-600 sLOC\n\t\t5 -> 750+ 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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'This score aims to find out how much and how often a strategy can be subject to losses. \n\t\t1 -> Strategy has no lossable cases, only gains, up only.\n\t\t2 -> Loss of funds or non recoverable funds up to 0-10% (Example, deposit/withdrawal fees or anything protocol specific)\n\t\t3 -> Loss of funds or non recoverable funds up to 10-15% (Example, Protocol specific IL exposure, very high deposit/withdrawal fees)\n\t\t4 -> Loss of funds or non recoverable funds up to 15-70% (Example, adding liquidity to single sided curve stable pools)\n\t\t5 -> 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)'
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'The protocols that are integrated into the strategy that is being evaluated. \n\t\t1 -> Strategy interacts with 1 external protocol\n\t\t2 -> Strategy interacts with 2 external protocols\n\t\t3 -> Strategy interacts with 3 external protocols\n\t\t4 -> Strategy interacts with 4 external protocols\n\t\t5 -> Strategy interacts with 5 external protocols'
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'The centralization score of the strategy that is being evaluated. \n\t\t1 -> Strategy operates without dependency on any privileged roles, ensuring full permissionlessness.\n\t\t2 -> Strategy has privileged roles but they are not vital for operations and pose minimal risk of rug possibilities\n\t\t3 -> Strategy involves privileged roles but less frequently and with less risk of rug possibilities\n\t\t4 -> Strategy frequently depends on off-chain management but has safeguards against rug possibilities by admins\n\t\t5 -> Strategy heavily relies on off-chain management, potentially exposing user funds to rug possibilities by admins'
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'The public audits count of the external protocols. \n\t\t1 -> Audit conducted by 4 or more trusted firm or security researcher conducted.\n\t\t2 -> Audit conducted by 3 trusted firm or security researcher conducted\n\t\t3 -> Audit conducted by 2 trusted firm or security researcher conducted\n\t\t4 -> Audit conducted by 1 trusted firm or security researcher conducted\n\t\t5 -> No audit conducted by a trusted firm or security researcher'
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
"Measurement of the centralization score of the external protocols. \n\t\t1 -> 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.\n\t\t2 -> Contracts owner is a multisig with known trusted people\n\t\t3 -> Contracts owner is a multisig with known people but multisig threshold is very low\n\t\t4 -> 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\n\t\t5 -> 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"
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'The active TVL that the external protocol holds. \n\t\t1 -> TVL of $480M or more\n\t\t2 -> TVL between $120M and $480M\n\t\t3 -> TVL between $40M and $120M\n\t\t4 -> TVL between $10M and $40M\n\t\t5 -> TVL of $10M or less'
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
'How long the external protocol contracts in scope have been deployed alive. \n\t\t1 -> 24 months or more\n\t\t2 -> Between 18 and 24 months\n\t\t3 -> Between 12 and 18 months\n\t\t4 -> Between 6 and 12 months\n\t\t5 -> Less than 6 months'
}
</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={'whitespace-break-spaces text-xs text-neutral-900/40'}>
{
"This is a rough estimate of evaluating a protocol's purpose. \n\t\t1 -> Blue-chip protocols such as AAVE, Compound, Uniswap, Curve, Convex, and Balancer.\n\t\t2 -> Slightly modified forked blue-chip protocols\n\t\t3 -> AMM lending/borrowing protocols that are not forks of blue-chip protocols, leveraged farming protocols, as well as newly conceptualized protocols\n\t\t4 -> Cross-chain applications, like cross-chain bridges, cross-chain yield aggregators, and cross-chain lending/borrowing protocols\n\t\t5 -> The main expertise of the protocol lies in off-chain operations, such as RWA protocols"
}
</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>
);
}
6 changes: 4 additions & 2 deletions apps/vaults-v3/components/list/VaultsV3ListHead.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function VaultsV3ListHead({items, sortBy, sortDirection, onSort}: TListHe
)}>
<div
className={cl(
'col-span-5',
'col-span-4',
'flex flex-row items-center justify-between',
'mb-2 py-4 md:mb-0 md:py-0'
)}>
Expand All @@ -77,7 +77,9 @@ export function VaultsV3ListHead({items, sortBy, sortDirection, onSort}: TListHe
</button>
</div>

<div className={cl('col-span-7', 'grid grid-cols-1 md:grid-cols-10', 'gap-0 md:gap-x-7')}>
<div />

<div className={cl('col-span-7 z-10', 'grid grid-cols-2 md:grid-cols-11 gap-1', 'mt-4 md:mt-0')}>
{rest.map(
(item, index): ReactElement => (
<button
Expand Down
Loading

0 comments on commit acbd713

Please sign in to comment.