Skip to content

Commit

Permalink
feat: add two new risk factors
Browse files Browse the repository at this point in the history
  • Loading branch information
rkalis committed Jan 21, 2025
1 parent 67d4cb2 commit e488b46
Show file tree
Hide file tree
Showing 9 changed files with 44 additions and 13 deletions.
17 changes: 15 additions & 2 deletions components/allowances/dashboard/cells/SpenderCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import Href from 'components/common/Href';
import Loader from 'components/common/Loader';
import WithHoverTooltip from 'components/common/WithHoverTooltip';
import { isNullish } from 'lib/utils';
import type { TokenAllowanceData } from 'lib/utils/allowances';
import { AllowanceType, type TokenAllowanceData } from 'lib/utils/allowances';
import { getChainExplorerUrl } from 'lib/utils/chains';
import { shortenAddress } from 'lib/utils/formatting';
import { YEAR } from 'lib/utils/time';
import { getSpenderData } from 'lib/utils/whois';
import { useMemo } from 'react';
import RiskTooltip from '../wallet-health/RiskTooltip';

interface Props {
Expand All @@ -24,6 +26,17 @@ const SpenderCell = ({ allowance }: Props) => {
enabled: !isNullish(allowance.payload?.spender),
});

// Add non-spender-specific risk factors (TODO: set up a proper system for this)
const riskFactors = useMemo(() => {
const factors = spenderData?.riskFactors ?? [];

if (allowance.payload?.type === AllowanceType.PERMIT2 && allowance.payload.expiration > Date.now() + 1 * YEAR) {
return [...factors, { type: 'excessive_expiration', source: 'onchain' }];
}

return factors;
}, [allowance.payload, spenderData?.riskFactors]);

const explorerUrl = `${getChainExplorerUrl(allowance.chainId)}/address/${allowance.payload?.spender}`;

if (!allowance.payload) return null;
Expand All @@ -44,7 +57,7 @@ const SpenderCell = ({ allowance }: Props) => {
</WithHoverTooltip>
</div>
<CopyButton content={allowance.payload.spender} className="w-4 h-4 text-zinc-500 dark:text-zinc-400" />
<RiskTooltip riskData={spenderData} />
<RiskTooltip riskFactors={riskFactors} />
</div>
</Loader>
);
Expand Down
14 changes: 7 additions & 7 deletions components/allowances/dashboard/wallet-health/RiskTooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import WithHoverTooltip from 'components/common/WithHoverTooltip';
import type { Nullable, SpenderRiskData } from 'lib/interfaces';
import type { RiskFactor } from 'lib/interfaces';
import { filterUnknownRiskFactors, getRiskLevel } from 'lib/utils/risk';
import { useTranslations } from 'next-intl';
import { twMerge } from 'tailwind-merge';
import RiskFactorDisplay from './RiskFactorDisplay';

interface Props {
riskData?: Nullable<SpenderRiskData>;
riskFactors?: RiskFactor[];
}

const RiskTooltip = ({ riskData }: Props) => {
const RiskTooltip = ({ riskFactors }: Props) => {
const t = useTranslations();

const filteredRiskFactors = filterUnknownRiskFactors(riskData?.riskFactors ?? []);
const filteredRiskFactors = filterUnknownRiskFactors(riskFactors ?? []);
const riskLevel = getRiskLevel(filteredRiskFactors);

// TODO: Properly handle low risk
if (riskLevel === 'unknown' || riskLevel === 'low') return null;

const riskFactors = filteredRiskFactors.map((riskFactor) => (
const riskFactorDisplays = filteredRiskFactors.map((riskFactor) => (
<RiskFactorDisplay key={`${riskFactor.type}-${riskFactor.source}-${riskFactor.data}`} riskFactor={riskFactor} />
));

const riskTooltip = (
<div className="flex flex-col">
{t('address.tooltips.risk_factors', { riskLevel: t(`address.risk_factors.levels.${riskLevel}`) })}
<ul className="my-2">
{riskFactors?.map((riskFactor) => (
<li key={riskFactor.key}>{riskFactor}</li>
{riskFactorDisplays?.map((riskFactorDisplay) => (
<li key={riskFactorDisplay.key}>{riskFactorDisplay}</li>
))}
</ul>
</div>
Expand Down
2 changes: 2 additions & 0 deletions lib/utils/risk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ export const RiskFactorScore: Record<string, number> = {
closed_source: 40,
deprecated: 100,
eoa: 100,
excessive_expiration: 60,
exploit: 100,
phishing_risk: 40,
proxy: 20,
suspicious_address: 60,
unsafe: 40,
uninitialized: 40,
};
Expand Down
6 changes: 6 additions & 0 deletions lib/whois/spender/risk/OnchainSpenderRiskDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class OnchainSpenderRiskDataSource implements SpenderDataSource {
// if (this.isSmallBytecode(bytecode)) riskFactors.push({ type: 'unsafe', source: 'revoke' });
if (this.isOpenSeaProxy(bytecode)) riskFactors.push({ type: 'deprecated', source: 'onchain' });
if (this.hasPhishingRisk(address, bytecode)) riskFactors.push({ type: 'phishing_risk', source: 'onchain' });
if (this.isSuspiciousAddress(address)) riskFactors.push({ type: 'suspicious_address', source: 'onchain' });

const elapsedTime = (new Date().getTime() - time) / 1000;
console.log(elapsedTime, 'Onchain', address);
Expand Down Expand Up @@ -76,6 +77,11 @@ export class OnchainSpenderRiskDataSource implements SpenderDataSource {
return bytecode === OPENSEA_PROXY_BYTECODE;
}

// Legitimate apps can sometimes have 0000 prefixes as an optimisation, but never end with 0000 as well
isSuspiciousAddress(address: Address): boolean {
return address.startsWith('0x0000') && address.endsWith('0000');
}

// TODO: Add checker that gets recent approval / revoke logs and checks if many people are approving / revoking this address
// - First needs the Approval event log normalisation
// - Also needs registration of average block times for every supported chain
Expand Down
2 changes: 2 additions & 0 deletions locales/en/address.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"closed_source": "Closed source",
"deprecated": "Deprecated or outdated",
"eoa": "Externally Owned Account",
"excessive_expiration": "Excessive expiration",
"exploit": "Involved in the {data}",
"levels": {
"high": "high risk",
Expand All @@ -90,6 +91,7 @@
"phishing_risk": "Increases risk surface in case of phishing",
"proxy": "Upgradeable proxy contract",
"source": "reported by <source-link>{source}</source-link>",
"suspicious_address": "Suspicious-looking address",
"uninitialized": "No contract deployed to this address",
"unsafe": "Potentially unsafe contract code"
},
Expand Down
6 changes: 4 additions & 2 deletions locales/es/address.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,22 @@
"expiration": "Caduca {inTime}"
},
"risk_factors": {
"allowlist": "Well-known spender address",
"allowlist": "Dirección conocida del comprador",
"blocklist": "Peligroso o malintencionado",
"closed_source": "Código cerrado",
"deprecated": "Obsoleto o desactualizado",
"eoa": "Cuenta de Propiedad Externa",
"excessive_expiration": "Excessive expiration",
"exploit": "Involucrado en la {data}",
"levels": {
"high": "alto riesgo",
"low": "bajo riesgo",
"medium": "riesgo medio"
},
"phishing_risk": "Aumenta la superficie de riesgo en caso de phishing",
"proxy": "Upgradeable proxy contract",
"proxy": "Contrato de proxy actualizable",
"source": "reportado por <source-link>{source}</source-link>",
"suspicious_address": "Suspicious-looking address",
"uninitialized": "No se ha implementado ningún contrato en esta dirección",
"unsafe": "Código de contrato potencialmente inseguro"
},
Expand Down
6 changes: 4 additions & 2 deletions locales/ja/address.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,22 @@
"expiration": "{inTime}で期限が切れます"
},
"risk_factors": {
"allowlist": "Well-known spender address",
"allowlist": "よく知られている支出者のアドレス",
"blocklist": "危険または悪意のある",
"closed_source": "ソースがクローズになっている",
"deprecated": "非推奨または古い",
"eoa": "外部所有口座",
"excessive_expiration": "Excessive expiration",
"exploit": "{data}に関与していました",
"levels": {
"high": "高リスク",
"low": "低リスク",
"medium": "中リスク"
},
"phishing_risk": "フィッシングの場合のリスク領域が増加",
"proxy": "Upgradeable proxy contract",
"proxy": "アップグレード可能なプロキシ契約",
"source": "<source-link>{source}</source-link>に報告された",
"suspicious_address": "Suspicious-looking address",
"uninitialized": "この住所にはコントラクトがデプロイされていません",
"unsafe": "安全でない可能性のある契約コード"
},
Expand Down
2 changes: 2 additions & 0 deletions locales/ru/address.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"closed_source": "Закрытый исходный код",
"deprecated": "Устаревший или неактуальный",
"eoa": "Внешний аккаунт",
"excessive_expiration": "Excessive expiration",
"exploit": "Участвует в {data}",
"levels": {
"high": "высокий риск",
Expand All @@ -90,6 +91,7 @@
"phishing_risk": "Увеличивает поверхность риска в случае фишинга",
"proxy": "Upgradeable proxy contract",
"source": "сообщил(а)<source-link>{source}</source-link>",
"suspicious_address": "Suspicious-looking address",
"uninitialized": "Контракт по этому адресу не отправлен",
"unsafe": "Потенциально небезопасный код контракта"
},
Expand Down
2 changes: 2 additions & 0 deletions locales/zh/address.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"closed_source": "闭源",
"deprecated": "已弃用或已过时",
"eoa": "外部持有账户",
"excessive_expiration": "Excessive expiration",
"exploit": "涉及 {data}",
"levels": {
"high": "高风险",
Expand All @@ -90,6 +91,7 @@
"phishing_risk": "增加钓鱼风险",
"proxy": "Upgradeable proxy contract",
"source": "举报人:<source-link>{source}</source-link>",
"suspicious_address": "Suspicious-looking address",
"uninitialized": "没有部署到此地址的合约",
"unsafe": "可能不安全的合约代码"
},
Expand Down

0 comments on commit e488b46

Please sign in to comment.