Is HAVAH Wallet safe?

Medium risk

HAVAH Wallet sends wallet interaction events (connect, sign, transaction URLs) to Google Analytics with a persistent client UUID.

Each time a user connects a site, signs a message, or sends a transaction, the extension transmits the origin URL and transaction details to Google Analytics GA4 endpoints. A persistent client UUID stored in chrome.storage.local ties these events together across sessions, allowing cross-session linkage of wallet activity to a single user.

HAVAHv2.3.7Chrome Web Store
48Risk
Who publishes it

HAVAH - no other listings under this identity, 1 shared hostname

What this publisher told the store about itself, and the other listings that told it the same thing.

Shared hosts - 1 hostname

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

bridge.dcentwallet.com
Also called by 2 other listings: DCENT Wallet – Hardware-Secured Extension, HashPack

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Wallet action origins sent to Google Analytics GA4

MediumunwantedCWE-359Dynamic sandbox

Connecting HAVAH Wallet to a site, signing, or requesting a transaction sends the site host to GA4, with a persistent client UUID and session ID.

What actually happens

You did this

You use HAVAH Wallet to connect to a site, sign data, or request a transaction.

The extension did this

The extension sends the site host for that wallet action to Google Analytics GA4.

Fields the code places in wallet-action analytics events

  • Wallet-action site
    havah.io

    Shows which site was involved when you connected the wallet, signed data, or requested a transaction.

  • Wallet action type
    transaction

    Shows whether the event came from a connect, signing, or transaction request.

  • Network name
    VEGANET

    Adds which HAVAH network was active for transaction telemetry.

  • API method
    icx_sendTransaction

    Adds the transaction request method for transaction telemetry.

  • Client identifier
    8f2a4e1c-0b6e-4a10-9f9f-2d6a7b8c1e53

    Lets analytics link wallet events from the same browser profile over time.

  • Session identifier
    1720837354123

    Groups wallet activity that happens in the same browser session.

Captured request

POSThttps://www.google-analytics.com/mp/collect?measurement_id=G-2BXRDK4436&api_secret=<redacted>

The code that does this

Analytics sender adds persistent IDs and posts to GA4

Readable version

Readable analytics sender from the deobfuscated background script

deobfuscated/background.js
const google_analytics = new class {  network = "";  constructor(debug = !1) {    this.debug = debug  }  async getOrCreateClientId() {    let {      clientId    } = await chrome.storage.local.get("clientId");    return clientId || (clientId = self.crypto.randomUUID(), await chrome.storage.local.set({      clientId    })), clientId  }  async getOrCreateSessionId() {    let {      sessionData    } = await chrome.storage.session.get("sessionData");    const currentTimeInMs = Date.now();    if (sessionData && sessionData.timestamp) {      (currentTimeInMs - sessionData.timestamp) / 6e4 > 30 ? sessionData = null : (sessionData.timestamp = currentTimeInMs, await chrome.storage.session.set({        sessionData      }))    }    return sessionData || (sessionData = {      session_id: currentTimeInMs.toString(),      timestamp: currentTimeInMs.toString()    }, await chrome.storage.session.set({      sessionData    })), sessionData.session_id  }  async fireEvent(name, params = {}) {    params.session_id || (params.session_id = await this.getOrCreateSessionId()), params.engagement_time_msec || (params.engagement_time_msec = 100);    let MEASUREMENT_ID = "",      API_SECRET = "";    switch (this.network) {      case "VEGANET":        MEASUREMENT_ID = "G-1M074S78H0", API_SECRET = "<redacted>";        break;      case "DENEBNET":        MEASUREMENT_ID = DENEB_MEASUREMENT_ID, API_SECRET = DENEB_API_SECRET;        break;      default:        MEASUREMENT_ID = "G-2BXRDK4436", API_SECRET = "<redacted>"    }    try {      await fetch(`${this.debug?"https://www.google-analytics.com/debug/mp/collect":"https://www.google-analytics.com/mp/collect"}?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`, {        method: "POST",        body: JSON.stringify({          client_id: await this.getOrCreateClientId(),          events: [{            name,            params          }]        })      });      if (!this.debug) return    } catch (e) {      console.error("Google Analytics request failed with an exception", e)    }  }  async firePageViewEvent(pageTitle, pageLocation, additionalParams = {}, network) {    return this.network = network, this.fireEvent("page_view", {      page_title: pageTitle,      page_location: pageLocation,      ...additionalParams    })  }  async fireErrorEvent(error, additionalParams = {}) {    return this.fireEvent("extension_error", {      ...error,      ...additionalParams    })  }};

The code that does this

Wallet message handlers pass the site host into analytics events

Readable version

CONNECT handler sends connect_url

deobfuscated/background.js
const host = message.detail.host;return GoogleAnalytics.sendEventGoogleAnalytics("connect_url", {  connect_url: host}), isAllowSite = !(!accountInfo.allowSites || 0 == accountInfo.allowSites.length) && Array.from(accountInfo.allowSites).includes(host)

SIGN handler sends sign

deobfuscated/background.js
const host = message.detail.host;isAllowSite = !(!accountInfo.allowSites || 0 == accountInfo.allowSites.length) && Array.from(accountInfo.allowSites).includes(host), GoogleAnalytics.sendEventGoogleAnalytics("sign", {  sign: host});

SEND_TRANSACTION handler sends transaction, network, and API method

deobfuscated/background.js
const host = message.detail.host;isAllowSite = !(!accountInfo.allowSites || 0 == accountInfo.allowSites.length) && Array.from(accountInfo.allowSites).includes(host);try {  GoogleAnalytics.sendEventGoogleAnalytics("transaction", {    transaction: host,    network,    api_method: message.detail.body.method  })} catch (e) {  console.error("e", e)}

External analytics destination

    • www.google-analytics.com

    Receives GA4 Measurement Protocol POST requests containing the wallet-action event name, event parameters, client UUID, and session ID.

What it can do

Permissions this extension asks for, as declared in version 2.3.7. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on bridge.dcentwallet.com

    *://bridge.dcentwallet.com/*

  • Read and change your data on every site you visit

    http://*/*

  • Read and change your data on every secure site you visit

    https://*/*

  • Store data in your browser

    storage

  • See the address and title of every tab you have open

    tabs

  • Run its own code inside the pages you visit

    scripting

Where it sends data

Destinations our analysis observed HAVAH Wallet contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • www.google-analytics.comwidely used

    HAVAH Wallet sends data to www.google-analytics.com. A widely used service: 388 other extensions we have analyzed send data here.

Updated 30 September 2026cnncmdhjacpkmjmkcafchppbnpnhdmon