Is Nightly safe?
Nightly Wallet is medium risk. When the DeFi view loads for a connected Solana or Movement wallet, Nightly's code puts the wallet address in a Trackall positions URL sent to trackall.nightly.app with the Trackall API header. A prior network capture missed this request.
Who publishes itAkudama GmbH - no other listings under this identity
Akudama GmbH - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Wallet address sent to Trackall from the DeFi tab
When the DeFi view loads for a connected Solana or Movement wallet, Nightly's code puts the wallet address in a Trackall positions URL sent to trackall.nightly.app with the Trackall API header.
A prior network capture missed this request.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You open or refresh Nightly's DeFi portfolio tab for a connected Solana or Movement wallet.
The extension requests portfolio positions from Trackall with the wallet address in the URL.
The request is built for the selected chain and sent to trackall.nightly.app.
- Wallet address9xQeWvG816bUx9EPfXx5Zfxx2XWyZtQkRdrk8b4Z5Y6n
This identifies the blockchain account whose portfolio positions are being requested.
- Chain namesolana
This tells the remote service which network the wallet address belongs to.
- Positions URLhttps://trackall.nightly.app/api/solana/positions?address=9xQeWvG816bUx9EPfXx5Zfxx2XWyZtQkRdrk8b4Z5Y6n
The address is part of the web address itself, so it can be logged by the remote service and network intermediaries that record URLs.
- Trackall API headerx-api-key: <redacted>
This marks the request as coming from Nightly's extension client rather than from the mobile client.
No response body was captured in the traffic-only run; behavioral verification confirmed the shipped code path that builds this GET request.
- x-api-key
- <redacted>
The DeFi tab dispatches refresh actions that lead to Trackall requests
Solana DeFi tab refresh handler
chunks/DeFiTab-QSG7uFs5.jsJ = () => { const e = (0, P.c)(11), s = c(), t = l(S), r = l(f), n = l(h); let i, a, o, x; if (e[0] !== s || e[1] !== n ? (i = () => { n && s(m.refreshSolanaDeFi({ silentRefresh: !0 })) }, a = [s, n], e[0] = s, e[1] = n, e[2] = i, e[3] = a) : (i = e[2], a = e[3]), (0, B.useEffect)(i, a), !n) { let s; return e[4] === Symbol.for("react.memo_cache_sentinel") ? (s = (0, U.jsx)(z, { isLoading: !1, items: [] }), e[4] = s) : s = e[4], s } return e[5] !== s ? (o = () => s(m.refreshSolanaDeFi({})), e[5] = s, e[6] = o) : o = e[6], e[7] !== r || e[8] !== t || e[9] !== o ? (x = (0, U.jsx)(z, { isLoading: r, items: t, onClickRefresh: o, isRefreshDisabled: r, isRefreshAnimating: r }), e[7] = r, e[8] = t, e[9] = o, e[10] = x) : x = e[10], x}Movement DeFi tab refresh handler
chunks/DeFiTab-QSG7uFs5.jsX = () => { const e = (0, P.c)(11), s = c(), t = l(_), r = l(d), n = l(b); let i, a, o, x; if (e[0] !== s || e[1] !== n ? (i = () => { n && s(m.refreshMovementDeFi({ silentRefresh: !0 })) }, a = [s, n], e[0] = s, e[1] = n, e[2] = i, e[3] = a) : (i = e[2], a = e[3]), (0, B.useEffect)(i, a), !n) { let s; return e[4] === Symbol.for("react.memo_cache_sentinel") ? (s = (0, U.jsx)(z, { isLoading: !1, items: [] }), e[4] = s) : s = e[4], s } return e[5] !== s ? (o = () => s(m.refreshMovementDeFi({})), e[5] = s, e[6] = o) : o = e[6], e[7] !== r || e[8] !== t || e[9] !== o ? (x = (0, U.jsx)(z, { isLoading: r, items: t, onClickRefresh: o, isRefreshDisabled: r, isRefreshAnimating: r }), e[7] = r, e[8] = t, e[9] = o, e[10] = x) : x = e[10], x}The Trackall client puts the wallet address into the positions request
Trackall client initialization and positions request
chunks/store-C7D8UbGk.jsvar vy = { extension: "<redacted>", mobile: "<redacted>" }, Ay = (e = "extension") => function(e, t) { const n = t?.origin ?? function() { if ("undefined" == typeof window) return "http://localhost" }(); return a(e, { headers: { ...t?.apiKey && { "x-api-key": t.apiKey }, ...n && { origin: n } } }) }("https://trackall.nightly.app", { apiKey: vy[e] }), Oy = (e, t) => { if (e.error) throw new Error(`${t}: ${String(e.error)}`); if (null === e.data) throw new Error(t); return e.data }, by = async ({ clientApp: e } = {}) => Oy(await Ay(e).platforms.get(), "Failed to fetch Trackall platforms"), Ny = async ({ chain: e, address: t, useCache: n = !0, clientApp: r }) => { const a = (({ chain: e, address: t, clientApp: n }) => { const r = Ay(n); switch (e) { case "solana": return r.api.solana.positions({ address: t }); case "movement": return r.api.movement.positions({ address: t }) } })({ chain: e, address: t, clientApp: r }); if (n) { const t = Oy(await a.cache.get(), `Failed to fetch cached Trackall ${e} positions`); if ("error" in t) throw new Error(t.error); return t.positions } return Oy(await a.get(), `Failed to fetch Trackall ${e} positions`) }Solana saga passes the selected wallet address
chunks/store-C7D8UbGk.jsfunction* xy(e) { try { const t = yield* Lo(In.network), n = yield* Lo(St.publicKey); if (t !== fr.SOLANA || n !== e.payload.publicKey) return; e.payload.silentRefresh || (yield* Xo(oi.clearDeFiData({ network: fr.SOLANA, publicKey: e.payload.publicKey })), yield* Xo(oi.setNetworkLoading({ network: fr.SOLANA, isLoading: !0 }))); const r = yield* Yo(() => Ny({ chain: "solana", address: e.payload.address, useCache: e.payload.useCache, clientApp: "extension" })), a = yield* Yo(() => by({ clientApp: "extension" })), o = yield* Yo(() => My({ chain: "solana", positions: r, platforms: a })), s = yield* Lo(In.network), i = yield* Lo(St.publicKey); if (s !== fr.SOLANA || i !== e.payload.publicKey) return; yield* Xo(oi.setTrackallDeFiData({ network: fr.SOLANA, portfolioValue: o.portfolioValue, items: o.items, publicKey: e.payload.publicKey })) } catch (t) { return void console.log(t) } finally { const t = yield* Lo(In.network), n = yield* Lo(St.publicKey); t === fr.SOLANA && n === e.payload.publicKey && (e.payload.silentRefresh || (yield* Xo(oi.setNetworkLoading({ network: fr.SOLANA, isLoading: !1 }))), yield* Xo(oi.setShowDeFiTab({ network: fr.SOLANA, show: !0 }))) }}Movement saga passes the selected wallet address
chunks/store-C7D8UbGk.jsfunction* Uy(e) { try { const t = yield* Lo(In.network), n = yield* Lo(xs.network), r = yield* Lo(Ct.publicKey); if (t !== fr.MOVEMENT || n !== $r.MAINNET || r !== e.payload.publicKey) return; e.payload.silentRefresh || (yield* Xo(oi.clearDeFiData({ network: fr.MOVEMENT, publicKey: e.payload.publicKey })), yield* Xo(oi.setNetworkLoading({ network: fr.MOVEMENT, isLoading: !0 }))); const a = yield* Yo(() => Ny({ chain: "movement", address: e.payload.address, useCache: e.payload.useCache, clientApp: "extension" })), o = yield* Yo(() => by({ clientApp: "extension" })), s = yield* Yo(() => My({ chain: "movement", positions: a, platforms: o })), i = yield* Lo(In.network), c = yield* Lo(xs.network), d = yield* Lo(Ct.publicKey); if (i !== fr.MOVEMENT || c !== $r.MAINNET || d !== e.payload.publicKey) return; yield* Xo(oi.setTrackallDeFiData({ network: fr.MOVEMENT, portfolioValue: s.portfolioValue, items: s.items, publicKey: e.payload.publicKey })) } catch (t) { return void console.log(t) } finally { const t = yield* Lo(In.network), n = yield* Lo(xs.network), r = yield* Lo(Ct.publicKey); t === fr.MOVEMENT && n === $r.MAINNET && r === e.payload.publicKey && (e.payload.silentRefresh || (yield* Xo(oi.setNetworkLoading({ network: fr.MOVEMENT, isLoading: !1 }))), yield* Xo(oi.setShowDeFiTab({ network: fr.MOVEMENT, show: !0 }))) }}- trackall.nightly.app
Receives the Solana or Movement portfolio positions lookup with the selected wallet address in the request URL.