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

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

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.

01EvidenceCAUSE EFFECT
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.

02EvidenceFIELD TABLE
Values placed into the Motoswap request path
FieldValueWhy it matters
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
bitcoinThis tells the service which blockchain context your wallet is using for the price lookup.
Network name
mainnetThis tells the service whether the lookup is for mainnet, testnet, signet, or another configured network.
03EvidenceNETWORK CAPTURE
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-Typeapplication/json
X-App-Platformweb
04EvidenceCODE COMPARE
The code that does this

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

What it actually does
Popup price provider starts getBtcPrice() on mountui.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 APIsbackground.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 URLbackground.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