Is OP_WALLET safe?

Medium risk

OP_WALLET sends a persistent device ID and Bitcoin wallet address to its servers on every browser start.

Each time the browser starts, the extension transmits the user's Bitcoin wallet address and a persistent 12-character random device ID (stored in chrome.storage.local) in HTTP headers to wallet.opnet.org. On BTC price fetches, a deterministic hash of the user's quantum public key is embedded in the URL path sent to api.motoswap.org, functioning as a stable wallet-linked identifier. The extension also injects a script tag into every webpage that exposes a full Bitcoin wallet API object (window.opnet) to any page script, allowing any site to detect wallet presence and prompt account access.

opnetv1.9.0Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

Wallet hash placed in Motoswap price API path

After a wallet account is configured, OP_WALLET's BTC price flow reads the quantum public key hash from preferences, puts it in a GET path to api.motoswap.org.

DA didn't capture it: it stayed at setup, rejecting an account without a key.

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

You open the wallet after an account has been set up.

The popup starts its BTC price loading flow when the price provider mounts.

The extension did this

The extension reads your wallet-derived hash and builds a Motoswap API URL with that value in the path.

The request is only built when the stored current account contains quantumPublicKeyHash.

Values placed into the Motoswap request path
  • Wallet quantum public key hash
    0x4f3a1b2c9d0e8f76543210abcdef1234567890ab (illustrative)

    This stable wallet-derived value can let the receiving service recognize the same configured wallet across price checks.

  • Chain name
    bitcoin

    This tells the service which blockchain context your wallet is using for the price lookup.

  • Network name
    mainnet

    This tells the service whether the lookup is for mainnet, testnet, signet, or another configured network.

Captured request
GEThttps://api.motoswap.org/api/v1/bitcoin/mainnet/0x4f3a1b2c9d0e8f76543210abcdef1234567890ab/sync

No live response was captured because wallet setup was not completed during dynamic analysis.

Headers
Content-Type
application/json
X-App-Platform
web
The code that does this

The popup starts the price flow and the background request embeds the wallet hash

Readable version

Popup price provider starts getBtcPrice() on mount

ui.js
function Fi({  children: e}) {  const t = Ei(),    [r, n] = (0, Pr.useState)(!1),    [a, i] = (0, Pr.useState)(0),    o = (0, Pr.useCallback)(() => {      Mi || Date.now() - Li < 30 * 1e3 || (Mi = !0, n(!0), Li = Date.now(), t.getBtcPrice().then(e => {        e > 0 && i(e)      }).catch(e => {}).finally(() => {        n(!1), Mi = !1      }))    }, [t]);  (0, Pr.useEffect)(() => {    o()  }, [o]);  const s = {    isLoadingBtcPrice: r,    btcPrice: a,    refreshBtcPrice: o  };  return (0, Dr.jsx)(Di.Provider, {    value: s,    children: e  })}

Background BTC price method tries Motoswap before public price APIs

background.js
async getBtcPrice() {  const e = await this.fetchPriceFromMotoswap();  if (e > 0) return e;  const t = await this.fetchPriceFromPublicAPIs();  return t > 0 ? t : this.cachedBtcPrice}

Background Motoswap fetch reads quantumPublicKeyHash and inserts it into the URL

background.js
async fetchPriceFromMotoswap() {  try {    const e = preference_default.getChainType(),      t = preference_default.getCurrentAccount();    if (!t?.quantumPublicKeyHash) return 0;    const {      chain: n,      network: r    } = this.getChainAndNetwork(e), i = `https://api.motoswap.org/api/v1/${n}/${r}/${t.quantumPublicKeyHash.startsWith("0x")?t.quantumPublicKeyHash:`0x${t.quantumPublicKeyHash}`}/sync`, s = await this.fetchWithTimeout(i, {      method: "GET",      headers: {        "Content-Type": "application/json",        "X-App-Platform": "web"      }    });    if (!s.ok) return 0;    const o = (await s.json()).exchangeRates?.rates?.usd?.value;    return "number" == typeof o && o > 0 ? (this.updateCachedPrice(o), o) : 0  } catch {    return 0  }}

Where it sends data

Destinations our analysis observed OP_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.

  • api.motoswap.org

    OP_WALLET sends data to api.motoswap.org. No other extension we have analysed sends data here.

  • wallet.opnet.org

    OP_WALLET sends data to wallet.opnet.org. No other extension we have analysed sends data here.

Updated 30 September 2026pmbjpcmaaladnfpacpmhmnfmpklgbdjb