From 1f2719ba38ce61bbbcae29cb033ed7ad95ff81cb Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 26 Jun 2023 13:12:23 +0530 Subject: [PATCH 01/70] add component and styles --- .../prebuild-components/financial-alert.css | 133 ++++++++++++++++++ .../prebuild-components/financial-alert.tsx | 80 +++++++++++ 2 files changed, 213 insertions(+) create mode 100644 chrome-extension/src/prebuild-components/financial-alert.css create mode 100644 chrome-extension/src/prebuild-components/financial-alert.tsx diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css new file mode 100644 index 0000000..7fbfb2f --- /dev/null +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -0,0 +1,133 @@ +* { + box-sizing: border-box; +} + +.container { + --border: 10px; + --icon-size: 70px; + + padding: 25px 18px 10px; + border-radius: 10px; + position: relative; + background: hsl(265, 100%, 92%); + font-family: Roboto; + max-width: 540px; + min-width: 470px; +} + +.container:before { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -1; + background: linear-gradient(to bottom, hsl(265, 100%, 40%), hsl(265, 94%, 19%)); + margin: calc(-1 * var(--border)); + border-radius: inherit; +} + +.basic-info { + display: grid; + grid-template-rows: repeat(2, auto); + grid-template-columns: 1fr auto; + grid-template-areas: + "heading icon" + "info icon"; +} + +.basic-info h2 { + margin: 0 0 16px; + grid-area: heading; + font-size: 25px; +} +.basic-info .icon-container { + grid-area: icon; +} + +.basic-info .icon { + color: hsl(265, 100%, 40%); + width: var(--icon-size); + height: var(--icon-size); +} + +.basic-info p { + grid-area: info; + margin: 0; + font-size: 18px; +} +.basic-info p > span { + display: block; +} + +.financial-info { + display: flex; + flex-direction: row; + gap: 5px; + flex-wrap: nowrap; + margin: 15px 0; +} +.financial-info .item { + background: hsla(263, 77%, 88%, 1); + padding: 10px 0 16px; +} +h4 { + text-align: center; + color: hsl(264, 29%, 53%); + font-size: 18px; + font-weight: 700; + margin: 0 0 12px; +} + +.transaction-info { + flex: 3; + display: grid; + grid-template-rows: repeat(2, auto); + grid-template-columns: auto 4px auto; + place-items: center; +} + +.transaction-info h4 { + grid-row: 1; + grid-column: 1 / -1; +} +.seperator { + grid-column: 2; + grid-row: 2; +} + +.transaction-info-item { + grid-row: 2; +} +.transaction-info-item:first-child { + border-right: 2px solid gray; +} +.transaction-info-item span { + display: block; +} +.transaction-info-item span:first-child { + font-size: 2rem; + font-weight: 600; + text-align: center; + margin-bottom: 4px; +} + +.drained-info { + flex: 2; + display: flex; + flex-direction: column; +} +.drained-info .value { + color: hsl(0, 100%, 52%); + font-size: 25px; + font-weight: 700; + margin: auto; +} + +.credits { + text-align: right; + color: hsl(264, 29%, 53%); + font-size: 14px; + font-weight: 700; +} diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx new file mode 100644 index 0000000..f47acbb --- /dev/null +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -0,0 +1,80 @@ +import "./financial-alert.css"; + +export const config = { + skipTemplate: true, +}; + +export default function FinancialAlert() { + return ( +
+
+

You are about to interact with

+ + + + + + + + +

+ + Contract:{" "} + + Loading... + {/* {"Uniswap V3 Router 0x00...34244 [>]"} */} + + + + Created on:{" "} + + Loading... + {/* {"19 Now 2022"} */} + + +

+
+ +
+
+

Transactions

+
+ 0{/* {"101"} */} + 24 hours +
+ + + +
+ 0{/* 15k */} + 30 days +
+
+
+

Drained accounts

+
———{/* {"HIGH"} */}
+
+
+ +
Powered by VigialnceDAO
+
+ ); +} From 8f06db3082959fa6328690803e1f683aa955bdb0 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 26 Jun 2023 13:25:20 +0530 Subject: [PATCH 02/70] add function to create financial alert dialog --- chrome-extension/src/content.js | 69 +++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index 4c5f983..fb3ddf1 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -425,6 +425,75 @@ async function createAlertDialog(alertInfo) { alertDialog.showModal(); } +const financialAlertDialog = document.createElement("dialog"); + +/** + * @typedef FinancialAlertInfo + * @prop {string} contract + * @prop {string} createdOn + * @prop {"high"} drainedAccountsValue + * @prop {string} transactionsIn24hours + * @prop {string} transactionsIn30days + * + * @param {FinancialAlertInfo} alertInfo + */ +async function createFinancialAlertDialog(alertInfo) { + if (financialAlertDialog.innerHTML != "") return; + + financialAlertDialog.style.borderRadius = "9px"; + financialAlertDialog.style.zIndex = "10000"; + financialAlertDialog.style.margin = "auto clamp(10px, 3vw, 30px) 25px auto"; + financialAlertDialog.style.height = "fit-content"; + financialAlertDialog.style.border = "none"; + financialAlertDialog.style.padding = "0"; + + /** + * @type {string[]} + */ + const innerHTMLParts = new Array(2).fill(""); + // part 0 -> fonts + // part 1 -> financial-alert component content + + innerHTMLParts[0] = ``; + innerHTMLParts[1] = await fetch( + chrome.runtime.getURL("static/financial-alert.html") + ) + .then((response) => response.text()) + .catch((e) => { + console.error("Error while loading html from financial-alert.html", e); + return ""; + }); + + const div = document.createElement("div"); + const shadowRoot = div.attachShadow({ mode: "closed" }); + financialAlertDialog.append(div); + + shadowRoot.innerHTML = innerHTMLParts.join(""); + + const contractInfoElement = shadowRoot.querySelector(".contract-info"); + const contractCreatedOnElement = shadowRoot.querySelector( + ".contract-created-on" + ); + const transactionsIn24hoursElement = shadowRoot.querySelector( + ".transactions-in-day" + ); + const transactionsIn30daysElement = shadowRoot.querySelector( + ".transactions-in-month" + ); + const drainedAccountsValueElement = shadowRoot.querySelector( + ".drained-info .value" + ); + + contractInfoElement.innerHTML = alertInfo.contract; + contractCreatedOnElement.innerHTML = alertInfo.createdOn; + transactionsIn24hoursElement.innerHTML = alertInfo.transactionsIn24hours; + transactionsIn30daysElement.innerHTML = alertInfo.transactionsIn30days; + drainedAccountsValueElement.innerHTML = alertInfo.drainedAccountsValue; + + document.body.appendChild(financialAlertDialog); + financialAlertDialog.showModal(); +} + chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { console.log("on message", msg, sender); From 4c1c71703d0b391d8ed4fed40b1db8102c9d82d1 Mon Sep 17 00:00:00 2001 From: R4j4t-Singh <202011061@diu.iiitvadodara.ac.in> Date: Thu, 29 Jun 2023 21:44:59 +0530 Subject: [PATCH 03/70] Contract-info --- server/package.json | 4 +- server/src/app.ts | 100 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/server/package.json b/server/package.json index 63449ea..bdad001 100644 --- a/server/package.json +++ b/server/package.json @@ -15,8 +15,10 @@ }, "dependencies": { "capture-website": "^2.4.0", + "ethers": "^6.6.2", "express": "^4.18.1", "helmet": "^5.1.0", + "node-fetch": "^3.0.0", "pg": "^8.7.3", "pg-native": "^3.0.0", "pg-pool": "^3.5.1", @@ -26,7 +28,7 @@ "devDependencies": { "@commitlint/cli": "^16.3.0", "@commitlint/config-conventional": "^16.2.4", - "@types/express": "^4.17.13", + "@types/express": "^4.17.17", "@types/jest": "^28.1.0", "@types/pg": "^8.6.5", "@types/pg-pool": "^2.0.3", diff --git a/server/src/app.ts b/server/src/app.ts index f3aed40..115e4ab 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -2,6 +2,8 @@ import express, { json } from 'express'; import helmet from 'helmet'; import { PoolClient } from 'pg'; import pool from './db'; +import {ethers} from 'ethers'; +import fetch from 'node-fetch'; import { BasicDomainInfo, DomainInfo, DomainScamInfo } from "../../important-types"; const whois = require('whois-json'); @@ -9,6 +11,11 @@ const app = express(); app.use(json()); app.use(helmet()); +const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; +const ETHEREUM_RPC_URL = process.env.ETHEREUM_RPC_URL; + +const provider = new ethers.JsonRpcProvider(ETHEREUM_RPC_URL); + app.post('/domain-info', async (req, res) => { // await captureWebsite.file('https://cryptnesis.com/', 'screenshot.png'); @@ -31,6 +38,27 @@ app.post('/domain-info', async (req, res) => { return; }); +app.post("/contract-info", async (req, res) => { + const address = req.body.address; + + console.log("address", address); + if (!address) { + res.status(400).send({}); + return; + } + + try { + const client = await pool.connect(); + const output = await getContractInfo(client, address); + client.release(); + res.json(output); + } catch (err) { + console.log("get contract details", err); + res.status(500).send({}); + } + return; +}); + app.use((_, res, _2) => { res.status(404).json({ error: 'NOT FOUND' }); }); @@ -110,6 +138,76 @@ async function getDomainInfo(client: PoolClient, domain: string): Promise { + + const query = 'SELECT "creationDate" FROM "ContractAddresses" WHERE "address" LIKE $1'; + var contractData = await client.query(query, [address]); + + if(contractData.rowCount == 0){ + const query1 = 'INSERT INTO "ContractAddresses"("address") VALUES($1)'; + await client.query(query1, [address]); + contractData = await client.query(query, [address]); + } + + var date = contractData.rows[0].creationDate; + + if (date == "NA") { + const response = await fetch( + `https://api.etherscan.io/api?module=contract&action=getcontractcreation&contractaddresses=${address}&apikey=${ETHERSCAN_API_KEY}` + ); + + const data:any = await response.json(); + + if (data.result == null) { + return date; + } + + const txHash = data.result[0].txHash; + + try { + const tx = await provider.getTransaction(txHash); + if (tx?.blockHash) { + const block = await provider.getBlock(tx.blockHash); + if(block) { + date = new Date(block.timestamp * 1000); + date = date.toISOString().slice(0, 10); + } + } + } catch (error) { + console.log(error); + return date; + } + + const query2 = 'UPDATE "ContractAddresses" SET "creationDate"=$1 WHERE "address" LIKE $2'; + await client.query(query2, [date, address]); + } + + return date; +} + +async function getContractInfo(client: PoolClient, address: string): Promise { + const now = new Date(); + + var yesterday = new Date(now.setDate(now.getDate() - 1)).getTime() / 1000; + var lastMonth = new Date(now.setDate(now.getDate() - 30)).getTime() / 1000; + + yesterday = Math.floor(yesterday); + lastMonth = Math.floor(lastMonth); + + const query = 'SELECT COUNT(DISTINCT "from") FROM "Transactions" WHERE "to" LIKE $1 AND "timeStamp" > $2'; + + const transactionsLast24Hours = await client.query(query, [address, yesterday]); + + const transactionsLast30Days = await client.query(query, [address, lastMonth]); + + const date = await getCreationDate(client,address); + + return { + userCount24hours: transactionsLast24Hours.rows[0].count, + userCount30days: transactionsLast30Days.rows[0].count, + creationDate: date + }; +} export { app }; @@ -119,4 +217,4 @@ if (process.env.SERVER_TYPE == 'express' && !isListening) { isListening = true; console.log('server listening on 4000') }) -} +} \ No newline at end of file From 03fff071eb8dee4db76104a180fcdb38eb40a3d0 Mon Sep 17 00:00:00 2001 From: R4j4t-Singh <202011061@diu.iiitvadodara.ac.in> Date: Thu, 29 Jun 2023 23:17:19 +0530 Subject: [PATCH 04/70] Added try-catch --- server/src/app.ts | 86 +++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/server/src/app.ts b/server/src/app.ts index 115e4ab..f13e7bd 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -139,50 +139,49 @@ async function getDomainInfo(client: PoolClient, domain: string): Promise { - - const query = 'SELECT "creationDate" FROM "ContractAddresses" WHERE "address" LIKE $1'; - var contractData = await client.query(query, [address]); - - if(contractData.rowCount == 0){ - const query1 = 'INSERT INTO "ContractAddresses"("address") VALUES($1)'; - await client.query(query1, [address]); - contractData = await client.query(query, [address]); - } - - var date = contractData.rows[0].creationDate; - - if (date == "NA") { - const response = await fetch( - `https://api.etherscan.io/api?module=contract&action=getcontractcreation&contractaddresses=${address}&apikey=${ETHERSCAN_API_KEY}` - ); - - const data:any = await response.json(); - - if (data.result == null) { - return date; + try { + + const query = 'SELECT "creationDate" FROM "ContractAddresses" WHERE "address" LIKE $1'; + var contractData = await client.query(query, [address]); + + if(contractData.rowCount == 0){ + const query1 = 'INSERT INTO "ContractAddresses"("address") VALUES($1)'; + await client.query(query1, [address]); + contractData = await client.query(query, [address]); } + + var date = contractData.rows[0].creationDate; + + if (date == "NA") { + const response = await fetch( + `https://api.etherscan.io/api?module=contract&action=getcontractcreation&contractaddresses=${address}&apikey=${ETHERSCAN_API_KEY}` + ); + + const data:any = await response.json(); + + if (data.result == null) { + return date; + } + + const txHash = data.result[0].txHash; - const txHash = data.result[0].txHash; - - try { const tx = await provider.getTransaction(txHash); if (tx?.blockHash) { const block = await provider.getBlock(tx.blockHash); - if(block) { + if(block) { date = new Date(block.timestamp * 1000); date = date.toISOString().slice(0, 10); } } - } catch (error) { - console.log(error); - return date; + const query2 = 'UPDATE "ContractAddresses" SET "creationDate"=$1 WHERE "address" LIKE $2'; + await client.query(query2, [date, address]); } + return date; - const query2 = 'UPDATE "ContractAddresses" SET "creationDate"=$1 WHERE "address" LIKE $2'; - await client.query(query2, [date, address]); + } catch (error) { + console.log(error); + return "NA"; } - - return date; } async function getContractInfo(client: PoolClient, address: string): Promise { @@ -194,19 +193,24 @@ async function getContractInfo(client: PoolClient, address: string): Promise Date: Fri, 30 Jun 2023 01:28:23 +0530 Subject: [PATCH 05/70] try-catch --- server/src/app.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/app.ts b/server/src/app.ts index f13e7bd..2dd4939 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -180,8 +180,8 @@ async function getCreationDate(client: PoolClient, address: string): Promise { @@ -209,8 +209,8 @@ async function getContractInfo(client: PoolClient, address: string): Promise Date: Fri, 30 Jun 2023 09:29:29 +0530 Subject: [PATCH 06/70] add financial alert to prebuild script --- chrome-extension/src/prebuild.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/prebuild.tsx b/chrome-extension/src/prebuild.tsx index 0ab8c3b..6196f0f 100644 --- a/chrome-extension/src/prebuild.tsx +++ b/chrome-extension/src/prebuild.tsx @@ -5,6 +5,7 @@ import { renderToStaticMarkup } from "react-dom/server"; import * as Alert from "./prebuild-components/alert"; import * as Index from "./prebuild-components/index"; +import * as FinancialAlert from "./prebuild-components/financial-alert"; interface PrebuildComponentModule { default: () => JSX.Element; @@ -17,7 +18,11 @@ interface PrebuildComponentModule { }; } -const components: readonly PrebuildComponentModule[] = [Alert, Index] as const; +const components: readonly PrebuildComponentModule[] = [ + Alert, + Index, + FinancialAlert, +] as const; const template = ( content: string, From 468bd62df28ec3fd7cbb770d9570a869a0cbc963 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 30 Jun 2023 09:37:26 +0530 Subject: [PATCH 07/70] fix name issue in prebuild script --- chrome-extension/src/prebuild.tsx | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/prebuild.tsx b/chrome-extension/src/prebuild.tsx index 6196f0f..66df844 100644 --- a/chrome-extension/src/prebuild.tsx +++ b/chrome-extension/src/prebuild.tsx @@ -7,6 +7,30 @@ import * as Alert from "./prebuild-components/alert"; import * as Index from "./prebuild-components/index"; import * as FinancialAlert from "./prebuild-components/financial-alert"; +const UPPERCASE_REGEX = /[A-Z]/g; +function isUpperCase(char: string): boolean { + return UPPERCASE_REGEX.test(char); +} + +/** + * @example FinancialAlert -> financial-alert + */ +function pascalCaseToHypenedCase(str: string): string { + let newStr = ""; + + for (let i = 0; i < str.length; i++) { + const char = str.charAt(i); + + if (i != 0 && isUpperCase(char)) { + newStr += "-"; + } + + newStr += char.toLowerCase(); + } + + return newStr; +} + interface PrebuildComponentModule { default: () => JSX.Element; config?: { @@ -66,7 +90,8 @@ export function run() { components.map(async (componentModule) => { const component = componentModule.default; const rendered = renderToStaticMarkup(component()); - const baseName = component.name.toLowerCase(); + const baseName = pascalCaseToHypenedCase(component.name); + console.log(baseName); const jsFile = componentModule.config?.jsFile || "../prebuild-components/".concat(baseName.concat(".js")); From 1e9cc4b07535f9a073d3e3966e15dbd3dd01cae5 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 30 Jun 2023 09:37:42 +0530 Subject: [PATCH 08/70] fix wrong attributes in svg --- .../src/prebuild-components/financial-alert.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index f47acbb..232c2b8 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -16,11 +16,11 @@ export default function FinancialAlert() { width="24" height="24" viewBox="0 0 24 24" - stroke-width="2" + strokeWidth="2" stroke="currentColor" fill="none" - stroke-linecap="round" - stroke-linejoin="round" + strokeLinecap="round" + strokeLinejoin="round" > From f5cbcede2b10275ca8af3923d15e6b23aa3d9a5c Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 30 Jun 2023 09:57:49 +0530 Subject: [PATCH 09/70] minor fixes --- chrome-extension/src/content.js | 20 +++++++++++++++++-- .../prebuild-components/financial-alert.css | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index fb3ddf1..dcde268 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -431,7 +431,7 @@ const financialAlertDialog = document.createElement("dialog"); * @typedef FinancialAlertInfo * @prop {string} contract * @prop {string} createdOn - * @prop {"high"} drainedAccountsValue + * @prop {"High"} drainedAccountsValue * @prop {string} transactionsIn24hours * @prop {string} transactionsIn30days * @@ -440,6 +440,13 @@ const financialAlertDialog = document.createElement("dialog"); async function createFinancialAlertDialog(alertInfo) { if (financialAlertDialog.innerHTML != "") return; + if (alertInfo == undefined) { + console.error( + "createFinancialAlertDialog: alertInfo parameter is required" + ); + return; + } + financialAlertDialog.style.borderRadius = "9px"; financialAlertDialog.style.zIndex = "10000"; financialAlertDialog.style.margin = "auto clamp(10px, 3vw, 30px) 25px auto"; @@ -484,10 +491,19 @@ async function createFinancialAlertDialog(alertInfo) { ".drained-info .value" ); + let formattedTransactionsIn30days = alertInfo.transactionsIn30days.toString(); + if (alertInfo.transactionsIn30days >= 1000) { + formattedTransactionsIn30days = Math.floor( + alertInfo.transactionsIn30days / 1000 + ) + .toString() + .concat("K"); + } + contractInfoElement.innerHTML = alertInfo.contract; contractCreatedOnElement.innerHTML = alertInfo.createdOn; transactionsIn24hoursElement.innerHTML = alertInfo.transactionsIn24hours; - transactionsIn30daysElement.innerHTML = alertInfo.transactionsIn30days; + transactionsIn30daysElement.innerHTML = formattedTransactionsIn30days; drainedAccountsValueElement.innerHTML = alertInfo.drainedAccountsValue; document.body.appendChild(financialAlertDialog); diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 7fbfb2f..caac70b 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -13,6 +13,7 @@ font-family: Roboto; max-width: 540px; min-width: 470px; + overflow: hidden; } .container:before { @@ -99,6 +100,7 @@ h4 { .transaction-info-item { grid-row: 2; + text-align: center; } .transaction-info-item:first-child { border-right: 2px solid gray; From d335c8b632bd4656885f640dc6f621754891e564 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 30 Jun 2023 09:58:13 +0530 Subject: [PATCH 10/70] add proceed and close buttons --- chrome-extension/src/content.js | 20 ++++++++++++++ .../prebuild-components/financial-alert.css | 27 ++++++++++++++++++- .../prebuild-components/financial-alert.tsx | 7 ++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index dcde268..26bfcc6 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -490,6 +490,12 @@ async function createFinancialAlertDialog(alertInfo) { const drainedAccountsValueElement = shadowRoot.querySelector( ".drained-info .value" ); + const proceedButton = shadowRoot.querySelector( + "#proceed-btn" + ); + const closeButton = shadowRoot.querySelector( + "#close-btn" + ); let formattedTransactionsIn30days = alertInfo.transactionsIn30days.toString(); if (alertInfo.transactionsIn30days >= 1000) { @@ -505,11 +511,25 @@ async function createFinancialAlertDialog(alertInfo) { transactionsIn24hoursElement.innerHTML = alertInfo.transactionsIn24hours; transactionsIn30daysElement.innerHTML = formattedTransactionsIn30days; drainedAccountsValueElement.innerHTML = alertInfo.drainedAccountsValue; + proceedButton.addEventListener("click", () => { + console.log("TODO: proceedButton onclick"); + }); + closeButton.addEventListener("click", () => { + financialAlertDialog.close(); + }); document.body.appendChild(financialAlertDialog); financialAlertDialog.showModal(); } +createFinancialAlertDialog({ + contract: "Uniswap V3 Router 0x00...34244 [>]", + createdOn: new Date().toDateString(), + drainedAccountsValue: "High", + transactionsIn24hours: 102, + transactionsIn30days: 1000, +}); + chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { console.log("on message", msg, sender); diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index caac70b..6a75a1d 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -127,9 +127,34 @@ h4 { margin: auto; } -.credits { +.bottom-container { + display: flex; + align-items: center; +} + +.credits{ + margin-right: auto; text-align: right; color: hsl(264, 29%, 53%); font-size: 14px; font-weight: 700; } + +.bottom-container button { + margin-left: 5px; + border-radius: 3px; + padding: 5px 10px; + font-weight: 600; + cursor: pointer; + font-size: 16px; + line-height: 19px; + border: 2px solid rgb(223, 223, 223); + background: none; + opacity: 0.7; + transition: opacity .2s ease-in-out, border-color .2s ease-in-out; +} + +.bottom-container button:hover { + opacity: 1; + border-color: hsl(265deg 94% 75%); +} diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index 232c2b8..af24ff5 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -74,7 +74,12 @@ export default function FinancialAlert() { -
Powered by VigialnceDAO
+
+
Powered by VigialnceDAO
+ + + +
); } From 178f929e67dc8c412ca8dd88e531935baa478b46 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 30 Jun 2023 12:57:14 +0530 Subject: [PATCH 11/70] comment testing only code --- chrome-extension/src/content.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index 26bfcc6..c8f5c85 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -522,13 +522,14 @@ async function createFinancialAlertDialog(alertInfo) { financialAlertDialog.showModal(); } -createFinancialAlertDialog({ - contract: "Uniswap V3 Router 0x00...34244 [>]", - createdOn: new Date().toDateString(), - drainedAccountsValue: "High", - transactionsIn24hours: 102, - transactionsIn30days: 1000, -}); +// For testing only +// createFinancialAlertDialog({ +// contract: "Uniswap V3 Router 0x00...34244 [>]", +// createdOn: new Date().toDateString(), +// drainedAccountsValue: "High", +// transactionsIn24hours: 102, +// transactionsIn30days: 1000, +// }); chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { console.log("on message", msg, sender); From b50d9b6f18c48a278b851fa882e669e61e3deb5b Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 30 Jun 2023 13:27:41 +0530 Subject: [PATCH 12/70] fix backdrop style issue --- chrome-extension/public/content.css | 2 +- chrome-extension/src/content.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/chrome-extension/public/content.css b/chrome-extension/public/content.css index 12f764a..4bf3b3d 100644 --- a/chrome-extension/public/content.css +++ b/chrome-extension/public/content.css @@ -1,3 +1,3 @@ -.____vigilance-dao-alert-dialog____::backdrop { +.____vigilance-dao-dialog____::backdrop { background-color: hsl(20deg 8.57% 6.86% / 34.12%); } diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index c8f5c85..1f854b2 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -420,7 +420,7 @@ async function createAlertDialog(alertInfo) { alertDialog.close(); } }); - alertDialog.className = "____vigilance-dao-alert-dialog____"; + alertDialog.className = "____vigilance-dao-dialog____"; document.body.appendChild(alertDialog); alertDialog.showModal(); } @@ -518,6 +518,7 @@ async function createFinancialAlertDialog(alertInfo) { financialAlertDialog.close(); }); + financialAlertDialog.className = "____vigilance-dao-dialog____"; document.body.appendChild(financialAlertDialog); financialAlertDialog.showModal(); } From 40a2e26294fdaa1891a143f31a966ff4efa000e5 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 30 Jun 2023 13:28:30 +0530 Subject: [PATCH 13/70] fix missing border --- chrome-extension/src/content.js | 23 +++++++++++-------- .../prebuild-components/financial-alert.css | 23 +++++++------------ 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index 1f854b2..1fc0d28 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -447,22 +447,31 @@ async function createFinancialAlertDialog(alertInfo) { return; } - financialAlertDialog.style.borderRadius = "9px"; + const INNER_BORDER_RADIUS = 10; + const BORDER_WIDTH = 10; + const OUTER_BORDER_RADIUS = INNER_BORDER_RADIUS + BORDER_WIDTH; + + financialAlertDialog.style.borderRadius = + OUTER_BORDER_RADIUS.toString().concat("px"); financialAlertDialog.style.zIndex = "10000"; financialAlertDialog.style.margin = "auto clamp(10px, 3vw, 30px) 25px auto"; financialAlertDialog.style.height = "fit-content"; financialAlertDialog.style.border = "none"; financialAlertDialog.style.padding = "0"; + financialAlertDialog.style.background = + "linear-gradient(to bottom, hsl(265, 100%, 40%), hsl(265, 94%, 19%))"; /** * @type {string[]} */ const innerHTMLParts = new Array(2).fill(""); // part 0 -> fonts - // part 1 -> financial-alert component content + // part 1 -> css variables + // part 2 -> financial-alert component content innerHTMLParts[0] = ``; - innerHTMLParts[1] = await fetch( + innerHTMLParts[1] = ``; + innerHTMLParts[2] = await fetch( chrome.runtime.getURL("static/financial-alert.html") ) .then((response) => response.text()) @@ -490,12 +499,8 @@ async function createFinancialAlertDialog(alertInfo) { const drainedAccountsValueElement = shadowRoot.querySelector( ".drained-info .value" ); - const proceedButton = shadowRoot.querySelector( - "#proceed-btn" - ); - const closeButton = shadowRoot.querySelector( - "#close-btn" - ); + const proceedButton = shadowRoot.querySelector("#proceed-btn"); + const closeButton = shadowRoot.querySelector("#close-btn"); let formattedTransactionsIn30days = alertInfo.transactionsIn30days.toString(); if (alertInfo.transactionsIn30days >= 1000) { diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 6a75a1d..18a178a 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -1,32 +1,25 @@ +:host { + /* Will be set from javascript */ + /* --border: 10px; */ + /* --inner-border-radius: 10px; */ +} + * { box-sizing: border-box; } .container { - --border: 10px; --icon-size: 70px; padding: 25px 18px 10px; - border-radius: 10px; + border-radius: var(--inner-border-radius); position: relative; background: hsl(265, 100%, 92%); font-family: Roboto; max-width: 540px; min-width: 470px; overflow: hidden; -} - -.container:before { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: -1; - background: linear-gradient(to bottom, hsl(265, 100%, 40%), hsl(265, 94%, 19%)); - margin: calc(-1 * var(--border)); - border-radius: inherit; + margin: var(--border); } .basic-info { From 3d6d15373dc408c73db54ba3cea3f3cf24f2126c Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 3 Jul 2023 19:44:07 +0530 Subject: [PATCH 14/70] chore(deps): add @types/mixpanel-browser for better intellisense --- chrome-extension/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/chrome-extension/package.json b/chrome-extension/package.json index 69d7668..fb111cf 100644 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -53,6 +53,7 @@ "web3.storage": "4.4.0" }, "devDependencies": { + "@types/mixpanel-browser": "^2.47.0", "concurrently": "^8.2.0", "esbuild-envfile-plugin": "^1.0.5", "mkdirp": "^3.0.0", From 92bd6ce2e39b1ea696b2a71b4903ac62833561f4 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 3 Jul 2023 19:44:35 +0530 Subject: [PATCH 15/70] fix: scripts in package.json --- chrome-extension/package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/chrome-extension/package.json b/chrome-extension/package.json index fb111cf..ec4a43a 100644 --- a/chrome-extension/package.json +++ b/chrome-extension/package.json @@ -6,11 +6,12 @@ "scripts": { "build:other": "node bundle-config.mjs", "dev:other": "node bundle-config.mjs --watch", - "build": "concurrently npm:build:content npm:build:other", - "dev": "concurrently npm:dev:other npm:dev:content", + "build": "concurrently npm:build:content npm:build:other npm:build:inject", + "dev": "concurrently npm:dev:other npm:dev:content npm:dev:inject", "build:content": "browserify src/content.js -o build/content.js", - "dev:content": "nodemon --exec \"browserify src/content.js -o build/content.js\" --watch src/content.js". + "dev:content": "nodemon --exec \"browserify src/content.js -o build/content.js\" --watch src/content.js", "build:inject": "browserify src/inject.js -o build/inject.js", + "dev:inject": "nodemon --exec \"browserify src/inject.js -o build/inject.js\" --watch src/inject.js", "release": "yarn build && yarn build:content && yarn build:inject" }, "keywords": [], From 007975abe62625b2394fa923e4a156fabd6baa9d Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Wed, 5 Jul 2023 13:36:58 +0530 Subject: [PATCH 16/70] add financialAlertDialog in inject.js --- chrome-extension/src/content.js | 112 -------------- chrome-extension/src/inject.d.ts | 45 ++++++ chrome-extension/src/inject.js | 247 +++++++++++++++++++++++++++++-- 3 files changed, 276 insertions(+), 128 deletions(-) create mode 100644 chrome-extension/src/inject.d.ts diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index 6fa8d01..ad478c2 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -459,118 +459,6 @@ async function createAlertDialog(alertInfo) { alertDialog.showModal(); } -const financialAlertDialog = document.createElement("dialog"); - -/** - * @typedef FinancialAlertInfo - * @prop {string} contract - * @prop {string} createdOn - * @prop {"High"} drainedAccountsValue - * @prop {string} transactionsIn24hours - * @prop {string} transactionsIn30days - * - * @param {FinancialAlertInfo} alertInfo - */ -async function createFinancialAlertDialog(alertInfo) { - if (financialAlertDialog.innerHTML != "") return; - - if (alertInfo == undefined) { - console.error( - "createFinancialAlertDialog: alertInfo parameter is required" - ); - return; - } - - const INNER_BORDER_RADIUS = 10; - const BORDER_WIDTH = 10; - const OUTER_BORDER_RADIUS = INNER_BORDER_RADIUS + BORDER_WIDTH; - - financialAlertDialog.style.borderRadius = - OUTER_BORDER_RADIUS.toString().concat("px"); - financialAlertDialog.style.zIndex = "10000"; - financialAlertDialog.style.margin = "auto clamp(10px, 3vw, 30px) 25px auto"; - financialAlertDialog.style.height = "fit-content"; - financialAlertDialog.style.border = "none"; - financialAlertDialog.style.padding = "0"; - financialAlertDialog.style.background = - "linear-gradient(to bottom, hsl(265, 100%, 40%), hsl(265, 94%, 19%))"; - - /** - * @type {string[]} - */ - const innerHTMLParts = new Array(2).fill(""); - // part 0 -> fonts - // part 1 -> css variables - // part 2 -> financial-alert component content - - innerHTMLParts[0] = ``; - innerHTMLParts[1] = ``; - innerHTMLParts[2] = await fetch( - chrome.runtime.getURL("static/financial-alert.html") - ) - .then((response) => response.text()) - .catch((e) => { - console.error("Error while loading html from financial-alert.html", e); - return ""; - }); - - const div = document.createElement("div"); - const shadowRoot = div.attachShadow({ mode: "closed" }); - financialAlertDialog.append(div); - - shadowRoot.innerHTML = innerHTMLParts.join(""); - - const contractInfoElement = shadowRoot.querySelector(".contract-info"); - const contractCreatedOnElement = shadowRoot.querySelector( - ".contract-created-on" - ); - const transactionsIn24hoursElement = shadowRoot.querySelector( - ".transactions-in-day" - ); - const transactionsIn30daysElement = shadowRoot.querySelector( - ".transactions-in-month" - ); - const drainedAccountsValueElement = shadowRoot.querySelector( - ".drained-info .value" - ); - const proceedButton = shadowRoot.querySelector("#proceed-btn"); - const closeButton = shadowRoot.querySelector("#close-btn"); - - let formattedTransactionsIn30days = alertInfo.transactionsIn30days.toString(); - if (alertInfo.transactionsIn30days >= 1000) { - formattedTransactionsIn30days = Math.floor( - alertInfo.transactionsIn30days / 1000 - ) - .toString() - .concat("K"); - } - - contractInfoElement.innerHTML = alertInfo.contract; - contractCreatedOnElement.innerHTML = alertInfo.createdOn; - transactionsIn24hoursElement.innerHTML = alertInfo.transactionsIn24hours; - transactionsIn30daysElement.innerHTML = formattedTransactionsIn30days; - drainedAccountsValueElement.innerHTML = alertInfo.drainedAccountsValue; - proceedButton.addEventListener("click", () => { - console.log("TODO: proceedButton onclick"); - }); - closeButton.addEventListener("click", () => { - financialAlertDialog.close(); - }); - - financialAlertDialog.className = "____vigilance-dao-dialog____"; - document.body.appendChild(financialAlertDialog); - financialAlertDialog.showModal(); -} - -// For testing only -// createFinancialAlertDialog({ -// contract: "Uniswap V3 Router 0x00...34244 [>]", -// createdOn: new Date().toDateString(), -// drainedAccountsValue: "High", -// transactionsIn24hours: 102, -// transactionsIn30days: 1000, -// }); - chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { console.log("on message", msg, sender); diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts new file mode 100644 index 0000000..dd6fb05 --- /dev/null +++ b/chrome-extension/src/inject.d.ts @@ -0,0 +1,45 @@ +interface ETH_SendTransactionRequestParamsItem { + gas: string; + value: string; + from: string; + to: string; + data: string; +} + +interface ETH_SendTransactionRequest { + method: "eth_sendTransaction"; + params: ETH_SendTransactionRequestParamsItem[]; +} + +interface MetaMaskRequest { + method: string; +} + +interface ContractInfo { + userCount24hours: number; + userCount30days: number; + creationDate: string; + name: string; +} + +interface BasicContractInfo { + address: string; + chain_id: string; +} + +/** + * Example ETH_SendTransactionRequest + * { + "method": "eth_sendTransaction", + "params": [ + { + "gas": "0x1141c", + "value": "0x3782dace9d900000", + "from": "0x49df7fdcb70b24aead2b28ac1813582b99279304", + "to": "0x2ef4a574b72e1f555185afa8a09c6d1a8ac4025c", + "data": "0xa5e5657100000000000000000000000021804205c744dd98fbc87898704564d5094bb16700000000000000000000000049df7fdcb70b24aead2b28ac1813582b992793040000000000000000000000000000000000000000000000000000000000000038" + } + ] +} + * + */ diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 840355e..63bd370 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -1,19 +1,234 @@ -const mixpanel = require('mixpanel-browser'); +// @ts-check +const mixpanel = require("mixpanel-browser"); const { MIXPANEL_PROJECT_ID } = require("../privateenv"); +const { getFonts } = require("./fonts"); -(function() { - const metamaskRequest = window.ethereum.request; - window.ethereum.request = async (params) => { - if(params.method === "eth_sendTransaction") { - const { to, from, value, data } = params.params[0]; - mixpanel.init(MIXPANEL_PROJECT_ID, {debug: true}); - mixpanel.identify(from); - mixpanel.track("Transaction", { - "toAddress" : to, - "value" : value, - "data" : data - }); - } - return await metamaskRequest({ ...params }) +const ContractInfoAPIURL = + "https://8md2nmtej9.execute-api.ap-northeast-1.amazonaws.com/contract-info"; + +/** + * @param {MetaMaskRequest} params + * @returns {params is ETH_SendTransactionRequest} + */ +function isSendTransactionRequest(params) { + return params.method == "eth_sendTransaction"; +} + +/** + * @param {BasicContractInfo} basicInfo + * @returns {Promise} + */ +function fetchContractInfo(basicInfo) { + return fetch(ContractInfoAPIURL, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(basicInfo), + }) + .then((response) => response.json()) + .then( + /** + * @param {ContractInfo} jsonData + */ + (jsonData) => { + console.log(jsonData); + return jsonData; + } + ) + .catch((error) => { + console.error(error); + return null; + }); +} + +/** + * @type {(shadowRoot: ShadowRoot, selector: string) => E} + */ +function querySelector(shadowRoot, selector) { + const element = shadowRoot.querySelector(selector); + + if (element == null) { + throw new Error(`No element found for ${selector}`); + } + + // @ts-expect-error + return element; +} + +const financialAlertDialog = document.createElement("dialog"); +let financialAlertDialogInnerHtml = ""; + +/** + * @typedef FinancialAlertInfo + * @prop {string} contract + * @prop {string} createdOn + * @prop {"High"} drainedAccountsValue + * @prop {number} transactionsIn24hours + * @prop {number} transactionsIn30days + * @prop {() => void} cancelButtonClickListener + * @prop {() => void} proceedButtonClickListener + * + * @param {FinancialAlertInfo} alertInfo + */ +async function createFinancialAlertDialog(alertInfo) { + console.log("createFinancialAlertDialog", alertInfo); + if (alertInfo == undefined) { + console.error( + "createFinancialAlertDialog: alertInfo parameter is required" + ); + return; + } + + const INNER_BORDER_RADIUS = 10; + const BORDER_WIDTH = 10; + const OUTER_BORDER_RADIUS = INNER_BORDER_RADIUS + BORDER_WIDTH; + + if (financialAlertDialogInnerHtml == "") { + financialAlertDialog.style.borderRadius = + OUTER_BORDER_RADIUS.toString().concat("px"); + financialAlertDialog.style.zIndex = "10000"; + financialAlertDialog.style.margin = "auto clamp(10px, 3vw, 30px) 25px auto"; + financialAlertDialog.style.height = "fit-content"; + financialAlertDialog.style.border = "none"; + financialAlertDialog.style.padding = "0"; + financialAlertDialog.style.background = + "linear-gradient(to bottom, hsl(265, 100%, 40%), hsl(265, 94%, 19%))"; + + /** + * @type {string[]} + */ + const innerHTMLParts = new Array(3).fill(""); + // part 0 -> fonts + // part 1 -> css variables + // part 2 -> financial-alert component content + + innerHTMLParts[0] = ``; + innerHTMLParts[1] = ``; + innerHTMLParts[2] = await fetch( + chrome.runtime.getURL("static/financial-alert.html") + ) + .then((response) => response.text()) + .catch((e) => { + console.error("Error while loading html from financial-alert.html", e); + return ""; + }); + + financialAlertDialogInnerHtml = innerHTMLParts.join(""); + } + + financialAlertDialog.innerHTML = ""; + const div = document.createElement("div"); + const shadowRoot = div.attachShadow({ mode: "closed" }); + financialAlertDialog.appendChild(div); + + shadowRoot.innerHTML = financialAlertDialogInnerHtml; + + const select = querySelector.bind(null, shadowRoot); + + const contractInfoElement = select(".contract-info"); + const contractCreatedOnElement = select(".contract-created-on"); + const transactionsIn24hoursElement = select(".transactions-in-day"); + const transactionsIn30daysElement = select(".transactions-in-month"); + const drainedAccountsValueElement = select(".drained-info .value"); + + const proceedButton = select("#proceed-btn"); + const closeButton = select("#close-btn"); + + let formattedTransactionsIn30days = alertInfo.transactionsIn30days.toString(); + if (alertInfo.transactionsIn30days >= 1000) { + formattedTransactionsIn30days = Math.floor( + alertInfo.transactionsIn30days / 1000 + ) + .toString() + .concat("K"); + } + + contractInfoElement.innerHTML = alertInfo.contract; + contractCreatedOnElement.innerHTML = alertInfo.createdOn; + transactionsIn24hoursElement.innerHTML = + alertInfo.transactionsIn24hours.toString(); + transactionsIn30daysElement.innerHTML = formattedTransactionsIn30days; + drainedAccountsValueElement.innerHTML = alertInfo.drainedAccountsValue; + proceedButton.addEventListener("click", alertInfo.proceedButtonClickListener); + closeButton.addEventListener("click", alertInfo.cancelButtonClickListener); + + financialAlertDialog.className = "____vigilance-dao-dialog____"; + document.body.appendChild(financialAlertDialog); + financialAlertDialog.show(); } -})(); \ No newline at end of file + +(function () { + if (window.ethereum == undefined) { + console.warn("Metamask extension is not installed"); + return; + } + + // @ts-expect-error + const metamaskRequest = window.ethereum.request; + /** + * @param {MetaMaskRequest} params + */ + // @ts-expect-error + window.ethereum.request = (params) => { + return /** @type {Promise} */ ( + new Promise(async (continueRequest, reject) => { + console.log("INJECTED", params); + console.log("isSendTransactionRequest", params); + + if (window.ethereum == undefined || !isSendTransactionRequest(params)) { + continueRequest(); + return; + } + + // @ts-expect-error + const chainId = window.ethereum.networkVersion; + console.log("chainId", chainId); + + const { to, from, value, data } = params.params[0]; + mixpanel.init(MIXPANEL_PROJECT_ID, { debug: true }); + mixpanel.identify(from); + mixpanel.track("Transaction", { + "toAddress": to, + "value": value, + "data": data, + chainId, + }); + + const contractInfo = await fetchContractInfo({ + address: to, + chain_id: chainId, + }); + console.log("contractInfo", contractInfo); + + if (contractInfo == null) { + // TODO decide what to do at this point + continueRequest(); + return; + } + createFinancialAlertDialog({ + createdOn: contractInfo.creationDate, + contract: contractInfo.name, + transactionsIn24hours: contractInfo.userCount24hours, + transactionsIn30days: contractInfo.userCount30days, + proceedButtonClickListener: () => { + console.log("proceed btn clicked"); + financialAlertDialog.close(); + // continueRequest(); + // TODO FOR TESTING ONLY + reject(); + }, + cancelButtonClickListener: () => { + financialAlertDialog.close(); + reject(new Error("Transaction cancelled by user.")); + }, + // TODO + drainedAccountsValue: "High", + }); + }) + ).then(() => { + console.log("will complete transaction now"); + return metamaskRequest({ ...params }); + }); + }; +})(); From b584c0a8fc3c7c90e536d24d5648b547baf1e1b9 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 13:00:47 +0530 Subject: [PATCH 17/70] add comment about script injection --- chrome-extension/src/content.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index ad478c2..f4541fd 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -155,6 +155,12 @@ function trackVisitedDomain(data) { }) } +/** + * We can't use inject.js as a content script because content scripts wouldn't + * have access to window.ethereum or any other additional APIs. + * To learn more: + * https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#content_script_environment:~:text=However%2C%20content%20scripts,the%20redefined%20version. + */ const addScriptTagInPage = async () => { const script = window.document.createElement("script"); let url = chrome.runtime?.getURL("inject.js"); From e3d0b0fde20126e1b96c957ad567804bec0b12e5 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 13:02:20 +0530 Subject: [PATCH 18/70] fix issue related to chrome.runtime --- chrome-extension/src/fonts.js | 73 +++++++++++++++++++++++++++++----- chrome-extension/src/inject.js | 10 ++--- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/chrome-extension/src/fonts.js b/chrome-extension/src/fonts.js index abb570b..288e2d2 100644 --- a/chrome-extension/src/fonts.js +++ b/chrome-extension/src/fonts.js @@ -4,27 +4,80 @@ const FONT_WEIGHTS = { "700": "./fonts/Roboto-Bold.ttf", }; -function getFonts() { +/** + * @param {string} url + * @returns {Promise} + */ +async function chromeRuntimeGetUrlWrapped(url) { + if (typeof chrome == "undefined" || chrome.runtime == undefined) { + // We are running inside an injected script then (not a content script) + // In that case, we have to communicate the content script and get the url + const message = { + reason: "runtime-get-url", + relativeUrl: url, + }; + return new Promise(function (resolve) { + /** + * @param {MessageEvent} event + */ + function handleMessage(event) { + if ( + event.source === window && + event.data != undefined && + typeof event.data.response == "string" + ) { + // if event.data.for is available, only resolve it if it matches with relativeUrl + if (event.data.for && event.data.for != message.relativeUrl) { + return; + } + + // Remove the event listener once the response is received + window.removeEventListener("message", handleMessage); + resolve(event.data.response); + } + } + + // Listen for messages from the content script + window.addEventListener("message", handleMessage); + + // Send the message to the content script + window.postMessage(message, "*"); + }); + } else { + return chrome.runtime.getURL(url); + } +} + +async function getFonts() { if (typeof chrome == "undefined") { return ""; } - const rules = []; + const entries = Object.entries(FONT_WEIGHTS); - for (let i = 0; i < entries.length; i++) { - const [weight, value] = entries[i]; - rules.push(`@font-face { + return Promise.all( + entries.map(async (entry) => { + const [weight, value] = entry; + + return `@font-face { font-family: '${FONT_NAME}'; font-style: normal; font-weight: ${weight}; font-display: swap; - src: url(${chrome.runtime.getURL(value)}) format('ttf'); -}`); - } - - return rules.join("\n"); + src: url(${await chromeRuntimeGetUrlWrapped(value)}) format('ttf'); +}`; + }) + ) + .then((rules) => { + return rules.join("\n"); + }) + .catch((error) => { + console.error(error); + return ""; + }); } module.exports = { getFonts, + chromeRuntimeGetUrlWrapped, }; diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 63bd370..796fd76 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -1,7 +1,7 @@ // @ts-check const mixpanel = require("mixpanel-browser"); const { MIXPANEL_PROJECT_ID } = require("../privateenv"); -const { getFonts } = require("./fonts"); +const { getFonts, chromeRuntimeGetUrlWrapped } = require("./fonts"); const ContractInfoAPIURL = "https://8md2nmtej9.execute-api.ap-northeast-1.amazonaws.com/contract-info"; @@ -103,11 +103,11 @@ async function createFinancialAlertDialog(alertInfo) { // part 1 -> css variables // part 2 -> financial-alert component content - innerHTMLParts[0] = ``; + innerHTMLParts[0] = ``; innerHTMLParts[1] = ``; - innerHTMLParts[2] = await fetch( - chrome.runtime.getURL("static/financial-alert.html") - ) + const url = await chromeRuntimeGetUrlWrapped("static/financial-alert.html"); + console.log("LAODINLAJLKA", url); + innerHTMLParts[2] = await fetch(url) .then((response) => response.text()) .catch((e) => { console.error("Error while loading html from financial-alert.html", e); From 670b5083458f907e3f25d96ca42cb1805828c800 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 13:14:54 +0530 Subject: [PATCH 19/70] fix styling issues with financial-alert --- chrome-extension/src/inject.js | 5 ++++- .../src/prebuild-components/financial-alert.css | 17 +++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 796fd76..ac0e1f9 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -88,7 +88,10 @@ async function createFinancialAlertDialog(alertInfo) { financialAlertDialog.style.borderRadius = OUTER_BORDER_RADIUS.toString().concat("px"); financialAlertDialog.style.zIndex = "10000"; - financialAlertDialog.style.margin = "auto clamp(10px, 3vw, 30px) 25px auto"; + financialAlertDialog.style.position = "absolute"; + financialAlertDialog.style.top = "10px"; + financialAlertDialog.style.right = "clamp(10px, 3vw, 30px)"; + financialAlertDialog.style.left = "auto"; financialAlertDialog.style.height = "fit-content"; financialAlertDialog.style.border = "none"; financialAlertDialog.style.padding = "0"; diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 18a178a..c5aa777 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -2,6 +2,7 @@ /* Will be set from javascript */ /* --border: 10px; */ /* --inner-border-radius: 10px; */ + font-size: 16px; } * { @@ -34,7 +35,7 @@ .basic-info h2 { margin: 0 0 16px; grid-area: heading; - font-size: 25px; + font-size: 1.5em; } .basic-info .icon-container { grid-area: icon; @@ -49,7 +50,7 @@ .basic-info p { grid-area: info; margin: 0; - font-size: 18px; + font-size: 1.1em; } .basic-info p > span { display: block; @@ -69,7 +70,7 @@ h4 { text-align: center; color: hsl(264, 29%, 53%); - font-size: 18px; + font-size: 1.1em; font-weight: 700; margin: 0 0 12px; } @@ -102,7 +103,7 @@ h4 { display: block; } .transaction-info-item span:first-child { - font-size: 2rem; + font-size: 2em; font-weight: 600; text-align: center; margin-bottom: 4px; @@ -115,7 +116,7 @@ h4 { } .drained-info .value { color: hsl(0, 100%, 52%); - font-size: 25px; + font-size: 1.5em; font-weight: 700; margin: auto; } @@ -129,7 +130,7 @@ h4 { margin-right: auto; text-align: right; color: hsl(264, 29%, 53%); - font-size: 14px; + font-size: 0.8em; font-weight: 700; } @@ -139,9 +140,9 @@ h4 { padding: 5px 10px; font-weight: 600; cursor: pointer; - font-size: 16px; + font-size: 1em; line-height: 19px; - border: 2px solid rgb(223, 223, 223); + border: 2px solid rgb(187 187 187); background: none; opacity: 0.7; transition: opacity .2s ease-in-out, border-color .2s ease-in-out; From 8ae0490224a246ff2c91a30cb678357a6c9e454b Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 13:15:10 +0530 Subject: [PATCH 20/70] minor refactor in inject.js --- chrome-extension/src/inject.js | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index ac0e1f9..a8af090 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -27,15 +27,6 @@ function fetchContractInfo(basicInfo) { body: JSON.stringify(basicInfo), }) .then((response) => response.json()) - .then( - /** - * @param {ContractInfo} jsonData - */ - (jsonData) => { - console.log(jsonData); - return jsonData; - } - ) .catch((error) => { console.error(error); return null; @@ -72,7 +63,6 @@ let financialAlertDialogInnerHtml = ""; * @param {FinancialAlertInfo} alertInfo */ async function createFinancialAlertDialog(alertInfo) { - console.log("createFinancialAlertDialog", alertInfo); if (alertInfo == undefined) { console.error( "createFinancialAlertDialog: alertInfo parameter is required" @@ -109,7 +99,6 @@ async function createFinancialAlertDialog(alertInfo) { innerHTMLParts[0] = ``; innerHTMLParts[1] = ``; const url = await chromeRuntimeGetUrlWrapped("static/financial-alert.html"); - console.log("LAODINLAJLKA", url); innerHTMLParts[2] = await fetch(url) .then((response) => response.text()) .catch((e) => { @@ -176,9 +165,6 @@ async function createFinancialAlertDialog(alertInfo) { window.ethereum.request = (params) => { return /** @type {Promise} */ ( new Promise(async (continueRequest, reject) => { - console.log("INJECTED", params); - console.log("isSendTransactionRequest", params); - if (window.ethereum == undefined || !isSendTransactionRequest(params)) { continueRequest(); return; @@ -186,7 +172,6 @@ async function createFinancialAlertDialog(alertInfo) { // @ts-expect-error const chainId = window.ethereum.networkVersion; - console.log("chainId", chainId); const { to, from, value, data } = params.params[0]; mixpanel.init(MIXPANEL_PROJECT_ID, { debug: true }); @@ -202,7 +187,6 @@ async function createFinancialAlertDialog(alertInfo) { address: to, chain_id: chainId, }); - console.log("contractInfo", contractInfo); if (contractInfo == null) { // TODO decide what to do at this point @@ -217,9 +201,9 @@ async function createFinancialAlertDialog(alertInfo) { proceedButtonClickListener: () => { console.log("proceed btn clicked"); financialAlertDialog.close(); - // continueRequest(); + continueRequest(); // TODO FOR TESTING ONLY - reject(); + // reject(); }, cancelButtonClickListener: () => { financialAlertDialog.close(); @@ -230,8 +214,18 @@ async function createFinancialAlertDialog(alertInfo) { }); }) ).then(() => { - console.log("will complete transaction now"); return metamaskRequest({ ...params }); }); }; + + // FOR TESTING + // createFinancialAlertDialog({ + // contract: "Uniswap V3 Router 0x00...34244 [>]", + // createdOn: new Date().toDateString(), + // drainedAccountsValue: "High", + // transactionsIn24hours: 102, + // transactionsIn30days: 1000, + // cancelButtonClickListener: () => {}, + // proceedButtonClickListener: () => {}, + // }); })(); From 05ce7db8c875e13c218df8c18a566bbbeb851e4e Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 14:22:50 +0530 Subject: [PATCH 21/70] minor fixes --- chrome-extension/src/content.js | 23 +++++++++++++++++++++++ chrome-extension/src/inject.js | 2 ++ 2 files changed, 25 insertions(+) diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index f4541fd..296f660 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -588,3 +588,26 @@ function toggle() { // console.log('message cb', response); // }); } + +window.addEventListener("message", (event) => { + if ( + event.source != window || + event.data == undefined || + typeof event.data.reason != "string" + ) { + return; + } + + if ( + event.data.reason == "runtime-get-url" && + typeof event.data.relativeUrl == "string" + ) { + window.postMessage( + { + for: event.data.relativeUrl, + response: chrome.runtime.getURL(event.data.relativeUrl), + }, + "*" + ); + } +}); diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index a8af090..66c4036 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -19,6 +19,7 @@ function isSendTransactionRequest(params) { * @returns {Promise} */ function fetchContractInfo(basicInfo) { + console.log("fetchContractInfo", basicInfo); return fetch(ContractInfoAPIURL, { method: "POST", headers: { @@ -63,6 +64,7 @@ let financialAlertDialogInnerHtml = ""; * @param {FinancialAlertInfo} alertInfo */ async function createFinancialAlertDialog(alertInfo) { + console.log("createFinancialAlertDialog", alertInfo); if (alertInfo == undefined) { console.error( "createFinancialAlertDialog: alertInfo parameter is required" From 2529a0971d88b9456b9aea9df0ca9c48b5d69d3d Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 14:23:42 +0530 Subject: [PATCH 22/70] minor changes --- chrome-extension/src/fonts.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/chrome-extension/src/fonts.js b/chrome-extension/src/fonts.js index 288e2d2..b77e4d0 100644 --- a/chrome-extension/src/fonts.js +++ b/chrome-extension/src/fonts.js @@ -1,3 +1,4 @@ +// @ts-check const FONT_NAME = "X_Roboto"; const FONT_WEIGHTS = { "400": "./fonts/Roboto-Regular.ttf", @@ -28,9 +29,9 @@ async function chromeRuntimeGetUrlWrapped(url) { ) { // if event.data.for is available, only resolve it if it matches with relativeUrl if (event.data.for && event.data.for != message.relativeUrl) { - return; + console.error("data.for is undefined", event.data); + return ""; } - // Remove the event listener once the response is received window.removeEventListener("message", handleMessage); resolve(event.data.response); @@ -42,6 +43,9 @@ async function chromeRuntimeGetUrlWrapped(url) { // Send the message to the content script window.postMessage(message, "*"); + }).catch((error) => { + console.error(error); + return ""; }); } else { return chrome.runtime.getURL(url); @@ -58,13 +62,14 @@ async function getFonts() { return Promise.all( entries.map(async (entry) => { const [weight, value] = entry; + const url = await chromeRuntimeGetUrlWrapped(value); return `@font-face { font-family: '${FONT_NAME}'; font-style: normal; font-weight: ${weight}; font-display: swap; - src: url(${await chromeRuntimeGetUrlWrapped(value)}) format('ttf'); + src: url(${url}) format('ttf'); }`; }) ) From d498d8a7bb7f0d3539651f45e991d9617988bc86 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 14:27:16 +0530 Subject: [PATCH 23/70] move typedef into .d.ts --- chrome-extension/src/inject.d.ts | 10 ++++++++++ chrome-extension/src/inject.js | 11 +---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts index dd6fb05..3cd0f61 100644 --- a/chrome-extension/src/inject.d.ts +++ b/chrome-extension/src/inject.d.ts @@ -43,3 +43,13 @@ interface BasicContractInfo { } * */ + +interface FinancialAlertInfo { + contract: string; + createdOn: string; + drainedAccountsValue: "High"; + transactionsIn24hours: number; + transactionsIn30days: number; + cancelButtonClickListener: () => void; + proceedButtonClickListener: () => void; +} diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 66c4036..a0c9931 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -51,16 +51,7 @@ function querySelector(shadowRoot, selector) { const financialAlertDialog = document.createElement("dialog"); let financialAlertDialogInnerHtml = ""; -/** - * @typedef FinancialAlertInfo - * @prop {string} contract - * @prop {string} createdOn - * @prop {"High"} drainedAccountsValue - * @prop {number} transactionsIn24hours - * @prop {number} transactionsIn30days - * @prop {() => void} cancelButtonClickListener - * @prop {() => void} proceedButtonClickListener - * + /** * @param {FinancialAlertInfo} alertInfo */ async function createFinancialAlertDialog(alertInfo) { From 952a6d6b551c7d9eb9184c80a0e4c652b1da37c6 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 14:34:06 +0530 Subject: [PATCH 24/70] correctly format date string --- chrome-extension/src/inject.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index a0c9931..2fe8bc7 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -51,7 +51,7 @@ function querySelector(shadowRoot, selector) { const financialAlertDialog = document.createElement("dialog"); let financialAlertDialogInnerHtml = ""; - /** +/** * @param {FinancialAlertInfo} alertInfo */ async function createFinancialAlertDialog(alertInfo) { @@ -143,6 +143,18 @@ async function createFinancialAlertDialog(alertInfo) { financialAlertDialog.show(); } +/** + * Formats a ISO date string (2023-07-06T08:58:10.102Z) --> "06 Jul 2023" + * @param {string} dateString + */ +function formatDate(dateString) { + return new Date(dateString).toLocaleDateString("en-GB", { + year: "numeric", + month: "short", + day: "2-digit", + }); +} + (function () { if (window.ethereum == undefined) { console.warn("Metamask extension is not installed"); @@ -187,7 +199,7 @@ async function createFinancialAlertDialog(alertInfo) { return; } createFinancialAlertDialog({ - createdOn: contractInfo.creationDate, + createdOn: formatDate(contractInfo.creationDate), contract: contractInfo.name, transactionsIn24hours: contractInfo.userCount24hours, transactionsIn30days: contractInfo.userCount30days, From a994a0e6fc2feef5f6e7f6c5949e137c8d3f697c Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 6 Jul 2023 20:47:23 +0530 Subject: [PATCH 25/70] fix: show truncated reciever's address --- chrome-extension/src/inject.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 2fe8bc7..718e994 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -155,6 +155,16 @@ function formatDate(dateString) { }); } +/** + * Formats a long text into truncated format + * @example 0x2ef4a574b72e1f555185afa8a09c6d1a8ac4025c --> 0x2e...025c + * @param {string} text + * @returns {string} + */ +function truncateText(text) { + return text.slice(0, 4).concat("...", text.slice(-4)); +} + (function () { if (window.ethereum == undefined) { console.warn("Metamask extension is not installed"); @@ -200,15 +210,13 @@ function formatDate(dateString) { } createFinancialAlertDialog({ createdOn: formatDate(contractInfo.creationDate), - contract: contractInfo.name, + contract: truncateText(to).concat(" (", contractInfo.name, ")"), transactionsIn24hours: contractInfo.userCount24hours, transactionsIn30days: contractInfo.userCount30days, proceedButtonClickListener: () => { console.log("proceed btn clicked"); financialAlertDialog.close(); continueRequest(); - // TODO FOR TESTING ONLY - // reject(); }, cancelButtonClickListener: () => { financialAlertDialog.close(); From 3a948ca243c85e6e74284bbd5123312cfb2db0eb Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 7 Jul 2023 09:16:55 +0530 Subject: [PATCH 26/70] fix contract name --- chrome-extension/src/inject.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 718e994..6d93a0f 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -208,9 +208,19 @@ function truncateText(text) { continueRequest(); return; } + + let contractDisplay = truncateText(to); + if (contractInfo.name) { + contractDisplay = contractDisplay.concat( + " (", + contractInfo.name, + ")" + ); + } + createFinancialAlertDialog({ createdOn: formatDate(contractInfo.creationDate), - contract: truncateText(to).concat(" (", contractInfo.name, ")"), + contract: contractDisplay, transactionsIn24hours: contractInfo.userCount24hours, transactionsIn30days: contractInfo.userCount30days, proceedButtonClickListener: () => { From d010e1652ca56047a884563455c60b6234fd4c2c Mon Sep 17 00:00:00 2001 From: Venkat Kunisetty Date: Sun, 9 Jul 2023 14:50:58 +0530 Subject: [PATCH 27/70] add contract risk details in api --- server/package.json | 2 + server/src/app.ts | 247 ++++++++++++++++++++++++++++++-------- server/src/db.ts | 10 +- server/src/telegramBot.js | 48 ++++++++ 4 files changed, 254 insertions(+), 53 deletions(-) create mode 100644 server/src/telegramBot.js diff --git a/server/package.json b/server/package.json index bdad001..3e49c37 100644 --- a/server/package.json +++ b/server/package.json @@ -15,10 +15,12 @@ }, "dependencies": { "capture-website": "^2.4.0", + "cors": "^2.8.5", "ethers": "^6.6.2", "express": "^4.18.1", "helmet": "^5.1.0", "node-fetch": "^3.0.0", + "node-telegram-bot-api": "^0.61.0", "pg": "^8.7.3", "pg-native": "^3.0.0", "pg-pool": "^3.5.1", diff --git a/server/src/app.ts b/server/src/app.ts index 2dd4939..22c45a4 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -1,20 +1,20 @@ import express, { json } from 'express'; import helmet from 'helmet'; import { PoolClient } from 'pg'; -import pool from './db'; +import pool, { poolContracts } from './db'; import {ethers} from 'ethers'; import fetch from 'node-fetch'; import { BasicDomainInfo, DomainInfo, DomainScamInfo } from "../../important-types"; const whois = require('whois-json'); +const cors = require('cors'); +const { sendMessage } = require('./telegramBot'); const app = express(); app.use(json()); app.use(helmet()); - -const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; -const ETHEREUM_RPC_URL = process.env.ETHEREUM_RPC_URL; - -const provider = new ethers.JsonRpcProvider(ETHEREUM_RPC_URL); +app.use( + cors(), +); app.post('/domain-info', async (req, res) => { // await captureWebsite.file('https://cryptnesis.com/', 'screenshot.png'); @@ -40,20 +40,37 @@ app.post('/domain-info', async (req, res) => { app.post("/contract-info", async (req, res) => { const address = req.body.address; - - console.log("address", address); + const chain_id = req.body.chain_id; if (!address) { - res.status(400).send({}); - return; + res.status(400).send({error: "Bad params"}); + return; + } + + if (!chain_id || (chain_id != '1' && chain_id != '137')) { + res.status(400).send({error: "Bad params"}); + return; } + const network = chain_id == '1' ? 'ETHEREUM_MAINNET' : 'POLYGON_MAINNET'; + + console.log("address", address); + console.log("chain_id", chain_id); + console.log("network", network); + try { - const client = await pool.connect(); - const output = await getContractInfo(client, address); + const client = await poolContracts.connect(); + const output = await getContractInfo(client, address, network); client.release(); - res.json(output); + // res.json({ + // userCount24hours: 1000, + // userCount30days: 100000, + // creationDate: new Date((new Date().getTime() - 86400000)), + // name: 'Dummy', + // }); + res.json(output); } catch (err) { console.log("get contract details", err); + sendMessage(`Error: /contract-info API:\nAddress: ${address}\nChain ID: ${chain_id}\nError: ${err}`) res.status(500).send({}); } return; @@ -138,10 +155,49 @@ async function getDomainInfo(client: PoolClient, domain: string): Promise { +function getEtherscanEndpoint(chain_id: string) { + if (chain_id == Networks.ETHEREUM_MAINNET.valueOf()) { + return 'https://api.etherscan.io/api' + } else if (chain_id == Networks.POLYGON_MAINNET.valueOf()) { + return 'https://api.polygonscan.com/api' + } else { + throw new Error('Invalid chain id') + } +} + +enum Networks { + ETHEREUM_MAINNET = 'ETHEREUM_MAINNET', + POLYGON_MAINNET = 'POLYGON_MAINNET', +} + +function getEtherscanAPIkey(chain_id: string) { + if (chain_id == Networks.ETHEREUM_MAINNET.valueOf()) { + return process.env.ETHERSCAN_API_KEY + } else if (chain_id == Networks.POLYGON_MAINNET.valueOf()) { + return process.env.POLYGONSCAN_API_KEY + } else { + throw new Error('Invalid chain id') + } +} + +function getProvider(chain_id: string) { + if (chain_id == Networks.ETHEREUM_MAINNET.valueOf()) { + return new ethers.JsonRpcProvider(process.env.ETHEREUM_RPC_URL); + } else if (chain_id == Networks.POLYGON_MAINNET.valueOf()) { + return new ethers.JsonRpcProvider(process.env.POLYGON_RPC_URL); + } else { + throw new Error('Invalid chain id') + } +} + +async function getCreationDate(client: PoolClient, address: string, network: string): Promise<{ + riskRating: string, + feedback: string[], + name: string, + date: string, +}> { try { - - const query = 'SELECT "creationDate" FROM "ContractAddresses" WHERE "address" LIKE $1'; + const query = 'SELECT * FROM "ContractAddresses" WHERE "address" LIKE $1'; var contractData = await client.query(query, [address]); if(contractData.rowCount == 0){ @@ -150,41 +206,124 @@ async function getCreationDate(client: PoolClient, address: string): Promise { +function getRiskRating(contractRecord: any) { + console.log('contractRecord', contractRecord) + if (!contractRecord) { + return { + riskRating: 'NA', + feedback: [] + } + } + if (contractRecord.verified) { + return { + riskRating: 'LOW', + feedback: ["Verified by Vigilance DAO"] + } + } + let riskRating = 'MEDIUM'; + let feedback = []; + if (!contractRecord.contractVerified) { + feedback.push('Contract source code not verified'); + } + // @todo // try proxy detection + let now = new Date(); + if ((now.getTime() - new Date(contractRecord.creationDate).getTime()) < 86400000 * 120) { // 120 days + feedback.push('Contract is newly deployed. Maintain caution.'); + } + if (feedback.length == 0) { + riskRating = 'LOW'; + } + return { + riskRating, + feedback, + } +} + +async function getContractInfo(client: PoolClient, address: string, network: string): Promise { const now = new Date(); var yesterday = new Date(now.setDate(now.getDate() - 1)).getTime() / 1000; @@ -194,18 +333,22 @@ async function getContractInfo(client: PoolClient, address: string): Promise { + let member = allowedMembers[i] + console.log("Sending messaAGE TO: " + member.id) + let bot = botInfo + if (!bot) { + console.log('No telegram token found') + resolve() + return + } + bot.sendMessage(member.id, message, options).then(()=> { + console.log('sent', member.id) + if((i+1) { + resolve() + }).catch((err)=> { + reject(err) + }) + else + resolve() + }).catch((err)=> { + console.error('message sending failed', i, err); + resolve() + }) + }) +} From 97729be8e4b5fb99a548ac4980de9a20e9084e14 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 9 Jul 2023 20:49:23 +0530 Subject: [PATCH 28/70] fix: show risk rating --- chrome-extension/src/inject.d.ts | 5 ++++- chrome-extension/src/inject.js | 11 +++++++---- .../src/prebuild-components/financial-alert.css | 13 +++++++++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts index 3cd0f61..cec0054 100644 --- a/chrome-extension/src/inject.d.ts +++ b/chrome-extension/src/inject.d.ts @@ -15,11 +15,14 @@ interface MetaMaskRequest { method: string; } +type RiskRating = "LOW" | "MEDIUM" | "HIGH"; + interface ContractInfo { userCount24hours: number; userCount30days: number; creationDate: string; name: string; + riskRating: RiskRating; } interface BasicContractInfo { @@ -47,7 +50,7 @@ interface BasicContractInfo { interface FinancialAlertInfo { contract: string; createdOn: string; - drainedAccountsValue: "High"; + drainedAccountsValue: RiskRating; transactionsIn24hours: number; transactionsIn30days: number; cancelButtonClickListener: () => void; diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 6d93a0f..232d088 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -35,7 +35,7 @@ function fetchContractInfo(basicInfo) { } /** - * @type {(shadowRoot: ShadowRoot, selector: string) => E} + * @type {(shadowRoot: ShadowRoot, selector: string) => E} */ function querySelector(shadowRoot, selector) { const element = shadowRoot.querySelector(selector); @@ -135,6 +135,10 @@ async function createFinancialAlertDialog(alertInfo) { alertInfo.transactionsIn24hours.toString(); transactionsIn30daysElement.innerHTML = formattedTransactionsIn30days; drainedAccountsValueElement.innerHTML = alertInfo.drainedAccountsValue; + // color is applied based on this property + drainedAccountsValueElement.dataset["priority"] = + alertInfo.drainedAccountsValue.toLowerCase(); + proceedButton.addEventListener("click", alertInfo.proceedButtonClickListener); closeButton.addEventListener("click", alertInfo.cancelButtonClickListener); @@ -208,7 +212,7 @@ function truncateText(text) { continueRequest(); return; } - + let contractDisplay = truncateText(to); if (contractInfo.name) { contractDisplay = contractDisplay.concat( @@ -232,8 +236,7 @@ function truncateText(text) { financialAlertDialog.close(); reject(new Error("Transaction cancelled by user.")); }, - // TODO - drainedAccountsValue: "High", + drainedAccountsValue: contractInfo.riskRating, }); }) ).then(() => { diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index c5aa777..954a486 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -115,18 +115,27 @@ h4 { flex-direction: column; } .drained-info .value { - color: hsl(0, 100%, 52%); + color: hsl(0, 0%, 47%); font-size: 1.5em; font-weight: 700; margin: auto; } +.drained-info[data-priority="high"] .value { + color: hsl(0, 100%, 52%); +} +.drained-info[data-priority="medium"] .value { + color: hsl(60, 100%, 52%); +} +.drained-info[data-priority="low"] .value { + color: hsl(115, 100%, 42%); +} .bottom-container { display: flex; align-items: center; } -.credits{ +.credits { margin-right: auto; text-align: right; color: hsl(264, 29%, 53%); From 9e979d703c581d109d462542b128bea70f1c343f Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 9 Jul 2023 23:57:58 +0530 Subject: [PATCH 29/70] fix: handle creationDate being null --- chrome-extension/src/inject.d.ts | 4 ++-- chrome-extension/src/inject.js | 17 +++++++++++++++-- .../src/prebuild-components/financial-alert.css | 4 ++++ .../src/prebuild-components/financial-alert.tsx | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts index cec0054..6b3ec94 100644 --- a/chrome-extension/src/inject.d.ts +++ b/chrome-extension/src/inject.d.ts @@ -20,7 +20,7 @@ type RiskRating = "LOW" | "MEDIUM" | "HIGH"; interface ContractInfo { userCount24hours: number; userCount30days: number; - creationDate: string; + creationDate: string | null; name: string; riskRating: RiskRating; } @@ -49,7 +49,7 @@ interface BasicContractInfo { interface FinancialAlertInfo { contract: string; - createdOn: string; + createdOn?: string; drainedAccountsValue: RiskRating; transactionsIn24hours: number; transactionsIn30days: number; diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 232d088..cf74b2e 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -112,6 +112,9 @@ async function createFinancialAlertDialog(alertInfo) { const select = querySelector.bind(null, shadowRoot); const contractInfoElement = select(".contract-info"); + const contractCreatedOnContainerElement = select( + ".contract-created-on-container" + ); const contractCreatedOnElement = select(".contract-created-on"); const transactionsIn24hoursElement = select(".transactions-in-day"); const transactionsIn30daysElement = select(".transactions-in-month"); @@ -130,7 +133,14 @@ async function createFinancialAlertDialog(alertInfo) { } contractInfoElement.innerHTML = alertInfo.contract; - contractCreatedOnElement.innerHTML = alertInfo.createdOn; + + if (alertInfo.createdOn == undefined) { + contractCreatedOnContainerElement.classList.toggle("hidden", true); + } else { + contractCreatedOnContainerElement.classList.toggle("hidden", false); + contractCreatedOnElement.innerHTML = alertInfo.createdOn; + } + transactionsIn24hoursElement.innerHTML = alertInfo.transactionsIn24hours.toString(); transactionsIn30daysElement.innerHTML = formattedTransactionsIn30days; @@ -149,9 +159,12 @@ async function createFinancialAlertDialog(alertInfo) { /** * Formats a ISO date string (2023-07-06T08:58:10.102Z) --> "06 Jul 2023" - * @param {string} dateString + * @param {string | null} dateString */ function formatDate(dateString) { + if (dateString == null) { + return undefined; + } return new Date(dateString).toLocaleDateString("en-GB", { year: "numeric", month: "short", diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 954a486..d118976 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -5,6 +5,10 @@ font-size: 16px; } +.hidden { + display: none; +} + * { box-sizing: border-box; } diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index af24ff5..fcde9bc 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -36,7 +36,7 @@ export default function FinancialAlert() { {/* {"Uniswap V3 Router 0x00...34244 [>]"} */} - + Created on:{" "} Loading... From 736c823fd15a8df21557ab238cd748ec3b8cefde Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 9 Jul 2023 23:59:08 +0530 Subject: [PATCH 30/70] fix: handle count being null --- chrome-extension/src/inject.d.ts | 4 ++-- chrome-extension/src/inject.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts index 6b3ec94..2065cfe 100644 --- a/chrome-extension/src/inject.d.ts +++ b/chrome-extension/src/inject.d.ts @@ -18,8 +18,8 @@ interface MetaMaskRequest { type RiskRating = "LOW" | "MEDIUM" | "HIGH"; interface ContractInfo { - userCount24hours: number; - userCount30days: number; + userCount24hours: number | null; + userCount30days: number | null; creationDate: string | null; name: string; riskRating: RiskRating; diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index cf74b2e..4b9c629 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -238,8 +238,8 @@ function truncateText(text) { createFinancialAlertDialog({ createdOn: formatDate(contractInfo.creationDate), contract: contractDisplay, - transactionsIn24hours: contractInfo.userCount24hours, - transactionsIn30days: contractInfo.userCount30days, + transactionsIn24hours: contractInfo.userCount24hours || 0, + transactionsIn30days: contractInfo.userCount30days || 0, proceedButtonClickListener: () => { console.log("proceed btn clicked"); financialAlertDialog.close(); From fc73cc1dcce804ca13da0a5c7034cf46c304d043 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 10 Jul 2023 00:10:14 +0530 Subject: [PATCH 31/70] fix: color for drained accounts value --- .../src/prebuild-components/financial-alert.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index d118976..30c8f9e 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -124,13 +124,13 @@ h4 { font-weight: 700; margin: auto; } -.drained-info[data-priority="high"] .value { +.drained-info .value[data-priority="high"] { color: hsl(0, 100%, 52%); } -.drained-info[data-priority="medium"] .value { +.drained-info .value[data-priority="medium"] { color: hsl(60, 100%, 52%); } -.drained-info[data-priority="low"] .value { +.drained-info .value[data-priority="low"] { color: hsl(115, 100%, 42%); } From 4871cab373e6dc044b8c8a072b14d3abae94e39d Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 10 Jul 2023 17:35:58 +0530 Subject: [PATCH 32/70] add loader --- chrome-extension/src/inject.js | 91 +++++++-- .../prebuild-components/financial-alert.css | 51 ++++- .../prebuild-components/financial-alert.tsx | 180 +++++++++++------- 3 files changed, 235 insertions(+), 87 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 4b9c629..ec6bc12 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -49,20 +49,15 @@ function querySelector(shadowRoot, selector) { } const financialAlertDialog = document.createElement("dialog"); +const FINANCIAL_ALERT_INNER_DIV_ID = "FINANCIAL_ALERT_INNER_DIV_ID"; +const FINANCIAL_ALERT_INNER_DIV_SELECTOR = `div#${FINANCIAL_ALERT_INNER_DIV_ID}`; +const FINANCIAL_ALERT_IS_LOADING = "isLoading"; let financialAlertDialogInnerHtml = ""; /** - * @param {FinancialAlertInfo} alertInfo + * Creates the financial alert dialog and displays a loader inside */ -async function createFinancialAlertDialog(alertInfo) { - console.log("createFinancialAlertDialog", alertInfo); - if (alertInfo == undefined) { - console.error( - "createFinancialAlertDialog: alertInfo parameter is required" - ); - return; - } - +async function createFinancialAlertDialog() { const INNER_BORDER_RADIUS = 10; const BORDER_WIDTH = 10; const OUTER_BORDER_RADIUS = INNER_BORDER_RADIUS + BORDER_WIDTH; @@ -102,15 +97,76 @@ async function createFinancialAlertDialog(alertInfo) { financialAlertDialogInnerHtml = innerHTMLParts.join(""); } - financialAlertDialog.innerHTML = ""; - const div = document.createElement("div"); - const shadowRoot = div.attachShadow({ mode: "closed" }); - financialAlertDialog.appendChild(div); + let div = financialAlertDialog.querySelector( + FINANCIAL_ALERT_INNER_DIV_SELECTOR + ); + + if (div == null) { + div = document.createElement("div"); + div.id = FINANCIAL_ALERT_INNER_DIV_ID; + financialAlertDialog.appendChild(div); + } + const shadowRoot = div.attachShadow({ mode: "open" }); shadowRoot.innerHTML = financialAlertDialogInnerHtml; + financialAlertDialog.className = "____vigilance-dao-dialog____"; + document.body.appendChild(financialAlertDialog); + const select = querySelector.bind(null, shadowRoot); + const containerElement = select(".container"); + const loadingScreenElement = select(".loading-screen"); + containerElement.dataset[FINANCIAL_ALERT_IS_LOADING] = `${true}`; + + loadingScreenElement.addEventListener("animationend", (event) => { + if (!(event.target instanceof HTMLElement)) { + return; + } + event.target.remove(); + }); + + financialAlertDialog.show(); +} + +/** + * @param {FinancialAlertInfo} alertInfo + */ +function populateFinancialAlertWithData(alertInfo) { + console.log("populateFinancialAlertWithData", alertInfo); + if (alertInfo == undefined) { + console.error( + "populateFinancialAlertWithData: alertInfo parameter is required" + ); + return; + } + if (financialAlertDialog.innerHTML == "") { + console.error( + "populateFinancialAlertWithData: financialAlertDialog haven't been created yet" + ); + return; + } + + const innerDiv = financialAlertDialog.querySelector( + FINANCIAL_ALERT_INNER_DIV_SELECTOR + ); + if (innerDiv == null || !(innerDiv instanceof HTMLDivElement)) { + console.error( + "populateFinancialAlertWithData: financialAlertDialog doesn't have inner div" + ); + return; + } + const shadowRoot = innerDiv.shadowRoot; + if (shadowRoot == null) { + console.error( + "populateFinancialAlertWithData: financialAlertDialog's inner div have closed shadowRoot" + ); + return; + } + + const select = querySelector.bind(null, shadowRoot); + + const containerElement = select(".container"); const contractInfoElement = select(".contract-info"); const contractCreatedOnContainerElement = select( ".contract-created-on-container" @@ -152,9 +208,7 @@ async function createFinancialAlertDialog(alertInfo) { proceedButton.addEventListener("click", alertInfo.proceedButtonClickListener); closeButton.addEventListener("click", alertInfo.cancelButtonClickListener); - financialAlertDialog.className = "____vigilance-dao-dialog____"; - document.body.appendChild(financialAlertDialog); - financialAlertDialog.show(); + containerElement.dataset[FINANCIAL_ALERT_IS_LOADING] = `${false}`; } /** @@ -235,7 +289,8 @@ function truncateText(text) { ); } - createFinancialAlertDialog({ + await createFinancialAlertDialog(); + populateFinancialAlertWithData({ createdOn: formatDate(contractInfo.creationDate), contract: contractDisplay, transactionsIn24hours: contractInfo.userCount24hours || 0, diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 30c8f9e..290d803 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -16,7 +16,7 @@ .container { --icon-size: 70px; - padding: 25px 18px 10px; + padding: 10px; border-radius: var(--inner-border-radius); position: relative; background: hsl(265, 100%, 92%); @@ -27,6 +27,38 @@ margin: var(--border); } +.container .loading-screen { + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + + height: auto; + opacity: 1; + transition: height 0ms 0ms, opacity 600ms 0ms; +} + +.container .data-screen { + overflow: hidden; + height: 0; + opacity: 0; + transition: height 0ms 400ms, opacity 400ms 0ms; +} + +.container[data-is-loading="false"] .loading-screen { + overflow: hidden; + height: 0; + opacity: 0; + transition: height 0ms 400ms, opacity 400ms 0ms; +} + +.container[data-is-loading="false"] .data-screen { + padding: 15px 8px 0; + height: auto; + opacity: 1; + transition: height 0ms 0ms, opacity 600ms 0ms; +} + .basic-info { display: grid; grid-template-rows: repeat(2, auto); @@ -139,7 +171,7 @@ h4 { align-items: center; } -.credits { +.credits { margin-right: auto; text-align: right; color: hsl(264, 29%, 53%); @@ -147,8 +179,8 @@ h4 { font-weight: 700; } -.bottom-container button { - margin-left: 5px; +.bottom-container button { + margin-left: 5px; border-radius: 3px; padding: 5px 10px; font-weight: 600; @@ -165,3 +197,14 @@ h4 { opacity: 1; border-color: hsl(265deg 94% 75%); } + +@keyframes rotate-360 { + to { + rotate: 180deg; + } +} + +.icon-tabler-loader { + animation: rotate-360 infinite 0.6s linear; + margin-right: 10px; +} diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index fcde9bc..e13ef0c 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -6,79 +6,129 @@ export const config = { export default function FinancialAlert() { return ( -
-
-

You are about to interact with

- - - - - - - - -

- - Contract:{" "} - - Loading... - {/* {"Uniswap V3 Router 0x00...34244 [>]"} */} - +

+
+ + + + + + + + + + + + Gathering data about receiver... +
+
+
+

You are about to interact with

+ + + + + + + - - Created on:{" "} - - Loading... - {/* {"19 Now 2022"} */} +

+ + Contract:{" "} + + Loading... + {/* {"Uniswap V3 Router 0x00...34244 [>]"} */} + - -

-
+ + Created on:{" "} + + Loading... + {/* {"19 Now 2022"} */} + + +

+
-
-
-

Transactions

-
- 0{/* {"101"} */} - 24 hours +
+
+

Transactions

+
+ 0{/* {"101"} */} + 24 hours +
+ + + +
+ 0{/* 15k */} + 30 days +
- - - -
- 0{/* 15k */} - 30 days +
+

Drained accounts

+ + + ———{/* {"HIGH"} */} + + + + + + + + + +
-
-

Drained accounts

-
———{/* {"HIGH"} */}
-
-
-
-
Powered by VigialnceDAO
+
+
Powered by VigialnceDAO
- - + + +
); From 66722506768dd57de687e791cb22cd4e98069439 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 10 Jul 2023 17:36:37 +0530 Subject: [PATCH 33/70] update code for testing --- chrome-extension/src/inject.js | 41 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index ec6bc12..be3b465 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -190,10 +190,12 @@ function populateFinancialAlertWithData(alertInfo) { contractInfoElement.innerHTML = alertInfo.contract; - if (alertInfo.createdOn == undefined) { - contractCreatedOnContainerElement.classList.toggle("hidden", true); - } else { - contractCreatedOnContainerElement.classList.toggle("hidden", false); + // hide date if undefined + contractCreatedOnContainerElement.classList.toggle( + "hidden", + alertInfo.createdOn == undefined + ); + if (alertInfo.createdOn != undefined) { contractCreatedOnElement.innerHTML = alertInfo.createdOn; } @@ -311,15 +313,26 @@ function truncateText(text) { return metamaskRequest({ ...params }); }); }; +})(); +// (async () => { // FOR TESTING - // createFinancialAlertDialog({ - // contract: "Uniswap V3 Router 0x00...34244 [>]", - // createdOn: new Date().toDateString(), - // drainedAccountsValue: "High", - // transactionsIn24hours: 102, - // transactionsIn30days: 1000, - // cancelButtonClickListener: () => {}, - // proceedButtonClickListener: () => {}, - // }); -})(); +// if (location.hostname != "sahithyandev.github.io") { +// return; +// } +// await createFinancialAlertDialog(); + +// await new Promise((resolve) => { +// setTimeout(resolve, 5000); +// }); + +// populateFinancialAlertWithData({ +// contract: "Uniswap V3 Router 0x00...34244 [>]", +// createdOn: formatDate(new Date().toString()), +// drainedAccountsValue: "HIGH", +// transactionsIn24hours: 102, +// transactionsIn30days: 1000, +// cancelButtonClickListener: () => {}, +// proceedButtonClickListener: () => {}, +// }); +// })(); From 810d1d5bb5767a327a4172b0de0ae9a727b8f422 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 10 Jul 2023 17:45:34 +0530 Subject: [PATCH 34/70] do some validation for api response --- chrome-extension/src/inject.d.ts | 10 ++++++++++ chrome-extension/src/inject.js | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts index 2065cfe..352963f 100644 --- a/chrome-extension/src/inject.d.ts +++ b/chrome-extension/src/inject.d.ts @@ -1,3 +1,5 @@ +type Overwrite = Pick> & U; + interface ETH_SendTransactionRequestParamsItem { gas: string; value: string; @@ -25,6 +27,14 @@ interface ContractInfo { riskRating: RiskRating; } +type ContractInfoJsonResponse = Overwrite< + ContractInfo, + { + userCount24hours: string | null | number; + userCount30days: string | null | number; + } +>; + interface BasicContractInfo { address: string; chain_id: string; diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index be3b465..957eac2 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -28,6 +28,39 @@ function fetchContractInfo(basicInfo) { body: JSON.stringify(basicInfo), }) .then((response) => response.json()) + .then( + /** + * @param {ContractInfoJsonResponse} data + */ + (data) => { + /** + * @type {ContractInfo} + */ + const d = { + userCount24hours: 0, + userCount30days: 0, + creationDate: null, + feedback: [], + name: "NA", + riskRating: "MEDIUM", + }; + + if (data.userCount24hours) { + d.userCount24hours = + typeof data.userCount24hours == "string" + ? parseInt(data.userCount24hours) + : data.userCount24hours; + } + if (data.userCount30days) { + d.userCount30days = + typeof data.userCount30days == "string" + ? parseInt(data.userCount30days) + : data.userCount30days; + } + + return d; + } + ) .catch((error) => { console.error(error); return null; From 658ade7df26f088f18d189dfa15dc61111bc0373 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 10 Jul 2023 18:15:03 +0530 Subject: [PATCH 35/70] show feedback in expandable section --- chrome-extension/src/inject.d.ts | 2 + chrome-extension/src/inject.js | 31 +++++++++++++++ .../prebuild-components/financial-alert.css | 38 +++++++++++++++++-- .../prebuild-components/financial-alert.tsx | 9 ++++- 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts index 352963f..807baea 100644 --- a/chrome-extension/src/inject.d.ts +++ b/chrome-extension/src/inject.d.ts @@ -25,6 +25,7 @@ interface ContractInfo { creationDate: string | null; name: string; riskRating: RiskRating; + feedback: string[]; } type ContractInfoJsonResponse = Overwrite< @@ -65,4 +66,5 @@ interface FinancialAlertInfo { transactionsIn30days: number; cancelButtonClickListener: () => void; proceedButtonClickListener: () => void; + feedback: string[]; } diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 957eac2..87019bc 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -208,6 +208,9 @@ function populateFinancialAlertWithData(alertInfo) { const transactionsIn24hoursElement = select(".transactions-in-day"); const transactionsIn30daysElement = select(".transactions-in-month"); const drainedAccountsValueElement = select(".drained-info .value"); + const feedbackIconElement = select(".feedback-icon"); + const feedbackContainerElement = select(".feedback-container"); + const feedbackListElement = select(".feedback-list"); const proceedButton = select("#proceed-btn"); const closeButton = select("#close-btn"); @@ -240,6 +243,33 @@ function populateFinancialAlertWithData(alertInfo) { drainedAccountsValueElement.dataset["priority"] = alertInfo.drainedAccountsValue.toLowerCase(); + // hide feedback-icon if feedback is empty + feedbackIconElement.classList.toggle( + "hidden", + alertInfo.feedback.length == 0 + ); + feedbackIconElement.addEventListener("click", () => { + if (!(feedbackContainerElement instanceof HTMLDetailsElement)) { + return; + } + + feedbackContainerElement.open = !feedbackContainerElement.open; + }); + feedbackContainerElement.classList.toggle( + "hidden", + alertInfo.feedback.length == 0 + ); + + if (alertInfo.feedback.length != 0) { + feedbackListElement.innerHTML = alertInfo.feedback + .map((i) => `
  • ${i}
  • `) + .join(""); + } + + // feedbackIconElement.dataset["tooltipText"] = alertInfo.feedback + // .map((item) => "- ".concat(item)) + // .join("\n"); + proceedButton.addEventListener("click", alertInfo.proceedButtonClickListener); closeButton.addEventListener("click", alertInfo.cancelButtonClickListener); @@ -340,6 +370,7 @@ function truncateText(text) { reject(new Error("Transaction cancelled by user.")); }, drainedAccountsValue: contractInfo.riskRating, + feedback: contractInfo.feedback, }); }) ).then(() => { diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 290d803..9db1089 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -21,8 +21,7 @@ position: relative; background: hsl(265, 100%, 92%); font-family: Roboto; - max-width: 540px; - min-width: 470px; + width: 470px; overflow: hidden; margin: var(--border); } @@ -150,11 +149,15 @@ h4 { display: flex; flex-direction: column; } + +.drained-info > span { + margin: auto; +} + .drained-info .value { color: hsl(0, 0%, 47%); font-size: 1.5em; font-weight: 700; - margin: auto; } .drained-info .value[data-priority="high"] { color: hsl(0, 100%, 52%); @@ -165,10 +168,17 @@ h4 { .drained-info .value[data-priority="low"] { color: hsl(115, 100%, 42%); } +.drained-info .icon { + height: 16px; + width: 16px; + margin-left: 10px; + cursor: pointer; +} .bottom-container { display: flex; align-items: center; + margin-top: 5px; } .credits { @@ -190,7 +200,7 @@ h4 { border: 2px solid rgb(187 187 187); background: none; opacity: 0.7; - transition: opacity .2s ease-in-out, border-color .2s ease-in-out; + transition: opacity 0.2s ease-in-out, border-color 0.2s ease-in-out; } .bottom-container button:hover { @@ -208,3 +218,23 @@ h4 { animation: rotate-360 infinite 0.6s linear; margin-right: 10px; } + +summary { + opacity: 0.7; + cursor: pointer; + padding: 3px 0; + width: fit-content; +} + +summary::marker { + color:#5500cc; +} + +details[open] summary { + opacity: 1; +} + +ul { + list-style: "- " outside; + padding-left: 13px; +} diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index e13ef0c..a5449a6 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -123,11 +123,18 @@ export default function FinancialAlert() {
    +
    + View feedbacks +
      +
      +
      Powered by VigialnceDAO
      - +
      From 3c9241b982ef076fa7a7136b636d63c6e3216cd5 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 10 Jul 2023 19:14:42 +0530 Subject: [PATCH 36/70] minor fixes --- chrome-extension/bundle-config.mjs | 20 +++++++++++-------- chrome-extension/src/fonts.js | 1 - chrome-extension/src/inject.js | 5 +++-- .../prebuild-components/financial-alert.css | 2 +- chrome-extension/src/prebuild.tsx | 1 - 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/chrome-extension/bundle-config.mjs b/chrome-extension/bundle-config.mjs index 5a0b36b..e7fb172 100644 --- a/chrome-extension/bundle-config.mjs +++ b/chrome-extension/bundle-config.mjs @@ -110,16 +110,20 @@ const prebuildOptions = { { name: "run-prebuild-script", setup(build) { - build.onStart(async () => { + build.onStart(() => { console.log("Building", prebuildScript); - try { - await rm("./build/static", { - recursive: true, - force: true + const directory = "./build/static"; + console.log("Removing", directory); + + return rm(directory, { + recursive: true, + force: true, + }) + .then(() => console.log("Removing", directory, "done")) + .catch((_e) => { + console.error("Couldn't remove", directory); + console.error(_e); }); - } catch (_e) { - console.log(_e); - } }); build.onEnd(async (result) => { if (result.metafile == undefined) { diff --git a/chrome-extension/src/fonts.js b/chrome-extension/src/fonts.js index b77e4d0..4fe6814 100644 --- a/chrome-extension/src/fonts.js +++ b/chrome-extension/src/fonts.js @@ -29,7 +29,6 @@ async function chromeRuntimeGetUrlWrapped(url) { ) { // if event.data.for is available, only resolve it if it matches with relativeUrl if (event.data.for && event.data.for != message.relativeUrl) { - console.error("data.for is undefined", event.data); return ""; } // Remove the event listener once the response is received diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 87019bc..4d8b3ce 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -334,6 +334,8 @@ function truncateText(text) { chainId, }); + await createFinancialAlertDialog(); + const contractInfo = await fetchContractInfo({ address: to, chain_id: chainId, @@ -346,7 +348,7 @@ function truncateText(text) { } let contractDisplay = truncateText(to); - if (contractInfo.name) { + if (contractInfo.name && contractInfo.name != "NA") { contractDisplay = contractDisplay.concat( " (", contractInfo.name, @@ -354,7 +356,6 @@ function truncateText(text) { ); } - await createFinancialAlertDialog(); populateFinancialAlertWithData({ createdOn: formatDate(contractInfo.creationDate), contract: contractDisplay, diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 9db1089..2cd2f6b 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -6,7 +6,7 @@ } .hidden { - display: none; + display: none !important; } * { diff --git a/chrome-extension/src/prebuild.tsx b/chrome-extension/src/prebuild.tsx index 66df844..2812b1c 100644 --- a/chrome-extension/src/prebuild.tsx +++ b/chrome-extension/src/prebuild.tsx @@ -91,7 +91,6 @@ export function run() { const component = componentModule.default; const rendered = renderToStaticMarkup(component()); const baseName = pascalCaseToHypenedCase(component.name); - console.log(baseName); const jsFile = componentModule.config?.jsFile || "../prebuild-components/".concat(baseName.concat(".js")); From c8c830408cf1d0d76839a3477ac1a0c60ec01fe9 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Tue, 11 Jul 2023 22:26:04 +0530 Subject: [PATCH 37/70] fix: medium text color issue --- chrome-extension/src/prebuild-components/financial-alert.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 2cd2f6b..f060e05 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -163,7 +163,7 @@ h4 { color: hsl(0, 100%, 52%); } .drained-info .value[data-priority="medium"] { - color: hsl(60, 100%, 52%); + color: hsl(37, 100%, 52%); } .drained-info .value[data-priority="low"] { color: hsl(115, 100%, 42%); From 1999c0614ff3f394733978344cc1c8d14a09ef44 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Wed, 12 Jul 2023 10:49:55 +0530 Subject: [PATCH 38/70] fix wrong info shown --- chrome-extension/src/inject.js | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 4d8b3ce..d05c76a 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -39,23 +39,32 @@ function fetchContractInfo(basicInfo) { const d = { userCount24hours: 0, userCount30days: 0, - creationDate: null, - feedback: [], - name: "NA", - riskRating: "MEDIUM", + creationDate: data.creationDate || null, + feedback: Array.isArray(data.feedback) ? data.feedback : [], + name: typeof data.name == "string" ? data.name : "NA", + riskRating: data.riskRating || "MEDIUM", }; if (data.userCount24hours) { - d.userCount24hours = - typeof data.userCount24hours == "string" - ? parseInt(data.userCount24hours) - : data.userCount24hours; + let value = 0; + if (typeof data.userCount24hours == "number") { + value = data.userCount24hours; + } else if (typeof data.userCount24hours == "string") { + value = parseInt(data.userCount24hours); + if (Number.isNaN(value)) value = 0; + } + + d.userCount24hours = value; } if (data.userCount30days) { - d.userCount30days = - typeof data.userCount30days == "string" - ? parseInt(data.userCount30days) - : data.userCount30days; + let value = 0; + if (typeof data.userCount30days == "number") { + value = data.userCount30days; + } else if (typeof data.userCount30days == "string") { + value = parseInt(data.userCount30days); + if (Number.isNaN(value)) value = 0; + } + d.userCount30days = value; } return d; From 48639f859c726d0d9e2f2764b0c17f8dbe4a6147 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Wed, 12 Jul 2023 10:53:02 +0530 Subject: [PATCH 39/70] fix issue with font --- chrome-extension/src/prebuild-components/financial-alert.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index f060e05..80bb4b5 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -20,12 +20,15 @@ border-radius: var(--inner-border-radius); position: relative; background: hsl(265, 100%, 92%); - font-family: Roboto; width: 470px; overflow: hidden; margin: var(--border); } +.container * { + font-family: "X_Roboto", sans-serif; +} + .container .loading-screen { font-weight: 500; display: flex; From 520098cb6b0aeb9f1b618f2a765108fa1777b426 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Wed, 12 Jul 2023 10:54:00 +0530 Subject: [PATCH 40/70] fix financial-alert not showing again error --- chrome-extension/src/inject.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index d05c76a..ac8037f 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -149,7 +149,7 @@ async function createFinancialAlertDialog() { financialAlertDialog.appendChild(div); } - const shadowRoot = div.attachShadow({ mode: "open" }); + const shadowRoot = div.shadowRoot || div.attachShadow({ mode: "open" }); shadowRoot.innerHTML = financialAlertDialogInnerHtml; financialAlertDialog.className = "____vigilance-dao-dialog____"; @@ -373,10 +373,12 @@ function truncateText(text) { proceedButtonClickListener: () => { console.log("proceed btn clicked"); financialAlertDialog.close(); + document.body.removeChild(financialAlertDialog); continueRequest(); }, cancelButtonClickListener: () => { financialAlertDialog.close(); + document.body.removeChild(financialAlertDialog); reject(new Error("Transaction cancelled by user.")); }, drainedAccountsValue: contractInfo.riskRating, From 447242ad084628a1f05ff307c93f13ce7591e911 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Wed, 12 Jul 2023 10:55:49 +0530 Subject: [PATCH 41/70] remove tooltip code (not used) --- chrome-extension/src/inject.js | 4 ---- chrome-extension/src/prebuild-components/financial-alert.tsx | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index ac8037f..419a3ef 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -275,10 +275,6 @@ function populateFinancialAlertWithData(alertInfo) { .join(""); } - // feedbackIconElement.dataset["tooltipText"] = alertInfo.feedback - // .map((item) => "- ".concat(item)) - // .join("\n"); - proceedButton.addEventListener("click", alertInfo.proceedButtonClickListener); closeButton.addEventListener("click", alertInfo.cancelButtonClickListener); diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index a5449a6..68e853d 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -100,7 +100,7 @@ export default function FinancialAlert() { ———{/* {"HIGH"} */} - + Date: Wed, 12 Jul 2023 10:56:33 +0530 Subject: [PATCH 42/70] rename close button -> cancel button for better clarity --- chrome-extension/src/prebuild-components/financial-alert.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index 68e853d..b9d84ca 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -133,7 +133,7 @@ export default function FinancialAlert() {
      From f70e7f92e038855a49544d5dacf817bf605fa950 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 13 Jul 2023 09:21:20 +0530 Subject: [PATCH 43/70] fix fonts by using google fonts --- chrome-extension/src/inject.js | 35 +++++++++++++++---- .../prebuild-components/financial-alert.css | 6 ++-- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 419a3ef..aaa6938 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -96,6 +96,24 @@ const FINANCIAL_ALERT_INNER_DIV_SELECTOR = `div#${FINANCIAL_ALERT_INNER_DIV_ID}` const FINANCIAL_ALERT_IS_LOADING = "isLoading"; let financialAlertDialogInnerHtml = ""; +function getFontLinks() { + const link1 = document.createElement("link"); + link1.rel = "preconnect"; + link1.href = "https://fonts.googleapis.com"; + + const link2 = document.createElement("link"); + link2.rel = "preconnect"; + link2.href = "https://fonts.gstatic.com"; + link2.crossOrigin = ""; + + const link3 = document.createElement("link"); + link3.rel = "stylesheet"; + link3.href = + "https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap"; + + return [link1, link2, link3]; +} + /** * Creates the financial alert dialog and displays a loader inside */ @@ -105,6 +123,11 @@ async function createFinancialAlertDialog() { const OUTER_BORDER_RADIUS = INNER_BORDER_RADIUS + BORDER_WIDTH; if (financialAlertDialogInnerHtml == "") { + const fontLinks = getFontLinks(); + document.head.appendChild(fontLinks[0]); + document.head.appendChild(fontLinks[1]); + document.head.appendChild(fontLinks[2]); + financialAlertDialog.style.borderRadius = OUTER_BORDER_RADIUS.toString().concat("px"); financialAlertDialog.style.zIndex = "10000"; @@ -121,15 +144,13 @@ async function createFinancialAlertDialog() { /** * @type {string[]} */ - const innerHTMLParts = new Array(3).fill(""); - // part 0 -> fonts - // part 1 -> css variables - // part 2 -> financial-alert component content + const innerHTMLParts = new Array(2).fill(""); + // part 0 -> css variables + // part 1 -> financial-alert component content - innerHTMLParts[0] = ``; - innerHTMLParts[1] = ``; + innerHTMLParts[0] = ``; const url = await chromeRuntimeGetUrlWrapped("static/financial-alert.html"); - innerHTMLParts[2] = await fetch(url) + innerHTMLParts[1] = await fetch(url) .then((response) => response.text()) .catch((e) => { console.error("Error while loading html from financial-alert.html", e); diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 80bb4b5..56d5226 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -26,7 +26,7 @@ } .container * { - font-family: "X_Roboto", sans-serif; + font-family: "Roboto", sans-serif; } .container .loading-screen { @@ -142,7 +142,7 @@ h4 { } .transaction-info-item span:first-child { font-size: 2em; - font-weight: 600; + font-weight: 500; text-align: center; margin-bottom: 4px; } @@ -196,7 +196,7 @@ h4 { margin-left: 5px; border-radius: 3px; padding: 5px 10px; - font-weight: 600; + font-weight: 500; cursor: pointer; font-size: 1em; line-height: 19px; From 3b445f38cf0d0d344675313554e508a0bc8d3888 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 13 Jul 2023 09:42:22 +0530 Subject: [PATCH 44/70] pass address through checksum function before fetching --- chrome-extension/src/inject.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index aaa6938..59fdb63 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -361,9 +361,14 @@ function truncateText(text) { }); await createFinancialAlertDialog(); + if (window._ethers == undefined) { + console.warn("window._ethers is undefined."); + continueRequest(); + return; + } const contractInfo = await fetchContractInfo({ - address: to, + address: window._ethers.utils.getAddress(to), chain_id: chainId, }); From 9ed40fe00c033ccde986d2d93411a07e3eed1598 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 13 Jul 2023 09:43:16 +0530 Subject: [PATCH 45/70] fix types --- chrome-extension/src/inject.d.ts | 11 +++++++++++ chrome-extension/src/inject.js | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts index 807baea..d498df9 100644 --- a/chrome-extension/src/inject.d.ts +++ b/chrome-extension/src/inject.d.ts @@ -1,5 +1,16 @@ type Overwrite = Pick> & U; +import { ethers } from "ethers"; + +declare global { + interface Window { + /** + * Provided by `ethers` npm package or the referenced `ethers.umd.min.js` file. + */ + _ethers?: typeof ethers; + } +} + interface ETH_SendTransactionRequestParamsItem { gas: string; value: string; diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 59fdb63..f04ed29 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -7,16 +7,16 @@ const ContractInfoAPIURL = "https://8md2nmtej9.execute-api.ap-northeast-1.amazonaws.com/contract-info"; /** - * @param {MetaMaskRequest} params - * @returns {params is ETH_SendTransactionRequest} + * @param {import("./inject").MetaMaskRequest} params + * @returns {params is import("./inject").ETH_SendTransactionRequest} */ function isSendTransactionRequest(params) { return params.method == "eth_sendTransaction"; } /** - * @param {BasicContractInfo} basicInfo - * @returns {Promise} + * @param {import("./inject").BasicContractInfo} basicInfo + * @returns {Promise} */ function fetchContractInfo(basicInfo) { console.log("fetchContractInfo", basicInfo); @@ -30,11 +30,12 @@ function fetchContractInfo(basicInfo) { .then((response) => response.json()) .then( /** - * @param {ContractInfoJsonResponse} data + * @param {import("./inject").ContractInfoJsonResponse} data */ (data) => { + console.log("contract info fetched", data); /** - * @type {ContractInfo} + * @type {import("./inject").ContractInfo} */ const d = { userCount24hours: 0, @@ -67,6 +68,7 @@ function fetchContractInfo(basicInfo) { d.userCount30days = value; } + console.log("contract info formatted", d); return d; } ) @@ -193,7 +195,7 @@ async function createFinancialAlertDialog() { } /** - * @param {FinancialAlertInfo} alertInfo + * @param {import("./inject").FinancialAlertInfo} alertInfo */ function populateFinancialAlertWithData(alertInfo) { console.log("populateFinancialAlertWithData", alertInfo); @@ -336,7 +338,7 @@ function truncateText(text) { // @ts-expect-error const metamaskRequest = window.ethereum.request; /** - * @param {MetaMaskRequest} params + * @param {import("./inject").MetaMaskRequest} params */ // @ts-expect-error window.ethereum.request = (params) => { From 9ada1c29dc397878a72fb52c497dc03beb6f1237 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 13 Jul 2023 10:00:11 +0530 Subject: [PATCH 46/70] change Drained Accounts to Risk --- chrome-extension/src/prebuild-components/financial-alert.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index b9d84ca..9b9446e 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -95,7 +95,7 @@ export default function FinancialAlert() {
      -

      Drained accounts

      +

      Risk

      ———{/* {"HIGH"} */} From ba15c7d7e502dce3f293cef232fba7518c046651 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 13 Jul 2023 11:18:46 +0530 Subject: [PATCH 47/70] minor fixes --- chrome-extension/src/inject.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index f04ed29..419b653 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -1,7 +1,7 @@ // @ts-check const mixpanel = require("mixpanel-browser"); const { MIXPANEL_PROJECT_ID } = require("../privateenv"); -const { getFonts, chromeRuntimeGetUrlWrapped } = require("./fonts"); +const { chromeRuntimeGetUrlWrapped } = require("./fonts"); const ContractInfoAPIURL = "https://8md2nmtej9.execute-api.ap-northeast-1.amazonaws.com/contract-info"; @@ -363,14 +363,17 @@ function truncateText(text) { }); await createFinancialAlertDialog(); + console.log("reciever's address (provided by metamask)", to); if (window._ethers == undefined) { console.warn("window._ethers is undefined."); continueRequest(); return; } + const checksumAddress = window._ethers.utils.getAddress(to); + console.log("reciever's address (checksum)", checksumAddress); const contractInfo = await fetchContractInfo({ - address: window._ethers.utils.getAddress(to), + address: checksumAddress, chain_id: chainId, }); @@ -380,7 +383,7 @@ function truncateText(text) { return; } - let contractDisplay = truncateText(to); + let contractDisplay = truncateText(checksumAddress); if (contractInfo.name && contractInfo.name != "NA") { contractDisplay = contractDisplay.concat( " (", @@ -397,12 +400,12 @@ function truncateText(text) { proceedButtonClickListener: () => { console.log("proceed btn clicked"); financialAlertDialog.close(); - document.body.removeChild(financialAlertDialog); + financialAlertDialog.remove(); continueRequest(); }, cancelButtonClickListener: () => { financialAlertDialog.close(); - document.body.removeChild(financialAlertDialog); + financialAlertDialog.remove(); reject(new Error("Transaction cancelled by user.")); }, drainedAccountsValue: contractInfo.riskRating, From 78178aa43bef3d6eb6413a99e49081c87a1e2ae4 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 13 Jul 2023 12:01:54 +0530 Subject: [PATCH 48/70] show alert for only selected chains --- chrome-extension/src/inject.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 419b653..3ce5ba5 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -329,6 +329,8 @@ function truncateText(text) { return text.slice(0, 4).concat("...", text.slice(-4)); } +const SUPPORTED_CHAINS = ["1", "137"]; + (function () { if (window.ethereum == undefined) { console.warn("Metamask extension is not installed"); @@ -361,6 +363,11 @@ function truncateText(text) { "data": data, chainId, }); + + if (!SUPPORTED_CHAINS.includes(chainId)) { + continueRequest(); + return; + } await createFinancialAlertDialog(); console.log("reciever's address (provided by metamask)", to); From bd9b268f6183a2a0a34eb9f3bce37dce580a9488 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Tue, 18 Jul 2023 13:16:16 +0530 Subject: [PATCH 49/70] chore(server): add .env-example --- server/.env-example | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 server/.env-example diff --git a/server/.env-example b/server/.env-example new file mode 100644 index 0000000..578be29 --- /dev/null +++ b/server/.env-example @@ -0,0 +1,4 @@ +# Postgres Database URL +DATABASE_URL="" + +DISCORD_BOT_TOKEN="" From 7ad93fa52eabfe760c291d8a18d38d24d0a13e87 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Wed, 19 Jul 2023 12:09:18 +0530 Subject: [PATCH 50/70] feat: integrate forta api --- chrome-extension/src/inject.d.ts | 17 ++++ chrome-extension/src/inject.js | 151 ++++++++++++++++++++++--------- 2 files changed, 126 insertions(+), 42 deletions(-) diff --git a/chrome-extension/src/inject.d.ts b/chrome-extension/src/inject.d.ts index d498df9..c658d1c 100644 --- a/chrome-extension/src/inject.d.ts +++ b/chrome-extension/src/inject.d.ts @@ -39,6 +39,23 @@ interface ContractInfo { feedback: string[]; } +interface FortaApiLabelItem { + label: { + label: string, + entity: string, + metadata: string[], + }, + createdAt: string +} + +interface FortaApiResonseData { + data: { + labels: { + labels: FortaApiLabelItem[] + } + } +} + type ContractInfoJsonResponse = Overwrite< ContractInfo, { diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 3ce5ba5..f13f52e 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -5,6 +5,8 @@ const { chromeRuntimeGetUrlWrapped } = require("./fonts"); const ContractInfoAPIURL = "https://8md2nmtej9.execute-api.ap-northeast-1.amazonaws.com/contract-info"; + +const FortaAPIUrl = "https://api.forta.network/graphql"; /** * @param {import("./inject").MetaMaskRequest} params @@ -20,58 +22,123 @@ function isSendTransactionRequest(params) { */ function fetchContractInfo(basicInfo) { console.log("fetchContractInfo", basicInfo); - return fetch(ContractInfoAPIURL, { + + const contractInfoApiFetch = fetch(ContractInfoAPIURL, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(basicInfo), - }) - .then((response) => response.json()) - .then( + }).then( + /** + * @returns {Promise} + */ + async (response) => { + if (response.ok) { + return response.json(); + } + throw await response.text(); + } + ); + + const fortaApiFetch = fetch(FortaAPIUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: `query Labels($input: LabelsInput) { + labels(input: $input) { + labels { + label { + label + entity + metadata + } + createdAt + } + } +}`, + variables: { + "input": { + "labels": ["scammer"], + "entities": [basicInfo.address], + }, + }, + }), + }).then( + /** + * @returns {Promise} + */ + async (response) => { + if (response.ok) { + return response.json(); + } + throw await response.text(); + } + ); + + return Promise.allSettled([contractInfoApiFetch, fortaApiFetch]) + .then((resolvedArr) => { + if (resolvedArr[0].status == "rejected") { + console.error("request to /contract-info failed"); + return null; + } + + const data = resolvedArr[0].value; + console.log("contract info fetched", data); + + let fortaApiResult = null; + resolvedArr[1].status == "fulfilled" + ? resolvedArr[1].value + : resolvedArr[1].reason; + if (resolvedArr[1].status == "fulfilled") { + fortaApiResult = resolvedArr[1].value; + } else { + console.error("request to Forta API failed", resolvedArr[1].reason); + } + /** - * @param {import("./inject").ContractInfoJsonResponse} data + * @type {import("./inject").ContractInfo} */ - (data) => { - console.log("contract info fetched", data); - /** - * @type {import("./inject").ContractInfo} - */ - const d = { - userCount24hours: 0, - userCount30days: 0, - creationDate: data.creationDate || null, - feedback: Array.isArray(data.feedback) ? data.feedback : [], - name: typeof data.name == "string" ? data.name : "NA", - riskRating: data.riskRating || "MEDIUM", - }; - - if (data.userCount24hours) { - let value = 0; - if (typeof data.userCount24hours == "number") { - value = data.userCount24hours; - } else if (typeof data.userCount24hours == "string") { - value = parseInt(data.userCount24hours); - if (Number.isNaN(value)) value = 0; - } - - d.userCount24hours = value; - } - if (data.userCount30days) { - let value = 0; - if (typeof data.userCount30days == "number") { - value = data.userCount30days; - } else if (typeof data.userCount30days == "string") { - value = parseInt(data.userCount30days); - if (Number.isNaN(value)) value = 0; - } - d.userCount30days = value; + const d = { + userCount24hours: 0, + userCount30days: 0, + creationDate: data.creationDate || null, + feedback: Array.isArray(data.feedback) ? data.feedback : [], + name: typeof data.name == "string" ? data.name : "NA", + riskRating: data.riskRating || "MEDIUM", + }; + + if (data.userCount24hours) { + let value = 0; + if (typeof data.userCount24hours == "number") { + value = data.userCount24hours; + } else if (typeof data.userCount24hours == "string") { + value = parseInt(data.userCount24hours); + if (Number.isNaN(value)) value = 0; } - console.log("contract info formatted", d); - return d; + d.userCount24hours = value; } - ) + if (data.userCount30days) { + let value = 0; + if (typeof data.userCount30days == "number") { + value = data.userCount30days; + } else if (typeof data.userCount30days == "string") { + value = parseInt(data.userCount30days); + if (Number.isNaN(value)) value = 0; + } + d.userCount30days = value; + } + if (fortaApiResult && fortaApiResult.data.labels.labels.length > 0) { + // labelled as scammer by Forta + d.feedback.push("Labelled as scammer by Forta"); + } + + console.log("contract info formatted", d); + return d; + }) .catch((error) => { console.error(error); return null; From b83424c669c6693d80ad8e978c13929a5315dda1 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Wed, 19 Jul 2023 14:06:42 +0530 Subject: [PATCH 51/70] chore: update packages to make discordjs work --- server/package.json | 2 +- server/serverless.yml | 2 +- server/src/app.ts | 2 +- server/tsconfig.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/package.json b/server/package.json index bdad001..71d3abc 100644 --- a/server/package.json +++ b/server/package.json @@ -50,7 +50,7 @@ "lint-staged": "^12.5.0", "prettier": "^2.6.2", "rimraf": "^3.0.2", - "serverless": "^3.19.0", + "serverless": "^3.33.0", "serverless-bundle": "^5.3.0", "serverless-dotenv-plugin": "^4.0.1", "serverless-offline": "^8.8.0", diff --git a/server/serverless.yml b/server/serverless.yml index cbbb470..d2e0ba7 100644 --- a/server/serverless.yml +++ b/server/serverless.yml @@ -14,7 +14,7 @@ custom: provider: name: aws - runtime: nodejs14.x + runtime: nodejs16.x region: ap-northeast-1 memorySize: 512 timeout: 10 diff --git a/server/src/app.ts b/server/src/app.ts index 2dd4939..aca62a3 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -221,4 +221,4 @@ if (process.env.SERVER_TYPE == 'express' && !isListening) { isListening = true; console.log('server listening on 4000') }) -} \ No newline at end of file +} diff --git a/server/tsconfig.json b/server/tsconfig.json index 73e4883..cee22c0 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "lib": ["es2017"], + "lib": ["es2022"], "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, @@ -19,7 +19,7 @@ "allowUnreachableCode": false, "noFallthroughCasesInSwitch": true, - "target": "es2017", + "target": "es2020", "outDir": "dist", "declaration": true, "sourceMap": true, From 66b20835dd399f120e6cb537ba93613639a711ad Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Wed, 19 Jul 2023 23:49:55 +0530 Subject: [PATCH 52/70] add /submit-contract-report --- server/package.json | 1 + server/src/app.ts | 107 +++++++++++++++++++++++++++++++ server/src/discord-client.ts | 119 +++++++++++++++++++++++++++++++++++ server/src/types.ts | 8 +++ 4 files changed, 235 insertions(+) create mode 100644 server/src/discord-client.ts create mode 100644 server/src/types.ts diff --git a/server/package.json b/server/package.json index 71d3abc..80bdc3e 100644 --- a/server/package.json +++ b/server/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "capture-website": "^2.4.0", + "discord-api-types": "^0.37.49", "ethers": "^6.6.2", "express": "^4.18.1", "helmet": "^5.1.0", diff --git a/server/src/app.ts b/server/src/app.ts index aca62a3..d1189c6 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -5,6 +5,8 @@ import pool from './db'; import {ethers} from 'ethers'; import fetch from 'node-fetch'; import { BasicDomainInfo, DomainInfo, DomainScamInfo } from "../../important-types"; +import client from './discord-client'; +import { ContractReport__Unknown, ContractReport, ContractReport_FraudType } from './types'; const whois = require('whois-json'); const app = express(); @@ -59,6 +61,111 @@ app.post("/contract-info", async (req, res) => { return; }); +function validateUnknownFinancialReport( + reportBody: ContractReport__Unknown +): asserts reportBody is ContractReport { + if (reportBody == undefined) { + throw "body.report is undefined"; + } + if (typeof reportBody != "object") { + throw "Invalid value passed for body.report"; + } + if (typeof reportBody.fraudType == undefined) { + throw "body.report.fraudType is undefined"; + } + if (typeof reportBody.info == undefined) { + throw "body.report.info is undefined"; + } + if (reportBody.info == "") { + throw "body.report.info is empty"; + } +} + +const TARGET_GUILD_NAME = "sahithyan's server"; +const TARGET_CHANNEL_NAME = "lkfj"; + +/** + * phishing -> Phishing + * financial-loss -> Financial Loss + */ +function fraudTypeDisplayText(fraudType: ContractReport_FraudType): string { + return fraudType + .split("-") + .map((word) => { + // capitalize first letter + return word.charAt(0).toUpperCase().concat(fraudType.slice(1)); + }) + .join(" "); +} + +app.post("/submit-contract-report", async (req, res) => { + const userError = (message: string) => { + res.status(400).send(message); + }; + const internalError = (message: string) => { + console.error(message); + res.status(500).send("Internal Error Occured"); + } + + const reportBody: ContractReport__Unknown = req.body.report; + + try { + validateUnknownFinancialReport(reportBody); + } catch (errorMesage) { + return userError( + typeof errorMesage == "string" ? errorMesage : "Unknown error occured." + ); + } + + if ( + reportBody.fraudType != "phishing" && + reportBody.fraudType != "financial-loss" + ) { + console.warn( + "Invalid value passed for body.report.fraudType", + reportBody.fraudType + ); + } + + const guilds = await client.guilds(); + if (guilds == null) { + return internalError("Couldn't get guilds of the bot"); + } + + const targetGuild = guilds.find((guild) => guild.name == TARGET_GUILD_NAME); + if (targetGuild == undefined) { + return internalError(`The bot haven't joined ${TARGET_GUILD_NAME} guild yet.`); + } + + const channels = await client.guildChannels(targetGuild.id); + if (channels == null) { + return internalError(`Couldn't get channels of ${targetGuild.name} guild`); + } + + const targetChannel = channels.find( + (channel) => channel.name == TARGET_CHANNEL_NAME + ); + if (targetChannel == undefined) { + return internalError( + `${TARGET_CHANNEL_NAME} wasn't found in ${TARGET_GUILD_NAME} guild.` + ); + } + + await client.sendMessage(targetChannel.id, { + embeds: [ + { + title: "Contract Report Received", + description: [ + `*Report type*: ${fraudTypeDisplayText(reportBody.fraudType)}`, + `*Report description*: ${reportBody.info}`, + ].join("\n"), + }, + ], + }); + + res.status(200).send("done"); +}); + app.use((_, res, _2) => { res.status(404).json({ error: 'NOT FOUND' }); }); diff --git a/server/src/discord-client.ts b/server/src/discord-client.ts new file mode 100644 index 0000000..e7b4171 --- /dev/null +++ b/server/src/discord-client.ts @@ -0,0 +1,119 @@ +import type { APIChannel, APIPartialGuild, APIEmbed, APIAllowedMentions, APIMessageReference, APIMessageComponent, Snowflake } from "discord-api-types/v10"; +import fetch, { HeadersInit, RequestInit } from "node-fetch"; + +/** + * Created from + * https://discord.com/developers/docs/resources/channel#create-message + */ +interface APIMessage_Create { + /** + * Message contents (up to 2000 characters) + */ + content?: string; + /** + * Can be used to verify a message was sent (up to 25 characters). Value will appear in the Message Create event. + */ + nonce?: number | string; + /** true if this is a TTS message */ + tts?: boolean; + /** Up to 11 rich embeds (up to 6000 characters) */ + embeds?: APIEmbed[]; + /** Allowed mentions for the message */ + allowed_mentions?: APIAllowedMentions[]; + /** Include to make your message a reply */ + message_reference?: APIMessageReference; + /** Components to include with the message */ + components?: APIMessageComponent[]; + /** IDs of up to 4 stickers in the server to send in the message */ + sticker_ids?: Snowflake[]; + /** Contents of the file being sent. See Uploading Files */ + "files[n]"?: unknown[]; + /** JSON-encoded body of non-file params, only for multipart/form-data requests. See Uploading Files */ + payload_json?: string; + /** Attachment objects with filename and description. See Uploading Files */ + attachments?: unknown[]; + /** Message flags combined as a bitfield (only SUPPRESS_EMBEDS and SUPPRESS_NOTIFICATIONS can be set */ + flags?: number; +} + +const BASIC_URL = "https://discord.com/api/v10"; + +const token = process.env.DISCORD_BOT_TOKEN || ""; + +if (token == "") { + console.error("ERROR: env.DISCORD_BOT_TOKEN is undefined"); + process.exit(2); +} + +/** + * To learn more, refer to the documentation of the Discord API + * https://discord.com/developers/docs/resources + */ +export class DiscordClientRestWrapper { + token: string; + + constructor(token: string) { + this.token = token; + } + + private fetch( + route: string, + method: "GET" | "POST" = "GET", + init?: RequestInit + ) { + const headers: HeadersInit = { + "Authorization": `Bot ${this.token}`, + }; + + if (method == "POST") { + headers["Content-Type"] = "application/json"; + } + + return fetch(BASIC_URL.concat(route), { + method, + headers, + ...init, + }).then((response) => { + return response.json(); + }); + } + + private get(route: string): Promise { + return this.fetch(route, "GET") as Promise; + } + private post>(route: string, body: T) { + return this.fetch(route, "POST", { + body: JSON.stringify(body), + }); + } + + guilds() { + return this.get("/users/@me/guilds") + .then((guilds) => { + return guilds; + }) + .catch((err) => { + console.error(err); + }); + } + + guildChannels(guildId: string) { + return this.get(`/guilds/${guildId}/channels`) + .then((channels) => { + return channels; + }) + .catch((err) => { + console.error(err); + }); + } + + sendMessage(channelId: string, message: APIMessage_Create) { + return this.post(`/channels/${channelId}/messages`, { + tts: false, + ...message + }); + } +} + +const client = new DiscordClientRestWrapper(token); +export default client; diff --git a/server/src/types.ts b/server/src/types.ts new file mode 100644 index 0000000..3e22655 --- /dev/null +++ b/server/src/types.ts @@ -0,0 +1,8 @@ +export type ContractReport_FraudType = "phishing" | "financial-loss" | string; + +export type ContractReport = { + fraudType: ContractReport_FraudType; + info: string; +} + +export type ContractReport__Unknown = Partial | undefined; From 0b0221d6d7b4e957fad028bf661db8d274e395e1 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Thu, 20 Jul 2023 00:31:39 +0530 Subject: [PATCH 53/70] add report form to financial-alert --- chrome-extension/src/inject.js | 155 ++++++++++++++++++ .../prebuild-components/financial-alert.css | 62 ++++++- .../prebuild-components/financial-alert.tsx | 39 +++++ 3 files changed, 254 insertions(+), 2 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index f13f52e..125cba0 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -8,6 +8,12 @@ const ContractInfoAPIURL = const FortaAPIUrl = "https://api.forta.network/graphql"; +const env = { + host: "https://8md2nmtej9.execute-api.ap-northeast-1.amazonaws.com", + // For developement + // host: "http://localhost:4000", +}; + /** * @param {import("./inject").MetaMaskRequest} params * @returns {params is import("./inject").ETH_SendTransactionRequest} @@ -145,6 +151,40 @@ function fetchContractInfo(basicInfo) { }); } +/** + * @param {import("../../server/src/types").ContractReport} report + * @returns {Promise} string --> error message, undefined --> successful + */ +function submitFinancialReport(report) { + return fetch(env.host.concat("/submit-contract-report"), { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + report, + }), + }) + .then((response) => { + if (response.ok) { + return; + } + return response.text(); + }) + .then( + (value) => ( + console.log("submitFinancialReport return value", value), value + ) + ) + .catch((err) => { + console.log("submitFinancialReport error", err); + if (err instanceof Error) { + return `${err.name}: ${err.message}`; + } + return "Unknown error occured"; + }); +} + /** * @type {(shadowRoot: ShadowRoot, selector: string) => E} */ @@ -312,8 +352,20 @@ function populateFinancialAlertWithData(alertInfo) { const feedbackListElement = select(".feedback-list"); const proceedButton = select("#proceed-btn"); + const reportButton = select("#report-btn"); const closeButton = select("#close-btn"); + const formElement = select("#report-form"); + const fraudTypeSelectElement = select("select[name=fraud-type]"); + const phishingInfoSectionContainer = select("div[data-show-if=phishing]"); + const phishingInfoElement = select("textarea[name=phishing-info]"); + const financialLossInfoSectionContainer = select( + "div[data-show-if=financial-loss]" + ); + const financialLossInfoElement = select("textarea[name=financial-loss-info]"); + const formResponseMessageElement = select(".form-response-message"); + const formSubmitButton = select("button.submit"); + let formattedTransactionsIn30days = alertInfo.transactionsIn30days.toString(); if (alertInfo.transactionsIn30days >= 1000) { formattedTransactionsIn30days = Math.floor( @@ -367,6 +419,109 @@ function populateFinancialAlertWithData(alertInfo) { proceedButton.addEventListener("click", alertInfo.proceedButtonClickListener); closeButton.addEventListener("click", alertInfo.cancelButtonClickListener); + reportButton.addEventListener("click", () => { + formElement.classList.toggle("hidden"); + }); + + if (!(formElement instanceof HTMLFormElement)) { + console.error(formElement); + throw new Error( + "formElement is expected to be a form element." + ); + } + + if (!(fraudTypeSelectElement instanceof HTMLSelectElement)) { + console.error(fraudTypeSelectElement); + throw new Error( + "fraudTypeSelectElement is expected to be a select element." + ); + } + if (!(financialLossInfoElement instanceof HTMLTextAreaElement)) { + console.error(financialLossInfoElement); + throw new Error( + "financialLossInfoElement is expected to be a textarea element." + ); + } + if (!(phishingInfoElement instanceof HTMLTextAreaElement)) { + console.error(phishingInfoElement); + throw new Error( + "phishingInfoElement is expected to be a textarea element." + ); + } + if (!(formSubmitButton instanceof HTMLButtonElement)) { + console.error(formSubmitButton); + throw new Error( + "formSubmitButton is expected to be a button element." + ); + } + + /** + * @param {import("../../server/src/types").ContractReport_FraudType} sectionId + */ + function displayInfoSectionInForm(sectionId) { + if (sectionId != "phishing" && sectionId != "financial-loss") { + console.error( + "displayInfoSectionInForm: invalid value for sectionId", + sectionId + ); + return; + } + + phishingInfoSectionContainer.classList.toggle( + "hidden", + // false, + sectionId != "phishing" + ); + financialLossInfoSectionContainer.classList.toggle( + "hidden", + // false + sectionId != "financial-loss" + ); + } + + /** + * @param {string} message + * @param {"error" | "success"} type + */ + function showFormResponseMessage(message, type) { + formResponseMessageElement.classList.toggle("error", type == "error"); + formResponseMessageElement.classList.toggle("success", type == "success"); + + formResponseMessageElement.innerText = message; + } + + displayInfoSectionInForm(fraudTypeSelectElement.value); + + fraudTypeSelectElement.addEventListener("change", () => { + displayInfoSectionInForm(fraudTypeSelectElement.value); + }); + + formElement.addEventListener("submit", (event) => { + event.preventDefault(); + + let info = ""; + // phishingInfoElement.value; + switch(fraudTypeSelectElement.value) { + case "financial-loss": + info = financialLossInfoElement.value; + break; + case "phishing": + info = phishingInfoElement.value; + break; + default: + console.warn("unknown value for fraud type", fraudTypeSelectElement.value); + } + + if (info == "") { + showFormResponseMessage("Provide some information about the fraud", "error"); + return; + } + + submitFinancialReport({ + fraudType: fraudTypeSelectElement.value, + info, + }); + }); containerElement.dataset[FINANCIAL_ALERT_IS_LOADING] = `${false}`; } diff --git a/chrome-extension/src/prebuild-components/financial-alert.css b/chrome-extension/src/prebuild-components/financial-alert.css index 56d5226..df84a44 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.css +++ b/chrome-extension/src/prebuild-components/financial-alert.css @@ -192,7 +192,7 @@ h4 { font-weight: 700; } -.bottom-container button { +button { margin-left: 5px; border-radius: 3px; padding: 5px 10px; @@ -206,7 +206,7 @@ h4 { transition: opacity 0.2s ease-in-out, border-color 0.2s ease-in-out; } -.bottom-container button:hover { +button:hover { opacity: 1; border-color: hsl(265deg 94% 75%); } @@ -241,3 +241,61 @@ ul { list-style: "- " outside; padding-left: 13px; } + +#report-form { + display: flex; + flex-direction: column; + gap: 8px; +} +#report-form h2 { + margin: 5px 0; +} + +#report-form div[data-show-if] { + margin-top: 6px; +} + +#report-form label { + font-weight: 500; + display: block; + margin-bottom: 4px; +} + +select { + border-radius: 4px; + padding: 4px 6px; + width: 100%; +} + +select, option { + cursor: pointer; +} + +#report-form select, +#report-form textarea { + background-color: hsl(265 100% 90%); +} + +#report-form textarea { + width: 100%; + border-radius: 4px; + padding: 2px 4px; + resize: vertical; + font-size: .9em; +} + +#report-form button { + margin-left: auto; +} + +.form-response-message.error { + color: hsl(0, 100%, 52%); +} + +.form-response-message.success { + color: hsl(133deg 100% 33%) +} + +textarea:invalid { + color: hsl(0, 100%, 52%); +} diff --git a/chrome-extension/src/prebuild-components/financial-alert.tsx b/chrome-extension/src/prebuild-components/financial-alert.tsx index 9b9446e..71fd2bb 100644 --- a/chrome-extension/src/prebuild-components/financial-alert.tsx +++ b/chrome-extension/src/prebuild-components/financial-alert.tsx @@ -132,10 +132,49 @@ export default function FinancialAlert() {
      Powered by VigialnceDAO
      +
      + +
      +

      Report

      +
      + + +
      + +
      + +