Is Hana Wallet safe?

Medium risk

Hana Wallet is medium risk. Hana Wallet looks up your public IP address and reads a persistent device ID, then sends both to Amplitude's analytics API on install and again each time you open the wallet.

Hana Walletv5.11.0.0Chrome Web Store
45Risk
Who publishes it

Hana Tech Pty Ltd - no other listings under this identity

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

Publisher
Hana Wallet
Declared legal entity
Hana Tech Pty Ltd
Registered address
Level 4, 11/17 York St, (Stone & Chalk), Sydney, NSW 2000, AU
Registered contact
Hana Tech Pty Ltd

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

Publishers can request a review.

Findings

Hana Wallet sends a persistent device ID and your public IP to Amplitude

Hana Wallet looks up your public IP address and reads a persistent device ID, then sends both to Amplitude's analytics API on install and again each time you open the wallet.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You install Hana Wallet, or open its popup, side panel, or main tab.

No wallet action, such as connecting to a site or sending funds, is required.

The extension did this

The background script looks up your public IP address and sends it, together with a persistent device ID, to Amplitude's analytics API.

The same report fires again on every later app load, not just the first install.

Fields in the Amplitude analytics event
  • Persistent device ID
    7d145bfe-cc78-42cc-aa35-c4355d87253f

    A UUID generated once and saved in the extension's local storage, tying every future event back to this exact install.

  • Your public IP address
    187.40.254.11

    Looked up from a separate IP service just before the event is sent, then attached to the analytics record.

  • Event name
    App Loaded

    Marks what triggered the report, such as installing the extension or opening the wallet UI.

  • Extension version
    5.13.10

    Lets Amplitude segment events by the exact build you're running.

  • Platform tag
    chrome-extension

    Always reports as a Chrome extension, separating this install from Hana's mobile app in Amplitude.

Captured request
POSThttps://api2.amplitude.com/2/httpapi
Headers
Content-Type
application/json
Body
{  "api_key": "<redacted>",  "events": [    {      "device_id": "7d145bfe-cc78-42cc-aa35-c4355d87253f",      "event_type": "App Loaded",      "app_version": "5.13.10",      "platform": "chrome-extension",      "ip": "187.40.254.11"    }  ]}
The code that does this

Shipped background script vs. the same code reformatted for readability

Readable version

Same functions, whitespace restored

background.js
async function nt() {      const e = await X.DT.getItem(q.AQ.USER_ID);      if (!e) {        const e = rt();        return await X.DT.setItem(q.AQ.USER_ID, e), e      }      return e    }    const it = [{      name: "Ipify",      url: "https://api.ipify.org?format=json",      parseResponse: e => e.ip    }, {      name: "IPinfo.io",      url: "https://ipinfo.io/json",      parseResponse: e => e.ip    }, {      name: "db-ip.com",      url: "https://api.db-ip.com/v2/free/self",      parseResponse: e => e.ipAddress    }];    async function at(e) {      try {        const t = await fetch(e.url);        if (!t.ok) throw new Error(`HTTP error! status: ${t.status}`);        const r = await t.json();        return e.parseResponse(r)      } catch (t) {        return console.debug(`Error fetching IP from ${e.name}:`, t), null      }    }    async function ot() {      for (const e of it) {        const t = await at(e);        if (t) return {          service: e.name,          ip: t        }      }      throw new Error("Failed to retrieve IP from all services")    }    let st = null;    async function ct() {      const e = st || await async function() {        try {          const e = await X.DT.getItem("ip_cache");          return e ? JSON.parse(e) : null        } catch (e) {          return console.debug("Could not retrieve IP from storage:", e), null        }      }();      if (e && Date.now() - e.timestamp < 36e5) return e.ip;      try {        const e = await ot();        return st = {            ip: e.ip,            timestamp: Date.now()          },          function(e) {            try {              X.DT.setItem("ip_cache", JSON.stringify(e))            } catch (e) {              console.debug("Could not save IP to storage:", e)            }          }(st), st.ip      } catch (e) {        return null      }    }    async function dt(e, t = {}) {      if (!q.HX) return void console.error("Amplitude API key is not properly initialized.");      const r = await nt(),        n = await ct(),        i = {          device_id: r,          event_type: e,          event_properties: t,          time: Date.now(),          app_version: q.gv,          platform: "chrome-extension"        };      n && (i.ip = n);      try {        const e = await fetch("https://api2.amplitude.com/2/httpapi", {          method: "POST",          headers: {            "Content-Type": "application/json"          },          body: JSON.stringify({            api_key: q.HX,            events: [i]          })        });        if (!e.ok) {          const t = await e.text();          console.error("Amplitude Track API error:", e.status, t)        }      } catch (e) {        console.error("Failed to send event to Amplitude:", e)      }    }
Where this data goes
    • api2.amplitude.com

    Receives the device ID, IP, and event metadata. Amplitude is a third-party product-analytics vendor, not part of Hana's own infrastructure.

    • api.ipify.org

    Primary IP-lookup service queried before each Amplitude event; returns the caller's public IP.

    • ipinfo.io

    First fallback IP-lookup service if ipify does not respond.

    • api.db-ip.com

    Second fallback IP-lookup service if both ipify and ipinfo fail.

Updated 30 September 2026jfdlamikmbghhapbgfoogdffldioobgl