import { ThreatClient, Chain } from '@webacy-xyz/sdk';
const client = new ThreatClient({
apiKey: process.env.WEBACY_API_KEY,
defaultChain: Chain.ETH,
});
// Types
type RiskLevel = 'safe' | 'warning' | 'danger' | 'blocked';
interface UrlCheckResult {
allowed: boolean;
riskLevel: RiskLevel;
message?: string;
categories?: string[];
}
interface TransactionCheckResult {
allowed: boolean;
riskLevel: RiskLevel;
preview: {
assetChanges: Array<{
type: string;
symbol: string;
amount: string;
}>;
warnings: string[];
};
requiresConfirmation: boolean;
}
interface ConnectionInfo {
domain: string;
riskScore: number;
riskLevel: RiskLevel;
connectedAt: Date;
lastTransaction?: Date;
}
// URL Security
async function checkUrlSafety(url: string): Promise<UrlCheckResult> {
const result = await client.url.check(url);
// Immediate block
if (result.isPhishing) {
return {
allowed: false,
riskLevel: 'blocked',
message: 'This site has been identified as a phishing site. Do not connect your wallet.',
categories: result.categories,
};
}
if (result.isMalware) {
return {
allowed: false,
riskLevel: 'blocked',
message: 'This site distributes malware. Navigation blocked for your safety.',
categories: result.categories,
};
}
// High risk warning
if (result.riskScore > 70) {
return {
allowed: true,
riskLevel: 'danger',
message: 'This site has a high risk score. Proceed with extreme caution.',
categories: result.categories,
};
}
// Medium risk caution
if (result.riskScore > 40) {
return {
allowed: true,
riskLevel: 'warning',
message: 'Exercise caution when interacting with this site.',
};
}
return {
allowed: true,
riskLevel: 'safe',
};
}
// Transaction Security
async function checkTransaction(
userAddress: string,
txData: { to: string; data: string; value: string },
chainId: number,
dappDomain: string
): Promise<TransactionCheckResult> {
// First, verify the contract
const chain = chainIdToChain(chainId);
const contractAnalysis = await client.addresses.analyze(txData.to, { chain });
// Check for known drainers
const isDrainer = contractAnalysis.issues?.some(i =>
i.tag.toLowerCase().includes('drainer')
);
if (isDrainer) {
return {
allowed: false,
riskLevel: 'blocked',
preview: {
assetChanges: [],
warnings: ['This contract has been identified as a wallet drainer.'],
},
requiresConfirmation: false,
};
}
// Simulate the transaction
const simulation = await client.scan.scanTransaction(userAddress, {
tx: {
from: userAddress,
to: txData.to,
data: txData.data,
value: txData.value,
},
chain: chainId,
domain: dappDomain,
});
// Determine risk level
let riskLevel: RiskLevel = 'safe';
if (simulation.riskLevel === 'critical') {
riskLevel = 'blocked';
} else if (simulation.riskLevel === 'high') {
riskLevel = 'danger';
} else if (simulation.riskLevel === 'medium') {
riskLevel = 'warning';
}
return {
allowed: riskLevel !== 'blocked',
riskLevel,
preview: {
assetChanges: simulation.assetChanges ?? [],
warnings: simulation.warnings ?? [],
},
requiresConfirmation: riskLevel === 'danger' || riskLevel === 'warning',
};
}
// Connection Management
class ConnectionManager {
private connections: Map<string, ConnectionInfo> = new Map();
async connect(domain: string): Promise<{
success: boolean;
riskLevel: RiskLevel;
message?: string;
}> {
const urlCheck = await checkUrlSafety(`https://${domain}`);
if (!urlCheck.allowed) {
return {
success: false,
riskLevel: urlCheck.riskLevel,
message: urlCheck.message,
};
}
const result = await client.url.check(`https://${domain}`);
this.connections.set(domain, {
domain,
riskScore: result.riskScore,
riskLevel: urlCheck.riskLevel,
connectedAt: new Date(),
});
return {
success: true,
riskLevel: urlCheck.riskLevel,
message: urlCheck.message,
};
}
disconnect(domain: string): void {
this.connections.delete(domain);
}
getConnections(): ConnectionInfo[] {
return Array.from(this.connections.values());
}
async refreshRiskScores(): Promise<ConnectionInfo[]> {
const flagged: ConnectionInfo[] = [];
for (const [domain, info] of this.connections) {
const result = await client.url.check(`https://${domain}`);
info.riskScore = result.riskScore;
if (result.isPhishing || result.isMalware) {
info.riskLevel = 'blocked';
flagged.push(info);
} else if (result.riskScore > 70) {
info.riskLevel = 'danger';
flagged.push(info);
} else if (result.riskScore > 40) {
info.riskLevel = 'warning';
} else {
info.riskLevel = 'safe';
}
}
return flagged;
}
}
// Browser Security Controller
class DappBrowserSecurity {
private connectionManager = new ConnectionManager();
// Call when user navigates to a new URL
async onNavigate(url: string): Promise<UrlCheckResult> {
return checkUrlSafety(url);
}
// Call when dApp requests wallet connection
async onConnectionRequest(domain: string) {
return this.connectionManager.connect(domain);
}
// Call when dApp requests transaction signature
async onTransactionRequest(
userAddress: string,
txData: { to: string; data: string; value: string },
chainId: number,
dappDomain: string
) {
return checkTransaction(userAddress, txData, chainId, dappDomain);
}
// Call periodically to check connected dApps
async refreshConnections() {
return this.connectionManager.refreshRiskScores();
}
// Get all active connections
getActiveConnections() {
return this.connectionManager.getConnections();
}
// Disconnect a dApp
disconnect(domain: string) {
this.connectionManager.disconnect(domain);
}
}
// Helper function
function chainIdToChain(chainId: number): Chain {
const mapping: Record<number, Chain> = {
1: Chain.ETH,
56: Chain.BSC,
137: Chain.POL,
10: Chain.OPT,
42161: Chain.ARB,
8453: Chain.BASE,
};
return mapping[chainId] ?? Chain.ETH;
}
// Export the controller
export const browserSecurity = new DappBrowserSecurity();