Is HAVAH Wallet safe?
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.
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
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.
A GA4 request with those IDs was observed, but no action event body was captured.
You use HAVAH Wallet to connect to a site, sign data, or request a transaction.
The extension sends the site host for that wallet action to Google Analytics GA4.
| Field | Value | Why it matters | |
|---|---|---|---|
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. |
Analytics sender adds persistent IDs and posts to GA4
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
})
}
};Wallet message handlers pass the site host into analytics events
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)const host = message.detail.host;
isAllowSite = !(!accountInfo.allowSites || 0 == accountInfo.allowSites.length) && Array.from(accountInfo.allowSites).includes(host), GoogleAnalytics.sendEventGoogleAnalytics("sign", {
sign: host
});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)
}- www.google-analytics.com
Receives GA4 Measurement Protocol POST requests containing the wallet-action event name, event parameters, client UUID, and session ID.