Is IOTA Wallet safe?

Medium risk

IOTA Wallet sends detailed financial activity events — transactions, DApp connections, and key exports — to Amplitude's analytics servers.

When users perform wallet actions such as sending coins, approving transactions, connecting to DApps, or exporting account keys, the extension captures those events along with associated metadata and transmits them via HTTPS to Amplitude's EU-region endpoint. The Amplitude SDK is initialized with a hardcoded API key and also uses navigator.sendBeacon to flush data on page hide and visibility-change events. No confirmed claims establish data collection beyond what the Amplitude integration describes.

tooling-adminv1.10.0Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

IOTA Wallet sends wallet-action analytics to Amplitude

Using select IOTA Wallet features records analytics for sending coins, connecting a DApp, or opening export-keys.

Events POST via HTTPS to Amplitude's EU host with the API key, device ID, and metadata like coin type.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You perform a tracked wallet action inside IOTA Wallet.

Examples in the shipped code include sending coins, connecting a DApp, responding to a transaction request, and choosing Export Account Keys.

The extension did this

The extension creates an analytics event for that action and sends it to Amplitude.

Dynamic analysis observed a POST to Amplitude after wallet activity, and the source contains the matching event names and properties.

02EvidenceNETWORK CAPTURE
Captured request
POSThttps://api.eu.amplitude.com/2/httpapi
Dynamic analysis observed the request after wallet activity; the evidence notes a body containing the hardcoded API key, a device ID, and an Amplitude page-location field.
03EvidenceFIELD TABLE
Concrete fields shown by the code and observed traffic
FieldValueWhy it matters
Analytics project key
2a5d35822a1bab41835813f0223f319eTells Amplitude which analytics project receives the event from your extension.
Your analytics device ID
d4f2c6b8-9a01-4f32-9d7c-0d1e2f3a4b5c (illustrative)Lets the analytics service group multiple wallet events from the same browser profile.
Your page location
chrome-extension://iidjkmdceolghepehaaddojmnjnkkija/ui.html (illustrative)Shows the extension page context associated with the wallet event.
Your wallet action
sent coinsRecords which wallet feature you used, such as sending coins or starting a DApp connection.
Your action details
coinType: IOTA; amount: 1.25; applicationUrl: https://app.example.iota.org (illustrative)Adds action-specific details, such as the coin type and amount for a send or the application URL for a DApp request.
Your approval result
approvedTransaction: true; receivedFailureWarning: falseShows whether you approved a transaction request and whether the approval followed a warning prompt.
Your account type
Private KeyWhen you open the export-account-keys flow, the analytics event records the account type selected in the wallet.
04EvidenceCODE COMPARE
The code that does this

The shipped extension initializes Amplitude and maps wallet actions to analytics events

What it actually does
Amplitude API key and EU server-zone configurationbackground.js
let sa = {
        iotawallet: "2a5d35822a1bab41835813f0223f319e"
      },
      su = {
        plan: {
          version: "3",
          branch: "main",
          source: "web",
          versionId: "41c28342-e260-4c3e-8465-6618203f115a"
        },
        ...{
          ingestionMetadata: {
            sourceName: "browser-typescript-ampli",
            sourceVersion: "2.0.0"
          }
        },
        serverZone: tu.EU
      };
EU endpoint selector used by the bundled Amplitude SDKui.js
nQ=function(t,n,i){if(void 0===t&&(t=""),void 0===n&&(n=nX().serverZone),void 0===i&&(i=nX().useBatch),t)return{serverUrl:t,serverZone:void 0};var a,o=["US","EU"].includes(n)?n:nX().serverZone;return{serverZone:o,serverUrl:(a=i,"EU"===o?a?"https://api.eu.amplitude.com/batch":"https://api.eu.amplitude.com/2/httpapi":a?"https://api2.amplitude.com/batch":nj)}}
Amplitude load function and beacon flush handlersui.js
async function le(){var t;let n,i,a;s8.load({environment:"iotawallet",disabled:!1,client:{configuration:{optOut:!1,identityStorage:"cookie",autocapture:{attribution:!1,fileDownloads:!1,formInteractions:!1,pageViews:!0,sessions:!0,elementInteractions:!1,frustrationInteractions:!1,networkTracking:!1,webVitals:!1,pageUrlEnrichment:!0},logLevel:mB.None,flushIntervalMillis:1e3,flushQueueSize:5}}}),s8.client.add((t=s8.client,n=null,i=null,{name:"dialog-context",type:"enrichment",setup:async()=>(n="u"<typeof window||"u"<typeof document?()=>{}:(document.addEventListener("click",rK,!0),()=>{document.removeEventListener("click",rK,!0)}),i=function(t){if("u"<typeof window||"u"<typeof document)return()=>{};let n=new MutationObserver(n=>{for(let i of n)i.addedNodes.forEach(n=>{if(n instanceof HTMLElement){var i,a;((null==(i=n.matches)?void 0:i.call(n,rH))?n:null==(a=n.querySelector)?void 0:a.call(n,rH))&&setTimeout(()=>{let n=r$();t.track("Dialog Opened",{dialog_title:n})},50)}})});return n.observe(document.body,{childList:!0,subtree:!0}),()=>{n.disconnect()}}(t),Promise.resolve()),execute:async t=>{if("u"<typeof window||"u"<typeof document)return t;let n=rU.get(),i=(null==n?void 0:n.dialogTitle)??r$();return i?{...t,event_properties:{...t.event_properties,dialog_title:i}}:t},teardown:async()=>(n&&n(),i&&i(),rU.clear(),Promise.resolve())})),window.addEventListener("pagehide",()=>{s8.client.setTransport("beacon"),s8.flush()}),document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&(s8.client.setTransport("beacon"),s8.flush())}),s8.client.add((a="dev_",{name:"environment-plugin",type:"enrichment",setup:async()=>{},execute:async t=>{let n=t.event_type;return t}}))}
Event classes for the wallet actions named in the claimui.js
class sF{event_properties;event_type="exported account keys";constructor(t){this.event_properties=t,this.event_properties=t}}class sq{event_properties;event_type="opened application";constructor(t){this.event_properties=t,this.event_properties=t}}class sU{event_properties;event_type="opened link";constructor(t){this.event_properties=t,this.event_properties=t}}class sV{event_properties;event_type="opened transaction";constructor(t){this.event_properties=t,this.event_properties=t}}class sH{event_properties;event_type="pinned coin";constructor(t){this.event_properties=t,this.event_properties=t}}class s${event_type="renamed account"}class sK{event_type="reset wallet"}class sG{event_properties;event_type="responded to connection request";constructor(t){this.event_properties=t,this.event_properties=t}}class sW{event_properties;event_type="responded to transaction request";constructor(t){this.event_properties=t,this.event_properties=t}}class sY{event_properties;event_type="selected coin";constructor(t){this.event_properties=t,this.event_properties=t}}class sJ{event_properties;event_type="selected validator";constructor(t){this.event_properties=t,this.event_properties=t}}class sX{event_properties;event_type="sent coins";constructor(t){this.event_properties=t,this.event_properties=t}}class sZ{event_properties;event_type="sent collectible";constructor(t){this.event_properties=t,this.event_properties=t}}class sQ{event_properties;event_type="staked IOTA";constructor(t){this.event_properties=t,this.event_properties=t}}class s0{event_properties;event_type="started dapp connection";constructor(t){this.event_properties=t,this.event_properties=t}}
Wallet-action call sites and concrete event propertiesui.js
onClick:function(){let n=null==t?void 0:t.type;n&&s8.exportedAccountKeys({accountType:qJ[n]}),m(`/accounts/export/${t.id}`)}
(0,dg.useEffect)(()=>{if(s){let t=lo(s.name,s.origin);s8.startedDappConnection({applicationName:t,applicationUrl:s.origin})}},[s])
onSuccess:t=>(f.invalidateQueries({queryKey:["get-coins"]}),f.invalidateQueries({queryKey:["coin-balance"]}),s8.sentCoins({coinType:g,amount:Number(x.values.amount)}),u(`/receipt?txdigest=${encodeURIComponent(t.digest)}&from=transactions`))
onSubmit:async n=>{if(!p){if(n&&v)return void d(!0);await l(Cy({approved:n,txRequestID:t.id,signer:s})),eiw.includes(t.origin)||s8.respondedToTransactionRequest({applicationUrl:t.origin,approvedTransaction:n,receivedFailureWarning:!1})}}
onResponse:async n=>{await l(Cy({approved:n,txRequestID:t.id,signer:s})),s8.respondedToTransactionRequest({applicationUrl:t.origin,approvedTransaction:n,receivedFailureWarning:!0}),d(!1)}
05EvidenceTHIRD PARTY LIST
External analytics destination
  • api.eu.amplitude.com

    Amplitude EU analytics ingestion receives the wallet-action events over HTTPS.

06EvidenceTEMPORAL PATTERN
When this fires
When a batch threshold is hit
Flushes when 5 events are queued.

The extension is configured to flush analytics quickly after activity, with a one-second flush interval and a queue size of five events.

Data recipients

api.eu.amplitude.com
Updated 17 September 2026iidjkmdceolghepehaaddojmnjnkkija