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.
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:
security/DeviceIntegrity.kt checks for root or an unlocked
bootloader on the device itself, once per process, and the app reacts in three places: a
blocking warning on every launch (before any content screen), a "Device integrity" card on the
Security screen and a block on new custody — creating a wallet, importing, on-ramping and
depositing. Money OUT (withdraw, send, consolidate, offboard, panic and revealing the phrase)
stays available.app/build.gradle.kts — bump 1.0.20 → 1.0.21 (code 21).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.
| 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) |
| # | 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 |
| # | 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) |
| # | 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 |
| # | 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 |
| # | 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) |
| 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.
0 open findings (critical, high, medium or low).
Informational notes, no mandatory action:
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.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.
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.
version.json)./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.audit.json.ots verify on the .ots files.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.
| 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 |