From 802d5bb2f2e7158f021c74ca911f982e402b25ac Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 6 Aug 2023 20:47:44 +0530 Subject: [PATCH 01/52] fix: warning on start --- webapp/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/package.json b/webapp/package.json index c1a5071..0978385 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -61,6 +61,7 @@ "node": "16.20.0" }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "assert": "^2.0.0", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", From b930abe29929423fdd38592a18b390d40796bd55 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 6 Aug 2023 20:48:00 +0530 Subject: [PATCH 02/52] fix: style issue in navbar --- webapp/src/Navbar.tsx | 87 ++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/webapp/src/Navbar.tsx b/webapp/src/Navbar.tsx index db2bfcf..12b12f8 100644 --- a/webapp/src/Navbar.tsx +++ b/webapp/src/Navbar.tsx @@ -39,6 +39,42 @@ function Navbar() { } const mobileNav = useDisclosure(); + + const CasesButton: typeof Button = (props) => { + return ( + + ); + }; + + const NavMenuButton: typeof MenuButton = (props) => { + return ( + } + style={{ color: "inherit" }} + {...props} + > + {selectedPolygonValue} + + ); + }; const MobileNavContent = ( - + - } - > - {selectedPolygonValue} - + - + - } - > - {selectedPolygonValue} - + Date: Mon, 7 Aug 2023 20:11:45 +0530 Subject: [PATCH 03/52] fix: theming issue --- webapp/src/index.css | 3 +++ webapp/src/theme/theme.js | 14 +++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/webapp/src/index.css b/webapp/src/index.css index 9a112bf..c65e9c3 100644 --- a/webapp/src/index.css +++ b/webapp/src/index.css @@ -20,3 +20,6 @@ code { #dropdownMenu { } +body { + min-height: 100vh; +} diff --git a/webapp/src/theme/theme.js b/webapp/src/theme/theme.js index 800f2ef..4386bbe 100644 --- a/webapp/src/theme/theme.js +++ b/webapp/src/theme/theme.js @@ -1,7 +1,15 @@ import { extendTheme, theme as base } from "@chakra-ui/react"; export const theme = extendTheme({ - fonts: { - Jost: `Jost, ${base.fonts.body}`, - }, + fonts: { + Jost: `Jost, ${base.fonts.body}`, + }, + styles: { + global: { + body: { + backgroundColor: "#04051C", + color: "white" + }, + }, + }, }); From 0c90bbcfd5e771e3136e838790da3a872729208a Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 7 Aug 2023 20:27:09 +0530 Subject: [PATCH 04/52] feat: add /extension-installed page --- webapp/src/assets/feature-alerts.webp | Bin 0 -> 22088 bytes .../src/assets/feature-report-and-earn.webp | Bin 0 -> 17882 bytes .../assets/feature-transaction-analysis.webp | Bin 0 -> 20544 bytes webapp/src/index.tsx | 4 +- webapp/src/pages/extensionInstalled.tsx | 144 ++++++++++++++++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 webapp/src/assets/feature-alerts.webp create mode 100644 webapp/src/assets/feature-report-and-earn.webp create mode 100644 webapp/src/assets/feature-transaction-analysis.webp create mode 100644 webapp/src/pages/extensionInstalled.tsx diff --git a/webapp/src/assets/feature-alerts.webp b/webapp/src/assets/feature-alerts.webp new file mode 100644 index 0000000000000000000000000000000000000000..0d0a36d090300380a929a2cec43d2cd233045c15 GIT binary patch literal 22088 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 JYa9YMumG+n;IjY# literal 0 HcmV?d00001 diff --git a/webapp/src/assets/feature-transaction-analysis.webp b/webapp/src/assets/feature-transaction-analysis.webp new file mode 100644 index 0000000000000000000000000000000000000000..7e0b9cb0c534ab169aaf9db1f4b64f2920096a3f GIT binary patch literal 20544 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 /> } /> } /> + } /> diff --git a/webapp/src/pages/extensionInstalled.tsx b/webapp/src/pages/extensionInstalled.tsx new file mode 100644 index 0000000..af06419 --- /dev/null +++ b/webapp/src/pages/extensionInstalled.tsx @@ -0,0 +1,144 @@ +import { + Flex, + Heading, + Button, + Image, + Text, + Link, + Box, + Grid, +} from "@chakra-ui/react"; + +import logo from "../assets/icon128.png"; + +import featureAlerts from "../assets/feature-alerts.webp"; +import featureReportAndEarn from "../assets/feature-report-and-earn.webp"; +import featureTransactionAnalysis from "../assets/feature-transaction-analysis.webp"; + +const MAX_WIDTH = "min(92vw, 72rem)"; + +interface FeatureCardProps { + imageSrc: string; + description: string; +} + +function FeatureCard(props: FeatureCardProps) { + return + + + + {props.description} + +} + +export default function ExtensionInstalled() { + return ( + <> + + + + Thank you for installing our extension + + + + + + + + + Follow us on{" "} + + Twitter/X + + + + + + Features + + + + + + + + + ); +} From b042c639fb5df4ecd946cc740e3631daa1d82874 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 7 Aug 2023 20:27:33 +0530 Subject: [PATCH 05/52] fix: hide nav bar on /extension-installed page --- webapp/src/Navbar.tsx | 9 ++++++++- webapp/src/index.tsx | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/webapp/src/Navbar.tsx b/webapp/src/Navbar.tsx index 12b12f8..2fd59a3 100644 --- a/webapp/src/Navbar.tsx +++ b/webapp/src/Navbar.tsx @@ -18,7 +18,7 @@ import React, { useState } from "react"; import { useAccount, useConnect, useDisconnect } from "wagmi"; import { InjectedConnector } from "wagmi/connectors/injected"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useLocation } from "react-router-dom"; import MenuIcon from "@mui/icons-material/Menu"; import { useScroll } from "framer-motion"; import { ExpandMore } from "@mui/icons-material"; @@ -26,6 +26,8 @@ import polygon_icon from "assets/polygon_logo_wo_text.svg"; function Navbar() { const navigate = useNavigate(); + const location = useLocation(); + const { address, isConnected } = useAccount(); const [selectedPolygonValue, setSelectedPolygonValue] = useState("Polygon Mumbai"); @@ -39,6 +41,11 @@ function Navbar() { } const mobileNav = useDisclosure(); + + console.log("location", location); + if (location.pathname == "/extension-installed") { + return null; + } const CasesButton: typeof Button = (props) => { return ( diff --git a/webapp/src/index.tsx b/webapp/src/index.tsx index c457805..271413b 100644 --- a/webapp/src/index.tsx +++ b/webapp/src/index.tsx @@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client"; import "./index.css"; import reportWebVitals from "./reportWebVitals"; // import Navigation from './Navigation'; -import { BrowserRouter as Router, Route, Routes, useMatches } from "react-router-dom"; +import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import { configureChains, createClient, chain, WagmiConfig } from "wagmi"; import { alchemyProvider } from "wagmi/providers/alchemy"; import { publicProvider } from "wagmi/providers/public"; From 3b92d97892c9d7e0457b606bd1f7a8adae2b3bd7 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 7 Aug 2023 20:44:16 +0530 Subject: [PATCH 06/52] feat: open /extension-installed for users on install --- chrome-extension/src/background.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index 454e91a..11527d4 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -765,3 +765,16 @@ function takeScreenshot(tab) { ); }); } + +/** + * @param {chrome.runtime.InstalledDetails} details + */ +function onInstalled(details) { + chrome.tabs.create({ + url: `https://vigilancedao.org/extension-installed?reason=${details.reason}` + }) +} + +chrome.runtime.onInstalled.addListener( + onInstalled +); From c630e04a0102940091277cbc417466831d34ce34 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Mon, 7 Aug 2023 20:49:45 +0530 Subject: [PATCH 07/52] fix: discord invite link --- webapp/src/pages/extensionInstalled.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/pages/extensionInstalled.tsx b/webapp/src/pages/extensionInstalled.tsx index af06419..6ffbd3c 100644 --- a/webapp/src/pages/extensionInstalled.tsx +++ b/webapp/src/pages/extensionInstalled.tsx @@ -66,7 +66,7 @@ export default function ExtensionInstalled() { @@ -111,10 +101,8 @@ export default function ExtensionInstalled() { color: "whiteAlpha.600", bgColor: "#3c0191", }} - style={{ - boxShadow: "0px 4px 156px rgba(140, 0, 251, 0.25)", - color: "white", - }} + boxShadow= "0px 4px 156px rgba(140, 0, 251, 0.25)" + color= "white" > See how it works @@ -132,10 +120,8 @@ export default function ExtensionInstalled() { Features From 2b43523dfa4e2e5bf360fdc028a667af6cd93b34 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sat, 19 Aug 2023 10:17:28 +0530 Subject: [PATCH 36/52] chore: enable typing for telegramBot.js --- server/package.json | 1 + server/src/routes/contractInfo.ts | 2 +- server/src/telegramBot.js | 103 +++++++++++++++++++++--------- server/tsconfig.json | 2 +- 4 files changed, 76 insertions(+), 32 deletions(-) diff --git a/server/package.json b/server/package.json index 0fb8240..27c1511 100644 --- a/server/package.json +++ b/server/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@types/cookie-parser": "^1.4.3", + "@types/node-telegram-bot-api": "^0.61.7", "capture-website": "^2.4.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", diff --git a/server/src/routes/contractInfo.ts b/server/src/routes/contractInfo.ts index e1fc0e4..7e75462 100644 --- a/server/src/routes/contractInfo.ts +++ b/server/src/routes/contractInfo.ts @@ -4,7 +4,7 @@ import {ethers} from 'ethers'; import fetch from 'node-fetch'; import { poolContracts } from "../db"; -const { sendMessage } = require('../telegramBot'); +import { sendMessage } from "../telegramBot.js"; enum Networks { ETHEREUM_MAINNET = 'ETHEREUM_MAINNET', diff --git a/server/src/telegramBot.js b/server/src/telegramBot.js index c37848e..ba5ecc2 100644 --- a/server/src/telegramBot.js +++ b/server/src/telegramBot.js @@ -1,4 +1,5 @@ -const TelegramBot = require('node-telegram-bot-api'); +// @ts-check +const TelegramBot = require("node-telegram-bot-api"); const TELEGRAM_TOKEN = process.env.TELEGRAM_TOKEN; const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID; @@ -8,38 +9,80 @@ if (!TELEGRAM_CHAT_ID) { // Create a bot that uses 'polling' to fetch new updates +/** + * @type {TelegramBot | null} + */ let botInfo = null; if (TELEGRAM_TOKEN) { - botInfo = new TelegramBot(TELEGRAM_TOKEN, {polling: false}); + botInfo = new TelegramBot(TELEGRAM_TOKEN, { polling: false }); } else { - console.warn('No telegram token found. No failure messages will be sent. [Not to worry in dev]') + console.warn( + "No telegram token found. No failure messages will be sent. [Not to worry in dev]" + ); } +/** + * @typedef Member + * @prop {number} id + * @prop {string} name + */ -let allowedMembers = [{id: parseInt(TELEGRAM_CHAT_ID), name: 'abc'}] - -export function sendMessage(message, alertType='infoAlert', options={}, members = allowedMembers, i=0) { - return new Promise((resolve, reject)=> { - 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() - }) - }) +/** + * @type {Member[]} + */ +let allowedMembers = []; + +if (TELEGRAM_CHAT_ID) { + allowedMembers = [{ id: parseInt(TELEGRAM_CHAT_ID), name: "abc" }]; +} + +/** + * @param {string} message + * @returns {Promise} + */ +export function sendMessage( + message, + alertType = "infoAlert", + options = {}, + members = allowedMembers, + i = 0 +) { + return new Promise((resolve, reject) => { + let member = members[i]; + + if (member == undefined) { + resolve(); + return; + } + + 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(() => { + if (member == undefined) return; + + console.log("sent", member.id); + if (i + 1 < members.length) { + // TODO sendBoth is not defined? + // sendBoth(message, alertType, options, allowedMembers, i + 1) + // .then(() => { + // resolve(); + // }) + // .catch((err) => { + // reject(err); + // }); + } else { + resolve(); + } + }) + .catch((err) => { + console.error("message sending failed", i, err); + resolve(); + }); + }); } diff --git a/server/tsconfig.json b/server/tsconfig.json index cee22c0..44131cb 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -26,7 +26,7 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "allowJs": false, + "allowJs": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, From 3f79c1b4808200f40b87eb2286d004864a38fb6f Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 20 Aug 2023 09:41:38 +0530 Subject: [PATCH 37/52] extract utility functions from background.js --- chrome-extension/src/background.js | 70 +----------------------- chrome-extension/src/utils/background.js | 69 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 69 deletions(-) create mode 100644 chrome-extension/src/utils/background.js diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index 3dac2db..4174504 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -5,6 +5,7 @@ import { API_ENDPOINT, DOMAIN } from "../constants"; import { sendEvent } from "./utils"; +import { updateActionBadge, getStorageKey } from "./utils/background"; // ! For production uncomment these lines console.log = function(){}; @@ -27,67 +28,6 @@ const env = { "https://api.thegraph.com/subgraphs/name/venkatteja/vigilancedao", }; -/** - * @typedef ActionBadgeValues - * @prop {string} [text] - * - * - * @param {"loading" | "scam" | "legit" | "warning" | "reset" | "error"} key - * @param {ActionBadgeValues} [values] - */ -function updateActionBadge(key, values) { - if (key != "warning" && typeof values != "undefined") { - console.warn("Values object only has effect when key is 'warning'"); - } - - if (key == "loading") { - chrome.action.setIcon({ - path: { 16: "/images/icon16.png", 32: "/images/icon32.png" }, - }); - chrome.action.setBadgeText({ text: "..." }); - chrome.action.setBadgeBackgroundColor({ color: "yellow" }); - } else if (key == "scam") { - chrome.action.setIcon({ - path: { - 19: "/images/alerticon19-red.png", - 38: "/images/alerticon38-red.png", - }, - }); - chrome.action.setBadgeText({ text: "❌" }); - chrome.action.setBadgeBackgroundColor({ color: "#f96c6c" }); - } else if (key == "legit") { - chrome.action.setIcon({ - path: { 16: "/images/icon16.png", 32: "/images/icon32.png" }, - }); - chrome.action.setBadgeText({ text: "✔️" }); - chrome.action.setBadgeBackgroundColor({ color: "#05ed05" }); - } else if (key == "warning") { - chrome.action.setIcon({ - path: { - 19: "/images/alerticon19-red.png", - 38: "/images/alerticon38-red.png", - }, - }); - chrome.action.setBadgeText({ text: values?.text || "1" }); - chrome.action.setBadgeBackgroundColor({ color: "#f96c6c" }); - } else if (key == "reset") { - chrome.action.setIcon({ - path: { 16: "/images/icon16.png", 32: "/images/icon32.png" }, - }); - chrome.action.setBadgeText({ text: "0" }); - chrome.action.setBadgeBackgroundColor({ color: "#05ed05" }); - } else if (key == "error") { - chrome.action.setIcon({ - path: { 16: "/images/icon16.png", 32: "/images/icon32.png" }, - }); - chrome.action.setBadgeText({ text: "⚠️" }); - chrome.action.setBadgeBackgroundColor({ color: "#000000" }); - } else { - console.error(`Invalid key for updateActionBadge: ${key}`); - return; - } -} - /** * TODO * @param {any[]} columns @@ -99,14 +39,6 @@ function searchColumnIndex(columns, column) { }); } -/** - * Returns a unique key for a specific url - * @param {string} url - */ -function getStorageKey(url) { - return `vigil__${url}`; -} - let inMemoryStorage = {}; /** * @type {string | null} diff --git a/chrome-extension/src/utils/background.js b/chrome-extension/src/utils/background.js new file mode 100644 index 0000000..fad5dd4 --- /dev/null +++ b/chrome-extension/src/utils/background.js @@ -0,0 +1,69 @@ + +/** + * @typedef ActionBadgeValues + * @prop {string} [text] + * + * + * @param {"loading" | "scam" | "legit" | "warning" | "reset" | "error"} key + * @param {ActionBadgeValues} [values] + */ +export function updateActionBadge(key, values) { + if (key != "warning" && typeof values != "undefined") { + console.warn("Values object only has effect when key is 'warning'"); + } + + if (key == "loading") { + chrome.action.setIcon({ + path: { 16: "/images/icon16.png", 32: "/images/icon32.png" }, + }); + chrome.action.setBadgeText({ text: "..." }); + chrome.action.setBadgeBackgroundColor({ color: "yellow" }); + } else if (key == "scam") { + chrome.action.setIcon({ + path: { + 19: "/images/alerticon19-red.png", + 38: "/images/alerticon38-red.png", + }, + }); + chrome.action.setBadgeText({ text: "❌" }); + chrome.action.setBadgeBackgroundColor({ color: "#f96c6c" }); + } else if (key == "legit") { + chrome.action.setIcon({ + path: { 16: "/images/icon16.png", 32: "/images/icon32.png" }, + }); + chrome.action.setBadgeText({ text: "✔️" }); + chrome.action.setBadgeBackgroundColor({ color: "#05ed05" }); + } else if (key == "warning") { + chrome.action.setIcon({ + path: { + 19: "/images/alerticon19-red.png", + 38: "/images/alerticon38-red.png", + }, + }); + chrome.action.setBadgeText({ text: values?.text || "1" }); + chrome.action.setBadgeBackgroundColor({ color: "#f96c6c" }); + } else if (key == "reset") { + chrome.action.setIcon({ + path: { 16: "/images/icon16.png", 32: "/images/icon32.png" }, + }); + chrome.action.setBadgeText({ text: "0" }); + chrome.action.setBadgeBackgroundColor({ color: "#05ed05" }); + } else if (key == "error") { + chrome.action.setIcon({ + path: { 16: "/images/icon16.png", 32: "/images/icon32.png" }, + }); + chrome.action.setBadgeText({ text: "⚠️" }); + chrome.action.setBadgeBackgroundColor({ color: "#000000" }); + } else { + console.error(`Invalid key for updateActionBadge: ${key}`); + return; + } +} + +/** + * Returns a unique key for a specific url + * @param {string} url + */ +export function getStorageKey(url) { + return `vigil__${url}`; +} From dcec0b0144e9502839fa1fc3f98a14957e76f35d Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 20 Aug 2023 11:46:58 +0530 Subject: [PATCH 38/52] fix: wrong event name for update --- chrome-extension/src/background.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index 4174504..cb96dd6 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -737,8 +737,10 @@ function takeScreenshot(tab) { chrome.runtime.onInstalled.addListener((details) => { console.log('onInstalled', details); + + if (details.reason == "chrome_update" || details.reason == "shared_module_update") return; sendEvent({ - eventName: "install", + eventName: details.reason, ...details }); From 2e1641f0501df013d9c7792393224c7fb031a683 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 20 Aug 2023 12:15:38 +0530 Subject: [PATCH 39/52] fix: cant store Date object in storage --- chrome-extension/src/background.js | 12 +++++------- important-types.ts | 4 ++-- server/src/routes/domainInfo.ts | 6 +++--- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index cb96dd6..25aa5c9 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -248,11 +248,11 @@ async function getDomainValidationInfo(url, tab, createdOn) { } /** - * @param {Date} createdOn + * @param {string} createdOn */ function isSoftWarning(createdOn) { let now = new Date(); - return now.getTime() - createdOn.getTime() < env.alertPeriod; + return now.getTime() - new Date(createdOn).getTime() < env.alertPeriod; } /** @@ -350,8 +350,6 @@ async function fetchDomainInfo(simplifiedUrl) { storageItem = { ...storageItem, ...content, - createdon: new Date(content.createdon), - updatedon: new Date(content.updatedon) }; } @@ -458,7 +456,7 @@ async function processTab(tab) { const _validationInfo = await getDomainValidationInfo( url, tab, - storageItem.createdon || null + storageItem.createdon == undefined ? null : new Date(storageItem.createdon) ); if (_validationInfo != undefined) { isUpdated = true; @@ -553,7 +551,7 @@ async function processTab(tab) { sendMessage(tab, "domain", { isSuccess: true, domain: url, - createdOn: storageItem.createdon ? storageItem.createdon.getTime() : 0, + createdOn: storageItem.createdon == undefined ? 0 : new Date(storageItem.createdon).getTime(), type: storageItem.validationInfo?.type, msg: storageItem.validationInfo?.msg, description: storageItem.validationInfo?.description, @@ -572,7 +570,7 @@ async function processTab(tab) { const computedStorageItem = { ...storageItem, isNew: storageItem.createdon - ? isSoftWarning(new Date(storageItem.createdon)) + ? isSoftWarning(storageItem.createdon) : false, }; sendMessage(tab, "processing-finished", computedStorageItem); diff --git a/important-types.ts b/important-types.ts index 4989b59..57b33e2 100644 --- a/important-types.ts +++ b/important-types.ts @@ -2,8 +2,8 @@ export interface BasicDomainInfo { domain: string; - createdon: Date, - updatedon: Date, + createdon: string, + updatedon: string, recordCreatedOn?: string, isValid?: boolean } diff --git a/server/src/routes/domainInfo.ts b/server/src/routes/domainInfo.ts index ed4507e..4e89f64 100644 --- a/server/src/routes/domainInfo.ts +++ b/server/src/routes/domainInfo.ts @@ -67,8 +67,8 @@ async function getDomainInfo( } else { const results = await whois(domain); console.log("reading new domain info", domain, results); - domainInfo.createdon = new Date(results.creationDate); - domainInfo.updatedon = new Date(results.updatedDate); + domainInfo.createdon = new Date(results.creationDate).toISOString(); + domainInfo.updatedon = new Date(results.updatedDate).toISOString(); domainInfo.isValid = true; let text = @@ -98,7 +98,7 @@ async function getDomainInfo( return domainInfo; } -export default async function (req: Request, res: Response) { +export default async function (req: Request, res: Response) { // await captureWebsite.file('https://cryptnesis.com/', 'screenshot.png'); let domain = req.body.domain; From 95ab5a2e61a5b1ef59506a3efb2c1e117d41b3f9 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 20 Aug 2023 12:23:51 +0530 Subject: [PATCH 40/52] chore: remove old items on update to avoid issues --- chrome-extension/src/background.js | 71 +++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index 25aa5c9..7a456bc 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -3,8 +3,8 @@ /// /// -import { API_ENDPOINT, DOMAIN } from "../constants"; -import { sendEvent } from "./utils"; +import { API_ENDPOINT, DOMAIN, USER_ID_KEY } from "../constants"; +import { getUserId, sendEvent } from "./utils"; import { updateActionBadge, getStorageKey } from "./utils/background"; // ! For production uncomment these lines @@ -28,6 +28,33 @@ const env = { "https://api.thegraph.com/subgraphs/name/venkatteja/vigilancedao", }; +/** + * @returns {Promise>} + */ +function loadDontShowAgainDomains() { + console.log("loadDontShowAgainDomains"); + return chrome.storage.sync + .get(DONT_SHOW_AGAIN_DOMAINS_KEY) + .then((items) => { + /** + * @type {Array} + */ + const x = items[DONT_SHOW_AGAIN_DOMAINS_KEY] || [] + return x; + }) + .catch((error) => { + console.error( + `Error while getting ${DONT_SHOW_AGAIN_DOMAINS_KEY}`, + error + ); + /** + * @type {Array} + */ + const x = []; + return x; + }); +} + /** * TODO * @param {any[]} columns @@ -673,19 +700,7 @@ async function processMsg(request, sender, sendResponse) { } else if (request.type == "stake-amount") { await sendMessage(sender.tab, "stake-amount", request.data); } else if (request.type == "alert-dont-show-again") { - /** - * @type {string[]} - */ - const dontShowAgainDomains = await chrome.storage.sync - .get(DONT_SHOW_AGAIN_DOMAINS_KEY) - .then((items) => items[DONT_SHOW_AGAIN_DOMAINS_KEY] || []) - .catch((error) => { - console.error( - `Error while getting ${DONT_SHOW_AGAIN_DOMAINS_KEY}`, - error - ); - return []; - }); + const dontShowAgainDomains = await loadDontShowAgainDomains(); chrome.storage.sync.set({ [DONT_SHOW_AGAIN_DOMAINS_KEY]: dontShowAgainDomains.concat( // @ts-expect-error @@ -733,7 +748,7 @@ function takeScreenshot(tab) { }); } -chrome.runtime.onInstalled.addListener((details) => { +chrome.runtime.onInstalled.addListener(async (details) => { console.log('onInstalled', details); if (details.reason == "chrome_update" || details.reason == "shared_module_update") return; @@ -746,5 +761,29 @@ chrome.runtime.onInstalled.addListener((details) => { chrome.tabs.create({ url: `${DOMAIN}/extension-installed?reason=${details.reason}`, }); + } else if (details.reason == "update") { + // on update clear everything on storage other than + // - userid + // - dont show again domains + // to avoid issues with mismatched types + const userId = await getUserId(); + const dontShowAgainDomains = await loadDontShowAgainDomains(); + + chrome.storage.sync + .clear() + .then(() => { + console.log("storage.sync cleared"); + }) + .catch(console.error); + + /** @type {Record} */ + const newItems = {}; + if (userId) { + newItems[USER_ID_KEY] = userId; + } + if (dontShowAgainDomains.length > 0) { + newItems[DONT_SHOW_AGAIN_DOMAINS_KEY] = dontShowAgainDomains; + } + chrome.storage.sync.set(newItems); } }); From 8924b39af273676e4e64f283720e354cbf6f9e6a Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 20 Aug 2023 12:24:54 +0530 Subject: [PATCH 41/52] chore: improve logs --- chrome-extension/src/background.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index 7a456bc..ff176ae 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -191,6 +191,7 @@ function getUrl(tab) { * @returns {Promise} */ async function getDomainValidationInfo(url, tab, createdOn) { + console.log("getDomainValidationInfo", url, tab, createdOn); /** * @type {"info" | "warning"} */ @@ -278,6 +279,7 @@ async function getDomainValidationInfo(url, tab, createdOn) { * @param {string} createdOn */ function isSoftWarning(createdOn) { + console.log("isSoftWarning", createdOn, new Date(createdOn)); let now = new Date(); return now.getTime() - new Date(createdOn).getTime() < env.alertPeriod; } @@ -288,6 +290,7 @@ function isSoftWarning(createdOn) { * @returns {Promise} */ async function fetchDomainInfo(simplifiedUrl) { + console.log("fetchDomainInfo", simplifiedUrl); /** * @type {import("./types").DomainStorageItem} */ @@ -336,7 +339,8 @@ async function fetchDomainInfo(simplifiedUrl) { body: JSON.stringify({ query }), }); let data = await rawResponse.json(); - console.log("response", data); + console.log("subgraph response", data); + let reports = data.data.reports; for (let i = 0; i < reports.length; ++i) { const report = reports[i]; @@ -373,6 +377,8 @@ async function fetchDomainInfo(simplifiedUrl) { */ const content = await response.json(); + console.log("/domain-info response", content); + if (content.domain) { storageItem = { ...storageItem, From b8a743e80a74c0109ea7d3d5760a1286b88ddad4 Mon Sep 17 00:00:00 2001 From: Venkat Kunisetty Date: Wed, 23 Aug 2023 23:06:05 +0530 Subject: [PATCH 42/52] fix event userid empty logic --- chrome-extension/constants.js | 8 ++++---- chrome-extension/src/background.js | 4 ++++ chrome-extension/src/inject.js | 2 ++ chrome-extension/src/utils/index.js | 5 ++--- server/src/app.ts | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/chrome-extension/constants.js b/chrome-extension/constants.js index 9bada78..dcd3b4f 100644 --- a/chrome-extension/constants.js +++ b/chrome-extension/constants.js @@ -411,12 +411,12 @@ const abi = [ const address = "0x68Db62ADCaADdb21cB000841f1F347A6d8bEED9b" // for production -const API_ENDPOINT = "https://api.vigilancedao.org" -const DOMAIN = 'https://vigilancedao.org' +// const API_ENDPOINT = "https://api.vigilancedao.org" +// const DOMAIN = 'https://vigilancedao.org' // for development -// const API_ENDPOINT = "http://localhost:4000"; -// const DOMAIN = "http://localhost:3000"; +const API_ENDPOINT = "http://localhost:4000"; +const DOMAIN = "http://localhost:3000"; const USER_ID_KEY = "user-id"; diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index ec98481..08689e2 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -790,6 +790,10 @@ function takeScreenshot(tab) { }); } +async function doHttpPost(endpoint, data) { + +} + chrome.runtime.onInstalled.addListener((details) => { console.log('onInstalled', details); sendEvent({ diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index fdcc653..6fd7552 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -29,6 +29,8 @@ function isSendTransactionRequest(params) { return params.method == "eth_sendTransaction"; } + + /** * @param {import("./inject").BasicContractInfo} basicInfo * @returns {Promise} diff --git a/chrome-extension/src/utils/index.js b/chrome-extension/src/utils/index.js index e54d03d..0143708 100644 --- a/chrome-extension/src/utils/index.js +++ b/chrome-extension/src/utils/index.js @@ -83,8 +83,7 @@ export async function sendEvent(event) { } const userId = await getUserId(); - if (userId == null) return; - + event.userId = userId; console.log("sendEvent event", event); @@ -101,7 +100,7 @@ export async function sendEvent(event) { console.error("sendEvent: response not ok", response.status, body); return; } - + console.log('event', body) if (userId != body) { return chrome.storage.sync .set({ [USER_ID_KEY]: body }) diff --git a/server/src/app.ts b/server/src/app.ts index 7d9dc65..b3895ac 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -15,7 +15,7 @@ app.use(helmet()); app.use( cors(), ); -app.use(cookieParser()); +// app.use(cookieParser()); app.post('/domain-info', domainInfo); app.post("/contract-info", contractInfo); From 166cb499a29e6768d85fcf6969e89f77937d07a2 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 25 Aug 2023 14:07:27 +0530 Subject: [PATCH 43/52] chore: remove prebuild script after running --- chrome-extension/bundle-config.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chrome-extension/bundle-config.mjs b/chrome-extension/bundle-config.mjs index e7fb172..e591087 100644 --- a/chrome-extension/bundle-config.mjs +++ b/chrome-extension/bundle-config.mjs @@ -137,7 +137,7 @@ const prebuildOptions = { }) || "" ) .replace("\\", "/"); - console.log(prebuildOutputScript); + console.log("Transpiled prebuild script", prebuildOutputScript); if (prebuildOutputScript == "./") { throw new Error("Prebuild script is not found"); @@ -164,6 +164,11 @@ const prebuildOptions = { console.error(err); } console.log("Generating prebuilt html done"); + + // remove prebuild script + await rm(prebuildOutputScript).then(() => { + console.log("Removed", prebuildOutputScript); + }).catch(console.error); }); build.onDispose(async () => { From 696b183073b65e1be25e513e988235a6eaee710c Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sat, 26 Aug 2023 22:05:48 +0530 Subject: [PATCH 44/52] chore: minor changes --- chrome-extension/src/background.js | 2 ++ chrome-extension/src/utils/index.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index ff176ae..34f537b 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -146,6 +146,7 @@ let counter = {}; * @returns {string | undefined} */ function getUrl(tab) { + console.log("getUrl", tab); /** * @type {string} */ @@ -522,6 +523,7 @@ async function processTab(tab) { // description: storageItem.validationInfo?.description, // }); // } + console.log(storageItem); // If we know from blockchain or our Backend API that a domain is scam // then we show the ❌ icon on extension diff --git a/chrome-extension/src/utils/index.js b/chrome-extension/src/utils/index.js index e0a910b..d6fec85 100644 --- a/chrome-extension/src/utils/index.js +++ b/chrome-extension/src/utils/index.js @@ -25,7 +25,7 @@ export async function subgraphQuery(query) { * undefined -> no user id saved * string -> user id */ -function getUserId() { +export function getUserId() { return chrome.storage.sync .get(USER_ID_KEY) .then( From 1c62f53f57787b508ed9e94c76a6df0b139a94cf Mon Sep 17 00:00:00 2001 From: Venkat Kunisetty Date: Mon, 28 Aug 2023 21:08:35 +0530 Subject: [PATCH 45/52] deploy staging API and fix events API --- chrome-extension/constants.js | 8 ++++++-- chrome-extension/src/background.js | 12 +++++++----- server/package.json | 3 ++- server/src/app.ts | 2 +- server/src/routes/event.ts | 8 ++++---- server/src/telegramBot.js | 3 +-- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/chrome-extension/constants.js b/chrome-extension/constants.js index dcd3b4f..037ca98 100644 --- a/chrome-extension/constants.js +++ b/chrome-extension/constants.js @@ -414,9 +414,13 @@ const address = "0x68Db62ADCaADdb21cB000841f1F347A6d8bEED9b" // const API_ENDPOINT = "https://api.vigilancedao.org" // const DOMAIN = 'https://vigilancedao.org' +// for staging +const API_ENDPOINT = 'https://i4jz77p469.execute-api.ap-northeast-1.amazonaws.com/staging'; +const DOMAIN = 'https://vigilancedao.org' + // for development -const API_ENDPOINT = "http://localhost:4000"; -const DOMAIN = "http://localhost:3000"; +// const API_ENDPOINT = "http://localhost:4000"; +// const DOMAIN = "http://localhost:3000"; const USER_ID_KEY = "user-id"; diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index 34f537b..1b2f8f6 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -8,10 +8,10 @@ import { getUserId, sendEvent } from "./utils"; import { updateActionBadge, getStorageKey } from "./utils/background"; // ! For production uncomment these lines -console.log = function(){}; -console.debug = function(){}; -console.error = function(){}; -console.warn = function(){}; +// console.log = function(){}; +// console.debug = function(){}; +// console.error = function(){}; +// console.warn = function(){}; try { importScripts("./psl.min.js" /*, and so on */); @@ -762,7 +762,9 @@ chrome.runtime.onInstalled.addListener(async (details) => { if (details.reason == "chrome_update" || details.reason == "shared_module_update") return; sendEvent({ eventName: details.reason, - ...details + eventData: { + ...details + } }); if (details.reason == 'install') { diff --git a/server/package.json b/server/package.json index 27c1511..7854c04 100644 --- a/server/package.json +++ b/server/package.json @@ -6,6 +6,7 @@ "dev": "cross-env NODE_ENV=development sls offline start --stage offline", "tail-log": "sls logs -f app -t", "deploy-prod": "cross-env NODE_ENV=production sls deploy --stage prod", + "deploy-staging": "cross-env NODE_ENV=production sls deploy --stage staging", "remove-prod": "sls remove --stage prod", "clean": "rimraf dist .webpack .serverless", "lint": "eslint --ext .js,.ts .", @@ -15,7 +16,6 @@ }, "dependencies": { "@types/cookie-parser": "^1.4.3", - "@types/node-telegram-bot-api": "^0.61.7", "capture-website": "^2.4.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", @@ -37,6 +37,7 @@ "@commitlint/config-conventional": "^16.2.4", "@types/express": "^4.17.17", "@types/jest": "^28.1.0", + "@types/node-telegram-bot-api": "^0.61.7", "@types/pg": "^8.6.5", "@types/pg-pool": "^2.0.3", "@types/supertest": "^2.0.12", diff --git a/server/src/app.ts b/server/src/app.ts index b3895ac..6eb7ffb 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -1,6 +1,6 @@ import express, { json } from 'express'; import helmet from 'helmet'; -import cookieParser from "cookie-parser"; +// import cookieParser from "cookie-parser"; import domainInfo from './routes/domainInfo'; import contractInfo from './routes/contractInfo'; diff --git a/server/src/routes/event.ts b/server/src/routes/event.ts index d0a6bf3..bb71301 100644 --- a/server/src/routes/event.ts +++ b/server/src/routes/event.ts @@ -20,11 +20,11 @@ export default async function ( req: Request<{}, unknown, TrackingEvent>, res: Response ) { - const { eventName, userId: _userId, ...others } = req.body; - console.log("event", { eventName, _userId, ...others }); + const { eventName, userId: _userId, eventData } = req.body; + console.log("event", { eventName, _userId, eventData }); let userId: string; - if (typeof _userId == "undefined") { + if (typeof _userId == "undefined" || _userId == '') { // create new userId = createUserId(); } else if (typeof _userId == "string") { @@ -39,7 +39,7 @@ export default async function ( console.warn("Mixpanel is not initialized. Events cannot be sent."); } else { // mixpanel.people.set(userId, {}); - mixpanel.track(eventName, { ...others, distinct_id: userId }); + mixpanel.track(eventName, { ...eventData, distinct_id: userId }); } res.status(200).send(userId); diff --git a/server/src/telegramBot.js b/server/src/telegramBot.js index ba5ecc2..4c28691 100644 --- a/server/src/telegramBot.js +++ b/server/src/telegramBot.js @@ -41,12 +41,11 @@ if (TELEGRAM_CHAT_ID) { */ export function sendMessage( message, - alertType = "infoAlert", options = {}, members = allowedMembers, i = 0 ) { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { let member = members[i]; if (member == undefined) { From d7fd899c5f85eb1815c9c56f0fa5ff668965b898 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Tue, 29 Aug 2023 14:19:46 +0530 Subject: [PATCH 46/52] feat: add tooltip for Verified Alert fixes #58 --- chrome-extension/src/content.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index 9251835..fac39c2 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -454,10 +454,29 @@ async function displayVerifiedAlert() { bottom: clamp(10px, 2vh, 30px); right: clamp(10px, 3vw, 30px); } + .container { + display: flex; + align-items: center; + gap: 5px; + } img.verified-icon { cursor: pointer; filter: drop-shadow(0px 0px 10px #00eb18); } + .tooltip { + opacity: 0; + transform: translate(0px, 10px) scale(0.5, 0.1); + transition: transform .18s ease-in-out, background-color .18s ease-in-out, opacity .18s ease-in-out; + transform-origin: bottom right; + padding: 4px 10px; + border-radius: 5px; + color: white; + } + div.container:hover .tooltip { + opacity: 1; + transform: translate(0px,0px) scale(1, 1); + background-color: #282f29; + } span.close-icon { cursor: pointer; position: absolute; @@ -481,9 +500,13 @@ async function displayVerifiedAlert() { `.trim(); const verifiedIconSrc = chrome.runtime.getURL("images/icon48.png"); + const tooltipText = "Verified by Vigilance DAO" innerHTMLParts[1] = `

- + + ${tooltipText} + + ${CLOSE_ICON} From ed769e73b6ab5de425193434616e0c959345571a Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Tue, 29 Aug 2023 15:10:24 +0530 Subject: [PATCH 47/52] fix: contract alert risk expanded event missed related to #66 --- chrome-extension/src/inject.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 6a1a2fa..e988e4a 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -458,27 +458,34 @@ 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)) { + if (!(feedbackContainerElement instanceof HTMLDetailsElement)) { + return; + } + feedbackContainerElement.addEventListener("toggle", () => { + if (!feedbackContainerElement.open) { return; } sendEvent({ eventName: "Contract Alert Risk Expanded", }); - - feedbackContainerElement.open = !feedbackContainerElement.open; }); + feedbackContainerElement.classList.toggle( "hidden", alertInfo.feedback.length == 0 ); + // hide feedback-icon if feedback is empty + feedbackIconElement.classList.toggle( + "hidden", + alertInfo.feedback.length == 0 + ); + + feedbackIconElement.addEventListener("click", () => { + feedbackContainerElement.open = !feedbackContainerElement.open; + }); + if (alertInfo.feedback.length != 0) { feedbackListElement.innerHTML = alertInfo.feedback .map((i) => `
  • ${i}
  • `) From 204e9231c0bbab442add173ad5c4eb225c36df0c Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Tue, 29 Aug 2023 15:11:25 +0530 Subject: [PATCH 48/52] feat: add contract alert report button clicked event related to #66 --- chrome-extension/src/inject.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index e988e4a..58767f1 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -496,6 +496,9 @@ function populateFinancialAlertWithData(alertInfo) { closeButton.addEventListener("click", alertInfo.cancelButtonClickListener); reportButton.addEventListener("click", () => { formElement.classList.toggle("hidden"); + sendEvent({ + eventName: "Contract Alert Report Button Clicked", + }); }); if (!(formElement instanceof HTMLFormElement)) { From 257dc8bb08ed5f80bda6492bc4ca626b19930284 Mon Sep 17 00:00:00 2001 From: Venkat Kunisetty Date: Tue, 29 Aug 2023 20:27:52 +0530 Subject: [PATCH 49/52] remove credentials --- chrome-extension/src/background.js | 2 -- chrome-extension/src/content.js | 6 +++--- chrome-extension/src/inject.js | 4 +--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/chrome-extension/src/background.js b/chrome-extension/src/background.js index 1b2f8f6..68b22a0 100644 --- a/chrome-extension/src/background.js +++ b/chrome-extension/src/background.js @@ -109,7 +109,6 @@ async function getDomainRegistrationDate(storageInfo, url) { "Content-Type": "application/json", }, body: JSON.stringify({ domain: url }), - credentials: "include", }); /** * @type {import("../../important-types").DomainInfo} @@ -371,7 +370,6 @@ async function fetchDomainInfo(simplifiedUrl) { "Content-Type": "application/json", }, body: JSON.stringify({ domain: simplifiedUrl }), - credentials: "include", }); /** * @type {import("../../important-types").DomainInfo} diff --git a/chrome-extension/src/content.js b/chrome-extension/src/content.js index 9251835..aebeedc 100644 --- a/chrome-extension/src/content.js +++ b/chrome-extension/src/content.js @@ -582,7 +582,7 @@ async function createAlertDialog(alertInfo) { descriptionElement.innerHTML = alertInfo.description; statusImgElement.src = alertInfo.imageSrc; - shadowRoot.addEventListener("click", (event) => { + shadowRoot.addEventListener("click", async (event) => { console.log("dialog clicked", event); if (!(event.target instanceof HTMLElement)) { @@ -593,7 +593,7 @@ async function createAlertDialog(alertInfo) { const target = targetElement.id; if (target == "close-website") { - trackEventInContentScript({ + await trackEventInContentScript({ eventName: "Domain Alert Action", eventData: { action: "Close Website", @@ -601,7 +601,7 @@ async function createAlertDialog(alertInfo) { }); sendMessageToBackground("close-website"); } else if (target == "hide") { - trackEventInContentScript({ + await trackEventInContentScript({ eventName: "Domain Alert Action", eventData: { action: "Hide", diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index 6a1a2fa..fce02e2 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -19,7 +19,7 @@ const env = { // host: "http://localhost:4000", }; -const ContractInfoAPIURL = env.host.concat("/contract-info"); +const ContractInfoAPIURL = env.host.concat("/contract-infoo"); /** * @param {import("./inject").MetaMaskRequest} params @@ -48,7 +48,6 @@ function fetchContractInfo(basicInfo) { "Content-Type": "application/json", }, body: JSON.stringify(basicInfo), - credentials: "include", }).then( /** * @returns {Promise} @@ -180,7 +179,6 @@ function submitContractReport(report) { body: JSON.stringify({ report, }), - credentials: "include", }) .then((response) => { if (response.ok) { From 9d29d9251eb78345aee8e966f66d2a42157f9d0e Mon Sep 17 00:00:00 2001 From: Venkat Kunisetty Date: Tue, 29 Aug 2023 20:55:43 +0530 Subject: [PATCH 50/52] v0.3.1 --- chrome-extension/public/manifest.json | 2 +- chrome-extension/src/inject.js | 30 +++++++++------------------ 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/chrome-extension/public/manifest.json b/chrome-extension/public/manifest.json index 6533a8f..c8a19d6 100644 --- a/chrome-extension/public/manifest.json +++ b/chrome-extension/public/manifest.json @@ -1,7 +1,7 @@ { "name": "Web3 Vigilance - Browser security", "description": "Protect yourself from new websites against potential scams, frauds & phishing sites. Maintained by a blockchain org 'Vigilance DAO'", - "version": "0.3.0", + "version": "0.3.1", "icons": { "16": "images/icon16.png", "32": "images/icon32.png", diff --git a/chrome-extension/src/inject.js b/chrome-extension/src/inject.js index e846704..8befef5 100644 --- a/chrome-extension/src/inject.js +++ b/chrome-extension/src/inject.js @@ -19,7 +19,7 @@ const env = { // host: "http://localhost:4000", }; -const ContractInfoAPIURL = env.host.concat("/contract-infoo"); +const ContractInfoAPIURL = env.host.concat("/contract-info"); /** * @param {import("./inject").MetaMaskRequest} params @@ -456,34 +456,27 @@ function populateFinancialAlertWithData(alertInfo) { drainedAccountsValueElement.dataset["priority"] = alertInfo.drainedAccountsValue.toLowerCase(); - if (!(feedbackContainerElement instanceof HTMLDetailsElement)) { - return; - } - feedbackContainerElement.addEventListener("toggle", () => { - if (!feedbackContainerElement.open) { + // hide feedback-icon if feedback is empty + feedbackIconElement.classList.toggle( + "hidden", + alertInfo.feedback.length == 0 + ); + feedbackIconElement.addEventListener("click", () => { + if (!(feedbackContainerElement instanceof HTMLDetailsElement)) { return; } sendEvent({ eventName: "Contract Alert Risk Expanded", }); - }); + feedbackContainerElement.open = !feedbackContainerElement.open; + }); feedbackContainerElement.classList.toggle( "hidden", alertInfo.feedback.length == 0 ); - // hide feedback-icon if feedback is empty - feedbackIconElement.classList.toggle( - "hidden", - alertInfo.feedback.length == 0 - ); - - feedbackIconElement.addEventListener("click", () => { - feedbackContainerElement.open = !feedbackContainerElement.open; - }); - if (alertInfo.feedback.length != 0) { feedbackListElement.innerHTML = alertInfo.feedback .map((i) => `
  • ${i}
  • `) @@ -494,9 +487,6 @@ function populateFinancialAlertWithData(alertInfo) { closeButton.addEventListener("click", alertInfo.cancelButtonClickListener); reportButton.addEventListener("click", () => { formElement.classList.toggle("hidden"); - sendEvent({ - eventName: "Contract Alert Report Button Clicked", - }); }); if (!(formElement instanceof HTMLFormElement)) { From 2ab3ed1603fae8c6fe81594fd930a370c615e29b Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Fri, 1 Sep 2023 12:53:00 +0530 Subject: [PATCH 51/52] first draft --- webapp/src/pages/extensionInstalled.tsx | 63 ++++++++++++++++++++----- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/webapp/src/pages/extensionInstalled.tsx b/webapp/src/pages/extensionInstalled.tsx index 2e4cad1..525eeb4 100644 --- a/webapp/src/pages/extensionInstalled.tsx +++ b/webapp/src/pages/extensionInstalled.tsx @@ -7,8 +7,13 @@ import { Link, Box, Grid, + OrderedList, + ListItem } from "@chakra-ui/react"; +import { useMemo } from "react"; +import { useLocation } from "react-router-dom"; + import logo from "../assets/icon128.png"; import featureAlerts from "../assets/feature-alerts.webp"; @@ -32,6 +37,12 @@ function FeatureCard(props: FeatureCardProps) { } export default function ExtensionInstalled() { + const search = useLocation().search; + const isOpenedOnExtensionInstallation = useMemo(() => { + const reason = new URLSearchParams(search).get("reason"); + return reason == "install"; + }, [search]); + return ( <> - - Features - - - - - - - + {isOpenedOnExtensionInstallation ? ( + + + Pin our extension + + + And get instant feedback —including Domain Alerts & Contract + Information— on various sites you visit. + + + + + Click{" "} + + Extensions{" "} + {" "} + icon + + + Find{" "} + + Web3 Vigilance - Browser Security + {" "} + & Click pin icon + + + + ) : null} ); } From 730520d26961dd44fe66b98040704faada6a51c5 Mon Sep 17 00:00:00 2001 From: Sahithyan Kandathasan Date: Sun, 3 Sep 2023 20:33:43 +0530 Subject: [PATCH 52/52] add images --- webapp/public/alert.png | Bin 0 -> 1003 bytes webapp/public/extensions.png | Bin 0 -> 645 bytes webapp/public/pin-image.png | Bin 0 -> 3474 bytes webapp/public/verified.png | Bin 0 -> 880 bytes webapp/src/pages/extensionInstalled.tsx | 61 ++++++++++++++++++++---- 5 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 webapp/public/alert.png create mode 100644 webapp/public/extensions.png create mode 100644 webapp/public/pin-image.png create mode 100644 webapp/public/verified.png diff --git a/webapp/public/alert.png b/webapp/public/alert.png new file mode 100644 index 0000000000000000000000000000000000000000..62cea6abc759bff4653cc398c2ef0e94bad5c1e7 GIT binary patch literal 1003 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1As|HK~z{r?UzkR zR8bVi|L@J6Y{swr7(XT_#VnB)Mp-FXkp=}V3Ti~!NJIr~YSFTc_AUB=h%6Uwf@)E; zEDEFVO%#+AWtNVXqh@I{j=u4A&Yd?_hIsWU1 zqUpoQ?3O7df$@n_qXrIky@fv*fP>yg*>B}Q8wVCp%f8FGK3wt^W5^+Fi&Rc=x-o{v z_Lrz9aS=^Jn2a>Rqz4=K*=LY7eD0L783xO>hmt@+3JwI`Ve{8M3~RbfGaI8ZBNst?vz> zl=iC~lmz;FyJD+DlgGueiCsC+JWgoYP6)qe!p|`ryMTo540h4m>`@Z9o=)o+wefVB zq(Jv2!Ev|%#_29;jza~~$72k~@(x_{Z6M~DB}xL*==8={Mz-`2d-I{WD4~Wajr(FB z`DpCd*u2pQJ!g-SzzJwpo>X6*0)1-+%uzZlZih74-;yE6I7TWYW;Vf}DBXj%p0&z5 zPzKVB&WokcSET?^65JhIp@C!<&fHjthtCB1Lw05TL`h)d`&Zchr3Vq(V4^My;>Z$! z(uzd_#@T?3Ii@c|^i2l#`#dOgB}v~@dyrLTH5w*ccL&P=9s?#PCg zOEn|a0lG;b8uFw)cp;R;F&x9ODkuqj>q^13Y%knnQABPIVyvkL(U#9Jnm-|Wy-(V3 zC^oSws6dq7jH-%SJgF?R-eR0=dyS^{_b5+K$N4p@keT$m zsCAFqtlrcp@8&__#EIe+Xz}_W1V9HD4xvG_Y%)*#2c+HEv=P2!H=5f!tdW_aB#@Vw zCGfA4Q{Ls+Q&NE@Pa)np-7x4RvrXn+ZxCyjE) literal 0 HcmV?d00001 diff --git a/webapp/public/extensions.png b/webapp/public/extensions.png new file mode 100644 index 0000000000000000000000000000000000000000..9c2d76804b3db5c7833c6d0f711c408c62fb8c60 GIT binary patch literal 645 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 literal 0 HcmV?d00001 diff --git a/webapp/public/verified.png b/webapp/public/verified.png new file mode 100644 index 0000000000000000000000000000000000000000..e2320ec7670e4e6805e2ed94c974afccd90bd0fc GIT binary patch literal 880 zcmV-$1CRWPP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0|iM$K~z{r?U&0> z6Hye#zm}Okc(e+%lnNFQ46)FlJOrYUfT)QPL5&g@8WR#-`gf>Hje8T>nXoaqQ4)>D zhzZ018l})8wS`h>p{39k>df^Ti-ODx3<6IfPD?^F&+X6FK4_&no?#g=Ji zJ*-BDd}}eXbFDO^45FvOwAY2niP24s^2E7oldezjeIKV8We{a{KH!7bjh5}fQhH3OS zYVJ$coM=Jwxy!H?mmw0GC;64-B`kzyiI$6Cvk|W9Q?c9asHm)@5#XqC)V4>!BJanNStD*pgZwzPp^7&u3V;G0}uSh3g& zRq_y3V88Hrz1)0eV*|%53Ze_t`1;XUzINM6;|0 zj-w6G>9P=6ijv$;AlNM5w%ijVMB7R#pzcs%=DHiPLTP3--}Ep)w!!$yOfy>}cp79G zbD+~_5N+VJ9HTt^SpFGe5NwuVSePwEM5m=Of{kYSbKrP(2Ku0ZW_}~~WknTOc8&!W zYYB~n(b&_4s=l*0(%(!o(h=PaEZhb~CAl6i z?D%^G^L<3iK{2!C{Y|irl+y?}{JIgw88Z@W3`6JpXjUGInBDKOqV8!YPCdGXvbXhM zqFORfd4^BFK-9QEv|JQ18_b)AzsN;&L(f&TJ-iKFI1>>=5W}r6NnQyQF`Hyo@%+vM zIBP$U*yQ@z;)dG$u%<~6tpt+I@+(j(yx})Do?xJ}m&AUX8&0mU{>7iq*b2!%jG?BP zO;@puKsHv=BS`2s-)%`*ZY7)jA1Kzkc7i>#+r=(23;Y0?A_a8X)mqsA0000) } +function CloseIcon() { + return ( + + + + + + ); +} + export default function ExtensionInstalled() { const search = useLocation().search; const isOpenedOnExtensionInstallation = useMemo(() => { @@ -135,22 +158,32 @@ export default function ExtensionInstalled() { position="absolute" top="10px" right="10px" - backgroundColor="hsl(265 100% 26% / 1)" + backgroundColor="hsl(240 71% 29% / 1)" borderRadius="4px" padding="10px 18px" > - - Pin our extension - + + + Pin our extension + + + + + + And get instant feedback —including Domain Alerts & Contract Information— on various sites you visit. + + + + @@ -158,6 +191,13 @@ export default function ExtensionInstalled() { Extensions{" "} {" "} + icon @@ -166,6 +206,7 @@ export default function ExtensionInstalled() { Web3 Vigilance - Browser Security {" "} & Click pin icon +