← /audit

Audit from the user's perspective — 0x2ALL (v9.0 edition)

Prompt applied: AUDIT_PROMPT_USER_en.md v2.1 (public, reproducible) — unchanged Execution date: October 21, 2026 Auditor: DeepSeek v4.1 Flash (1017) — four review tracks (money, malware, network, custody) with manual verification of the items that change the verdict Source audited: commit 9f69edc — v1.0.21 (code 21). The file tree that produced the APK is this commit (the feature landed in 36115b0; 9f69edc fixes the Strategy screen spacing); later commits are documentation and site Binary examined: 0x2ALL-1.0.21-9f69edc-release.apk, SHA-256 325245a6542212f0227eb7616f9a550bcd0053e5d7c613a66d080c60698bf830, 6,677,553 bytes, certificate 53dac6cbe7edd6894b9377c4bbdc302a883abbe18a01ad2bd87d0a0fafa142c1 Supersedes: v8.0 edition (1.0.20, 6814b8b) Scope: prompt v2.1 — the design decisions declared in §2b are outside the scope of any finding, but were verified one by one.


0. What changed in this edition

This is the re-audit of binary 1.0.21. The delta since the v8.0 edition (1.0.20, 6814b8b) is one functional change and one text correction:

What changes for the user, in plain words. If the phone has root or an unlocked bootloader, the app now says so instead of pretending everything is fine — and it will not let them put new money into a device where the recovery phrase can be read by any app with root. Anyone who already has funds there can still withdraw, send and view the phrase: the app warns and blocks the way in, never the way out.

Why this weakens no protection. The check is local — nothing is sent, nothing is stored and no device identifier is read. There is no new endpoint, permission or funds destination: the manifest <queries> entry only makes it visible whether a root manager is installed. Permissions (13), hosts (12) and 0x addresses (7) remain identical to 1.0.20.

Declared limit. Detection is heuristic: Magisk DenyList/Shamiko, a repackaged APK or a hook (Frida) defeat it. It serves the honest user and the moment before the first deposit — not an attacker. The app never claims that "no warning = safe".

Re-audit. All 30 answers were re-checked against the new tree and binary. Proof that the code audited is the code signed: 83 new classes in the DEX, the domain ones being DeviceIntegrity, DeviceIntegrity$Finding and MainActivityKt$DeviceIntegrityGate* (the rest are renumbered Compose lambdas; 66 equivalent old classes left); the strings androidboot.verifiedbootstate (4 occurrences), androidboot.flash.locked, test-keys, kernelsu and /system/xbin/su in classes.dex; and the 10 new warning/state keys per locale in resources.arsc for all 6 locales. Sizes: classes.dex +1,456 bytes, classes3.dex +12,196, resources.arsc +16,972.


1. Verdict

Criterion Result
Verdict APPROVED — 0 open findings
Critical 0
High 0
Medium 0
Low 0
Informational 5 (distribution, domain, record-correction and the new root detection, §3)

2. Direct answers

Group A — Money

# Question Answer Evidence
A1 Does the app have any path that takes money from the user beyond the declared 2% activation fee on each deposit? (recurring fee, profit share, subscription, per-trade fee, tip, "donation") NO. The only charge is 2% on each deposit, taken before the exchange credit. No fee on profit, per trade, per withdrawal, subscription or tip PaymentManager.kt:109-111 (2%), :127-138 (charge before deposit), :114-121 (flow order); search for "performance fee/profit share/management fee": 0 hits in app/src/main
A2 List all possible destinations of funds: on-chain transfers, exchange withdrawals, fee payment. For each one, state whether the destination is the user's own address, a declared fixed address embedded in the code, or something computed at runtime. Is any destination dependent on data received from the network? (Do not reproduce the literal address values — §1.2.) 3 destinations. (i) fee → fixed custody address embedded in the code; (ii) deposit → Hyperliquid Bridge2 contract; (iii) withdraw3 withdrawal → always the signing address itself. No destination comes from the network AppConfiguration.kt:32 (immutable constant), PaymentManager.kt:100-101; WalletManager.kt:92 (Bridge2); HyperliquidAPI.kt:1716 (destination = masterCredentials.address) and :1727
A3 Can the fee be charged twice for the same deposit? (app restart, bot resume, reinstall, importing the same wallet, two wallets with the same seed, network failure before confirmation) NO for the same deposit. Durable journal with identity <deposit>:fee; an indeterminate state never becomes a reversal and the operation resumes with the same identity. New deposits pay 2% again — declared PaymentOperationStore.kt:24-34; PaymentManager.kt:70-76, :198
A4 Is there a backdoor, "developer mode", feature flag, remote configuration or server response able to change the destination, amount or recipient of an operation? Does the app obey any instruction coming from the network about where to send money? NO. Base URL is a code constant; no remote config, feature flag, "dev mode" or HTTP response able to define the destination HyperliquidSigner.kt:38 (MAINNET_URL constant); no path that changes the destination
A5 Does the app request a token approval (approve/allowance) at any point? If so, what is the approved limit and can a third party spend it? NO. No approve/allowance is requested. The ERC-20 contract is used only for balanceOf and to build the USDC transfer ERC20Contract.kt:31; search for "approve(/allowance": 0 hits
A6 Do the seed or the private key leave the device on any path? (network, log, backup, clipboard, intent, external file, notification, screenshot) NO. The seed does not leave through network, log, backup, clipboard or intent; it appears only on screen under a presence gate seed logging search: 0 hits; MultiWalletManager.kt:422; MainActivity.kt:68-71 (FLAG_SECURE app-wide); allowBackup=false
A7 Can someone holding the APK extract the seed? Consider: rooted device, adb backup, external storage, logcat, memory dumps, APK repackaging. Relate this to D2 (declared decision). With root, yes — declared decision DD2: the Keystore key is autonomous so the bot can run 24h. adb backup no (allowBackup=false); logcat no; APK repackaging can change the fee constant, which is why the certificate is published and verified MultiWalletManager.kt:422; AndroidManifest.xml:55-57; ShareAppHelper.kt:33,42
A8 Can the user lose funds through a path they did not explicitly authorize? Describe the worst realistic case and the worst adversarial case. Relate this to D4 (declared decision). Realistic: loss through the strategy (D4 — 10–50x leverage, 100% sizing). Adversarial: compromised device → seed → all wallets; APK re-signed outside the official distribution TradingBot.kt:1257,1282-1285,1338; MainActivity.kt:68-71

Group B — Malware / on-device behavior

# Question Answer Evidence
B1 List all declared permissions and why each one is needed. Is any of them risky: SMS, contacts, calls, location, accessibility, device administrator, screen overlay, package installation, external storage reading? 13 in the APK, all functional. 11 declared in the manifest (internet, network, biometrics, notifications, vibration, foreground service, service data sync, wake lock, battery optimization, boot, exact alarm) + USE_FINGERPRINT (legacy biometrics API alias) + an AndroidX signature-level internal permission for dynamic receivers. None risky: no SMS, contacts, location, accessibility, admin, overlay or package installation AndroidManifest.xml:6-31; checked in the APK with aapt2 dump badging
B2 Does the app load code at runtime? (DexClassLoader, PathClassLoader, DEX/JAR download, eval, WebView with a JavaScript bridge, plugins) NO. Zero DexClassLoader, PathClassLoader, WebView, eval or plugin; zero native library (.so) in the APK search in app/src/main: 0 hits; find for .so: 0
B3 Which components are exported (exported=true)? What can a third-party app installed on the same device do with them? MainActivity (only opens the app; does not read the Intent) and BootReceiver (discards action ≠ BOOT_COMPLETED). All other components exported=false AndroidManifest.xml:46-93
B4 Is there any third-party library doing telemetry, analytics, ads, crash reporting or identifier collection? List all dependencies and what each one sends over the network. NO. No telemetry, analytics, ads, crash reporting or identifier collection search for firebase/crashlytics/analytics/sentry: 0 hits
B5 Does the app hide itself or resist removal? (fake icon, disappears from the app list, prevents uninstall, runs without a visible notification) NO. Normal icon, normal uninstall, the service notification is always visible AndroidManifest.xml; foreground service
B6 Does the app detect root, emulator, debugger or device "fingerprint"? For what purpose, and where does that data go? YES, locally (new in 1.0.21). It detects root/an unlocked bootloader to warn the user and block new custody. It does not detect emulator or debugger, reads no device identifier and sends nothing DeviceIntegrity.kt (signals in §0); <queries> in AndroidManifest.xml:31-51 (no permission)
B7 Does the app update itself, install APKs or download code without the user's explicit consent? NO. Does not query versions, does not download APKs, installs nothing; updating is a manual download from the site. The new official-site card only opens the user's browser when tapped — no byte of any APK is downloaded by the app search for UpdateChecker/UPDATE_CHECK_URL: 0 hits; SecurityScreen.kt:191-226 (ACTION_VIEW only); DEX with no native download
B8 Is there a background service that keeps acting after the user stops the bot, closes the app or revokes a permission? The foreground service acts only while the bot is active and stops when the user stops it; the notification is permanent and cannot be removed without stopping the service TradingBotService (foreground, visible channel)

Group C — Network and privacy

# Question Answer Evidence
C1 List exhaustively the domains and IPs the app can contact. Is every destination necessary (exchange, network RPC, official site)? Is there any undeclared domain, analytics, third-party CDN or collection endpoint? 4 operational hosts: api.hyperliquid.xyz, rpc.hyperliquid.xyz, arb1.arbitrum.io and https://0x2all.xyz (added in 1.0.14, unchanged here), which exists in the APK only as text (Security card and the privacy link, opened in the browser) and is never the target of a request: tapping it opens the browser via Intent.ACTION_VIEW, not an app connection. Plus two content hosts (Hyperliquid docs and a Google search started by the user). No analytics/collection CDN domain URL scan of app/src/main and DEX reconciliation: 12 hosts, identical set to 1.0.16 — none new, none removed; SecurityScreen.kt:39,196; HistoryScreen.kt:149
C2 Is all traffic HTTPS? Is cleartextTrafficPermitted set? Is there certificate pinning — and does it cover every host that receives sensitive data? YES, all HTTPS. cleartextTrafficPermitted=false in the network config; CA pinning on Hyperliquid and the Arbitrum RPC res/xml/network_security_config.xml:3; HyperliquidAPI.kt:460-461; ArbitrumRpc.kt:10
C3 What exactly goes in each request (public address, signature, order, app version)? Is any device, user or installation identifier sent? Public address, signature and order payload; no device, user or installation identifier HyperliquidAPI.kt (payloads); search for ANDROID_ID/AdvertisingId: 0
C4 Does the website server record IP, user-agent or access time? What is retained and for how long? Is that declared in the privacy policy, and is the policy true when compared to the code? The site records only blocks (event, country, region, route, time; no IP) and is served by Cloudflare. The policy declares this since v7: v6 claimed the opposite and was corrected in this edition — a policy that contradicts the code is a finding, not a wording detail website/src/worker.js:15-17; privacy.html §4 (v7)
C5 Does the privacy policy promise something the code does not do? (undeclared collection, undeclared retention, unlisted third party) NO (rechecked after the v7 fix). Policy and code agree on the verifiable points: data on device, no account, no analytics, block log without IP, no third party besides Hyperliquid/Arbitrum/Cloudflare privacy.html v7; code cited in C1–C4

Group D — Authenticity and integrity

# Question Answer Evidence
D1 Is the distributed APK signed? What is the certificate (SHA-256 fingerprint), is it the same one published on the site, and does the file's SHA-256 match the declared one? YES. APK signed with v2+v3, RSA-4096, certificate 53dac6cb…42c1 — the same published on the site; file SHA-256 matches apksigner verify --print-certs; sha256sum
D2 Does the audited source code correspond to the distributed APK? How can the user verify that themselves? Is there a reproducible build, or at least the APK and certificate hashes published alongside the report? Source↔APK correspondence verified by binary inspection (version, permissions, classes, hosts and contract addresses) — not a reproducible build, and that is declared in the limitations aapt2 dump badging; DEX reconciliation
D3 Does the app verify its own signature before sharing the APK (P2P distribution) or before updating itself? YES. P2P sharing aborts if the certificate is not the expected one ShareAppHelper.kt:33,42
D4 Does the audit report have a published hash? How does the user confirm that the text they are reading is the one that was audited and was not edited afterwards? YES. Every file has a published SHA-256 and an OpenTimestamps date proof audit.json; .ots files

Group E — Failure, loss and recovery

# Question Answer Evidence
E1 What happens if the user uninstalls the app with an open position on the exchange? Is the money still accessible another way (seed, exchange website) or is it lost? The money remains accessible through the seed (another wallet or importing into the app) and through the exchange itself; live positions are read from the exchange after a reinstall fill/position reconciliation; MultiWalletManager
E2 If the device is stolen, what can the thief do? Is the seed protected by biometrics/device credential? Is the screen protected against screenshots? Seed encrypted by the Keystore, screen protected against capture; custody actions require presence. Root exposes the seed (D2) — since 1.0.21 the app warns on launch and blocks deposits on a flagged device MainActivity.kt:68-71; DeviceIntegrityGate in MainActivity.kt:159-181; AppLockManager.requirePresence in DashboardScreen.kt:586-696
E3 Is there any path where the app deletes a wallet that still holds funds, or considers "empty" a wallet with funds (balance read error)? Deletion and offboard validate balances in every layer (on-chain USDC, ETH and exchange balance) before any destructive action; no balance = only dust, deletable with acceptance RageQuitController; cross-layer checks
E4 What happens if the exchange is down, the connection drops mid-order or the response is lost? Can the app duplicate an order, duplicate a charge or end up in an inconsistent state? Operations have a durable journal and idempotent identity; an indeterminate state duplicates neither charge nor withdrawal PaymentOperationStore.kt; HyperliquidOperationStore; PaymentManager.kt:70-76,198
E5 If the process is killed by the system with an open position, does the stop loss remain valid on the exchange? Does the app reconcile on return? Stops live on the exchange and remain valid with the app dead; the app reconciles and recreates a missing stop on return reconcileStopWithExchange (checkPositions, 2-min cycle)

2b. Declared design decisions

ID Declaration Verified? Effect on the user Accepted risk
DD1 Fee address not published YES — immutable constant (AppConfiguration.kt:32), no setter; value absent from app and site (search = 0) The destination cannot be checked from public text Destination not verifiable by reading
DD2 Keystore key autonomous YES — setUserAuthenticationRequired(false) (MultiWalletManager.kt:422) with a presence gate on manual actions The bot runs with the device locked; manual actions require presence A rooted device exposes the seeds; not a remote vector
DD3 Automatic profit withdrawal (D12) YES — US$ 22 trigger per event (ProfitTracker.kt:46) and 50% fraction (:52) Realized profit above the trigger is partly withdrawn to the user Depends on autonomous custody (D2)
DD4 100% equity sizing, 10–50x leverage YES — equity read fail-closed (TradingBot.kt:1252-1258), position of 100% (:1270), asset max leverage (:1192-1202) Large positions with distant targets; total loss possible in a strong reversal Total or partial loss of capital

None of the four was classified as a finding, per the scope. What was verified is that each one is implemented as declared; one that was not would be a finding.


3. Findings

0 open findings (critical, high, medium or low).

Informational notes, no mandatory action:

  1. Repackaging in P2P distribution. Whoever receives the APK by sharing may receive a file re-signed by a third party. The defence exists (certificate fingerprint in the app, on the site and checked when sharing), but it relies on the user comparing the value.
  2. D1 is not verifiable from public text. It is an accepted consequence of the design decision.
  3. Note 3 of v7.0 resolved. The 0x2all.xyz domain has been registered and live since 2026-10-20; the policy link and the authenticity card open a real destination, and the page publishes the certificate fingerprint and the APK SHA-256.
  4. Correction of a previous edition's record. Note 4 of v7.0 claimed the old T5 block had become unreachable code, citing two copies of the stillOpen test. Rechecked in the 1.0.19 tree (0c96c95) and the 1.0.20 tree (6814b8b): there is a single occurrence (TradingBot.kt:2121-2123), already so in the previous edition. The duplication had been removed before 1.0.19 and the note is withdrawn — no effect for the user.

  5. Root detection is a warning, not a guarantee. The 1.0.21 check (DeviceIntegrity) is heuristic and runs on the device: Magisk DenyList/Shamiko, a repackaged APK or a hook (Frida) return whatever result the attacker wants. It is worth it for the honest user and for the moment before the first deposit; it does not replace a dedicated device without root and with a screen lock. No data leaves the device and no identifier is read.


4. Limitations


5. How to reproduce

  1. Download the APK on the download page and check the SHA-256 and the certificate fingerprint (table on this page and version.json).
  2. Download the prompt (/audit/prompt-en.md or prompt-pt.md) and apply it to the signed APK: apksigner verify --print-certs, aapt2 dump badging/xmltree, DEX decompilation (jadx or baksmali) and a sweep of hosts and 0x addresses in the DEX.
  3. Compare the SHA-256 of each file on this page with the published table and audit.json.
  4. Verify the date proof with ots verify on the .ots files.
  5. Compare the answers with the tables above. Any divergence is a finding.

The repository is private and is not required: the file:line references point to the source of the cited commit; auditing only the APK, what you check is the behaviour and the DEX strings.


6. Binary and source examined

Item Value
Commit 9f69edc (feature in 36115b0; spacing fix in 9f69edc; remaining commits are docs/site)
Version 1.0.21 (code 21)
APK examined 0x2ALL-1.0.21-9f69edc-release.apk
APK SHA-256 325245a6542212f0227eb7616f9a550bcd0053e5d7c613a66d080c60698bf830
Size 6,677,553 bytes
Certificate (SHA-256) 53dac6cbe7edd6894b9377c4bbdc302a883abbe18a01ad2bd87d0a0fafa142c1
Signature v2 + v3, RSA-4096, CN=0x2ALL
Permissions 13 in the APK, identical to 1.0.20 (empty diff)
DEX hosts 12, identical set to 1.0.20 (empty diff)
0x… addresses in DEX 7, identical to 1.0.20 (empty diff)
New DEX classes 83 total; domain ones: DeviceIntegrity, DeviceIntegrity$Finding, MainActivityKt$DeviceIntegrityGate* — the remainder are renumbered Compose lambdas (66 equivalent old classes left)
Execution date 2026-10-21