Insights

The XRPH Wallet Hack: 4,011 Wallets Emptied, and Every Key Was Guessable

On the evening of 3 September 2026 an address that did not exist an hour earlier received the entire balance of 4,011 wallets. Three hours later the money was on a bridge to Ethereum. We read the whole thing off the ledger, then pulled the wallet app from Google Play and took it apart.

In one screen

4,011
wallets emptied
267,664 XRP
swept from them
23.2M XRPH
plus 2.43M XRPHAI taken
3 h 3 min
from the first sweep to DAI on Ethereum
−91%
XRPH price at the low
445,198 DAI
where it sits now, unmoved
  • All 4,011 wallets belong to XRPH Wallet users. We checked every one on the ledger: 98.6% carry the app's own fingerprint, 95.5% were created in it, and none ever used other wallet software.
  • The sweep started at 22:07 UTC and ran biggest wallet first. By 22:30 the collector held 242,536 XRP.
  • 311,613 XRP went into NEAR Intents, came out on Ethereum as 178.46 ETH within a minute of each deposit, and became 445,198 DAI at one address that has not moved since.
  • XRP Healthcare confirmed the count and the DAI figure. We followed every step to that address ourselves.
  • We took the app apart. Payments are signed on the phone; the seed is stored unencrypted on the phone; and the staking feature sends the seed to XRP Healthcare's server. That staking code still ships.
  • The reason every wallet could be emptied is in how the app makes keys. It feeds the key generator a line of text instead of raw randomness, and draws the digits from Math.random(). Fourteen digits end up deciding a wallet, about 2^46 possibilities, small enough to rebuild every key offline with no server and no phone. XRP Healthcare's developers published the same finding on 7 September.
  • 1,225 wallets staked, nearly all between December 2023 and July 2024. 1,198 of them were drained, and so was the company's own staking wallet, whose key only the server holds. Yet seven in ten victims never staked: the exposure is the app's whole user base, not one feature.
  • Asked on X, XRP Healthcare first said the wallet "is designed as a non-custodial wallet", then that it "was not aware that any part of the wallet's staking functionality was less than fully non-custodial" and is putting the question to the developers who wrote it.
  • Our verdict: no. The keys of every wallet it made are already weak and rebuildable, so existing wallets cannot be made safe; they must be remade with a corrected app. What would change that is listed near the end.
Words used below
Seed. the secret string that controls a wallet; whoever has it can sign for that wallet
Trustline. the entry a wallet must open before it can hold a token such as XRPH
Reserve. XRP the ledger locks in every account, 1 XRP plus 0.2 per trustline; a sweep leaves it behind
Collector. the attacker’s address that every victim’s balance was sent to
Hop wallet. a fresh address the money passes through on its way out
NEAR Intents. a cross-chain swap service: it takes XRP in on this ledger and pays another asset out on another chain, here ETH on Ethereum
Source tag. a number some wallet apps stamp on the transactions they send; the XRPH Wallet stamps none
Destination tag. a number a sender attaches to say what a payment is for; the staking server used 6 and 12
Non-custodial. a wallet where only the user holds the seed; the XRPH Wallet is listed as one
XRPH, XRPHAI. XRP Healthcare’s two tokens

Minute by minute

3 to 4 September 2026, UTC
21:36:32
The collector is born
The address everything will be swept into, funded with 15.4 XRP by rhAtfg…VM4Y.
21:37 to 21:39
Five trustlines in three minutes
XRPH, XRPHAI, RLUSD, USDT and USDC: the four tokens the app opens a trustline for on every new wallet, plus one.
22:07:40
The first sweep is the biggest
97,829 XRP from rUdG4c…KMkx, its whole balance minus the reserve.
22:14:31
The 25th wallet
Twenty-five unrelated wallets have now emptied into one address that is 38 minutes old. This is the moment the detector we describe at the end would have tripped.
22:30
90% of the XRP is in
242,536 XRP arrived in the first half hour. The sweep runs biggest wallet first.
22:38 to 23:01
XRPH goes on sale
Six swaps on XPMarket turn the stolen XRPH into 28,921 XRP in 23 minutes. The token loses 80% of its price inside the hour.
00:06 to 00:20
Second dump
Two more swaps, 11,043 XRP. XRPH prints its low of 0.000856 XRP at 00:50, down 91%.
00:42:32
307,000 XRP leaves for NEAR Intents
To a deposit address the bridge had issued for the attacker. The bridge sweeps it into its own wallet, r9R8jc…QitH, nine seconds later, and credits the attacker's account seven seconds after that.
00:42:53
Swapped and withdrawn as ether
On NEAR, 305,311 XRP becomes 175.81 ETH and leaves for Ethereum in one transaction. It lands at 0x3DC7BF…8930 eighteen seconds later.
01:04:01
The rest follows
4,614 XRP the same way: 2.65 ETH on Ethereum at 01:04:35.
01:10:11
178 ETH becomes 445,198 DAI
One swap through Uniswap's router. The DAI has not moved since.
4 Sep
XRP Healthcare speaks
"Please do not use the XRPH Wallet until further notice." A later update: 4,011 wallets, about $452,000, traced to about 445,000 DAI sitting on one Ethereum wallet. Both figures match what we read.
From the first sweep to ether on Ethereum: 2 hours 36 minutes. To DAI: 3 hours 3 minutes.

Nothing here required an exploit of the XRP Ledger. Each wallet signed an ordinary payment of its own balance. The ledger shows what the keys did, not how the attacker got them, and XRP Healthcare says that part is still under investigation. What the app itself does with those keys is further down.

Biggest wallets first

XRP arriving at the collector per half hour, and how many wallets sent it
15
21:30
5 wallets
242,536
22:00
738 wallets
14,082
22:30
1,588 wallets
8,602
23:00
1,463 wallets
2,067
23:30
1,270 wallets
180
00:00
719 wallets
198
00:30
236 wallets
The script sorted victims by balance. The 738 richest wallets went first and carried 90.6% of the XRP; the last 236 sent 198 XRP between them.

10,281 payments from 4,011 wallets: nearly all inside three hours, the last stragglers 27 hours later, 4,256 of them token transfers. Forty-five of the emptied accounts were then deleted, which sends the reserve to the collector as well.

Who was hit

What each wallet sent, XRP
under 1 XRP1,584 (39.5%)
1 to 20 XRP1,789 (44.6%)
20 to 100 XRP515 (12.8%)
100 to 1,000 XRP88 (2.2%)
1,000 to 10,000 XRP33 (0.8%)
over 10,000 XRP2 (0.0%)
Most victims were small. 1,584 wallets held under 1 XRP and 1,789 held between 1 and 20. Two wallets held more than 10,000, and the largest, 97,829 XRP, went first.
Where the victims' wallets were first funded from
Personal wallets and others1,887 (47.0%)
Coinbase472 (11.8%)
Binance307 (7.7%)
Uphold293 (7.3%)
MEXC238 (5.9%)
Crypto.com150 (3.7%)
Bybit83 (2.1%)
Bitrue75 (1.9%)
Revolut56 (1.4%)
Kraken56 (1.4%)
Ordinary retail wallets: created between 2023 and 2026 (554, 2,151, 1,016 and 242 per year), funded from personal wallets and from a dozen exchanges. No two victims are linked on chain except through the app they used.

The dump

XRPH price in XRP, 3 to 5 September (30-minute samples)
0.00983 XRP before0.000856 at 00:50, −91%0.00314 two days later, −68%sweep and dump, 21:36 to 00:57
23.2 million XRPH taken from 2,024 wallets and sold through XPMarket in 14 swaps for 42,057 XRP. The selling, not the theft, is what holders saw first.

The attacker did not wait to cash out. The first XRPH swap came 31 minutes after the first sweep, while wallets were still being emptied. XRPHAI went the same way: 2.43 million taken from 199 wallets and sold with five offers for 1,272 XRP in total. The market for it was that thin.

Where the money went

The collector, rGGXaB…xHVB: in and out
Swept from 4,010 wallets · whole balances, reserve left behind267,664 XRP
From selling the stolen tokens · 14 swaps on XPMarket for 42,057 XRP, and five offers that sold all 2.43M XRPHAI for 1,27243,329 XRP
Reserves of the 45 accounts the script then deleted · deleting an account sends its last XRP to the collector827 XRP
Into NEAR Intents, out as 175.81 ETH on Ethereum · deposit, credit, swap and withdrawal inside 40 seconds from 00:42:32307,000 XRP
Into NEAR Intents, out as 2.65 ETH · 01:044,614 XRP
Service fees to Xaman, a popular XRPL wallet app · the attacker did the swapping through it217 XRP
It reconciles to the drop: 311,836 XRP in, counting the attacker's own 15.4 to open the account, 311,831 out, 4.5 left. 178.46 ETH came out on Ethereum; 178 of it was swapped for 445,197.999 DAI, which still sits at the same address. NEAR Intents is a public service, not a party to the theft.

The collector kept 4.5 XRP. The wallet that created it, rhAtfg…VM4Y, dates from 2024, was funded by a withdrawal from the exchange VALR, and created nothing else. The two addresses the XRP passed through on its way out were not the attacker's: NEAR Intents issues a fresh XRPL address to each depositor, credits the deposit the moment it lands, and sweeps the address into its own wallet with a key of its own. That is why each was created by the attacker's payment and emptied nine seconds later.

From the collector to the DAI, every step, 4 September 2026 UTC
XRPL4 Sep 00:42:32307,000 XRP from the collector to a deposit address NEAR Intents had issued for the attacker15430E73FA…
NEAR00:42:48The bridge credits 307,000 XRP to the attacker’s intents account, naming that XRPL paymentF4HeE2d6qt…
NEAR00:42:53Swapped with a solver for 175.81 ETH and withdrawn to Ethereum, one transaction, memo WITHDRAW_TO:0x3DC7…8930An8z3wD7eb…
Ethereum00:43:11175.809 ETH arrives from the bridge’s wallet0xc18beb4462…
XRPL01:04:014,614 XRP the same way, through a second deposit address33CC212316…
NEAR01:04:20Swapped for 2.65 ETH and withdrawn to the same Ethereum address5nBcH9S8Ri…
Ethereum01:04:352.648 ETH arrives0x4212d86002…
Ethereum01:10:11178 ETH swapped through Uniswap’s router for 445,197.999 DAI0x67e11aa47e…
EthereumsinceThe DAI has not moved. One outgoing transaction ever, 0.45 ETH left for gas0x3DC7BF…8930
The bridge's own records tie the chains together: its NEAR credit names the XRPL payment it came from, and its withdrawal names the Ethereum address it went to.

Inside the app

The build in both stores on 3 September was 8.0.15: on Google Play since 28 July, on the App Store since 30 July, 50,000+ installs on Android, listed as a non-custodial wallet. We installed it from Google Play, decompiled its 1.3 million lines of JavaScript, and ran it on a rooted emulator with every encrypted connection opened, using a throwaway wallet funded with real XRP and XRPH.

8.0.15
the build in both stores since late July
0
times the seed appeared in 2,631 requests and 3,373 socket frames
2 of 2
payments left the phone already signed
What leaves the phone, and where it goes
Your phoneKey from Math.random(), cut to ~2^46Seed, private key, password, PINkept in clear, app-private storageSigns every payment itselfNo certificate pinningBackups disabledXRPL nodeOne of four mainnet nodesReceives signed transactions onlyXRP Healthcare serverCloudflare Worker and FirebaseAccount, address book, merchantprofile, stakesShared API key, token valid to 5195Send XRP or XRPHsigned on the phone, sent already signedSign upaddress, name, passwordStake or unstakethe seed itself; 1,225 wallets did thisNode listfrom Firebase
Solid arrows were observed on the wire. The dashed one is in the code and was live from December 2023 to July 2024; we had nothing staked, so it never fired in our session.
Seen live
Payments are signed on the phone
Both payments we sent left the phone already signed with the wallet's own key and went straight to an XRPL node. The seed, the private key, the entropy behind them and the secret numbers appear in none of the 2,631 requests or 3,373 socket frames, in any encoding; the server never even received the public key.
Seen live
The seed sits in clear on the phone
The app's local database holds the seed, private key, password and PIN unencrypted, outside the phone's secure key store. Other apps cannot read it and backups are switched off, so getting at it takes root access, malware with root access, or the unlocked phone in hand. The PIN and fingerprint unlock gate the screen, not the data; the PIN itself sits in clear beside the seed.
In the code, live Dec 2023 to Jul 2024
Staking posts the seed to the server
Staking sends { amount, address, secret: seed, stakeType } to XRP Healthcare's server, and unstaking sends the seed again. 1,225 wallets used it, nearly all between December 2023 and July 2024. The screen now says: "Due to the scarcity of XRPH tokens, staking is no longer available. Existing stakers may continue until their current period ends." We had nothing staked, so this path never fired in our session. The code ships in the current build, and unstaking still uses it.
Seen live
An account system behind a non-custodial wallet
Sign-up posts the address, a display name and a password to XRP Healthcare's server, and no seed: the account is keyed on the wallet address, not the key. The password protects nothing on the phone: it encrypts nothing, sits in clear next to the seed, and before every send the app asks the server whether it is correct and waits for a yes. Every call carries a shared API key the app fetches from the company's configuration, plus a session token that expires in the year 5195.
Seen live
Wallet sync does not sync the wallet
The store notes promise "wallet sync". The only thing the app reads from its cloud backend is the list of XRPL nodes to talk to; nothing about the wallet is written there.
Seen live
No certificate pinning
The app trusts any certificate the phone trusts, so one we planted on the phone decrypted all of its traffic. That needs a compromised phone or network first, so it is not a mass vector on its own.
Seen live
Telemetry carries no wallet data
Fourteen crash-reporting uploads to Sentry and the Firebase Analytics events went out with no address or key in them. The vendor's websites, a Linktree page and a swap page that says "coming soon" open inside the app and bring their own trackers, and the app asks for the phone's advertising ID.
In the code
The key is made from a small, guessable number
The wallet's key is not built from the phone's secure random source. The app draws eight six-digit blocks from Math.random(), joins them into a line of text, and passes that to Wallet.fromEntropy(), which expects raw bytes; the text is read as single-digit bytes and all but the first sixteen characters are dropped. Fourteen digits are left, so every wallet the app ever made is one of about 2^46 (72,899,838,000,090) and can be rebuilt offline. This is the defect that empties the wallets, set out in "Two paths to a weak key".
In the code
Nothing hidden in the rest of it
No over-the-air update channel, so the code on a victim's phone was the store build. No WalletConnect, no hardcoded keys beyond a testnet tutorial's, seventeen standard native libraries, and a support form that sends a description and device details to the company's server. Root and emulator checks exist but did not stop the app on a rooted emulator.

Where does the seed actually leave the phone for the operator? In the staking feature. Staking, unstaking and claiming rewards each read the seed straight off the stored account and post it as secret to XRP Healthcare's worker. The panel shows the stake and unstake code as it comes out of the decompiler; the table lists the staking calls and one more.

As decompiled from the Hermes bytecode of build 8.0.15
// Staking screen  (bundle_decompiled.js 836356-836377, then 836426)
r7['amount']    = r8;
r9 = r10.classicAddress;   r7['address']   = r9;
r9 = r10.seed;             r7['secret']    = r9;      // the wallet seed
r9 = _closure2_slot3;      r7['stakeType'] = r9;
r8 = r7.bind(r8)(r6);                                 // useStake().mutateAsync(body)

// useStake  (660203-660210)
r3 = r1.client;
r2['method'] = 'POST';
r2['data']   = a0;                                    // the body above, seed included
r1 = 'wallet/stake';
r1 = r3.bind(r4)(r1, r2);                             // client('wallet/stake', ...)

// Unstake sheet  (837292-837305)  and  useWithdrawStake  (660304-660310)
r9 = r10.seed;   r7['secret'] = r9;   r4['secret'] = r7;   r4['id'] = r8.unStakeId;
r1 = 'wallet/withdrawStake/' + id;    r1['method'] = 'POST';   r1['data'] = r6.secret;

// API client base  (472706-472708)
r1 = 'https://xrphealthcare.sptdmw.workers.dev';
What it amounts to
// stake
const body = { amount, address: account.classicAddress, secret: account.seed, stakeType };
client('wallet/stake', { method: 'POST', data: body });

// unstake or claim
client('wallet/withdrawStake/' + unStakeId, { method: 'POST', data: { secret: account.seed } });
Staking and unstaking read the seed straight off the stored account and post it as secret to the worker; claiming rewards uses the same withdrawStake call. The ordinary send does the opposite: it passes the seed to a local signing routine (Wallet.fromSeed, then submit to a node) and only the signed transaction leaves the phone, which is what both our live sends showed. Line numbers refer to our decompiled bundle; the APK hash in the sources lets anyone reproduce them.
Where the seed is sent to the operator
HandlerSeed in the body asBundle lineStatus
Stakesecret: seed836356Used
Unstake / withdrawsecret: { secret: seed }837278Used
Claim rewardssecret: { secret: seed }859505In the code
Swap approval (socket)walletKey: seed1020472In the code
// staking sends the seed to the worker as "secret"
stake          client('wallet/stake', { amount, address, secret: seed, stakeType })
unstake        client('wallet/withdrawStake/' + id, { secret: { secret: seed }, id })
claim          client('wallet/withdrawStake/' + id, { secret: { secret: seed }, id })
                 // worker base:  https://xrphealthcare.sptdmw.workers.dev

// the swap-approval screen emits the seed as walletKey over a socket (swap is disabled)
swap approval  socket.emit('swap-response', { walletKey: seed, ... })

// the ordinary send does the OPPOSITE — it signs on the device, the seed never leaves:
send           Wallet.fromSeed(seed).sign(tx);  submitAndWait(signed)
Staking posts the seed as secret to the worker (stake, and unstake and claim via withdrawStake); the swap-approval screen emits it as walletKey over a socket, though the in-app swap is disabled. The ordinary send is not here: it signs on the device with Wallet.fromSeed and submits only the signed transaction, so the seed stays put, which is what our live sends showed. Staking is confirmed used from the ledger; the swap path sits in the code. Line numbers refer to our decompiled bundle.

The one other place the seed is put on the wire is the swap-approval screen, which emits it as walletKey over a socket; the in-app swap still reads "coming soon", so that path sits in the code unused. The ordinary send does the opposite: both the plain send and the password-gated send hand the seed to a local signing routine (Wallet.fromSeed, then submit to a node), and only the signed transaction leaves, which is why our live sends showed no seed on the wire; the payment-review step only writes the details into the app's own state. So the seed reaches the operator through the staking feature, and would through the dormant swap screen, not through sending. This is separate from the key defect below, which is what actually emptied the wallets, but every wallet that staked also handed the operator its seed.

What the app keeps on the phone: its local database, read with root from our test phone
seedsEd•••••••••••••••••••••••••••the wallet itself, in clear
privateKeyED••••••••••••••••••••••••••••••in clear
password••••••••••••in clear, and sent to the server at sign-up
pin••••••in clear, beside the seed it is meant to guard
tokeneyJhbGciOi•••••session token, expires 21 July 5195
worker_key••••••••••••••••shared API key, sent with every call
Nothing here is encrypted. The storage is private to the app and the manifest disables backups, so it is out of reach of other apps and of a phone backup, and within reach of root, root-level malware, or anyone holding the unlocked phone.

Two paths to a weak key

This is the part that empties a wallet without touching it. When the app makes a wallet it never uses the phone's secure random source. It builds the "padlock" the user is shown, eight blocks of six digits, and turns that into the wallet's secret key. Two separate mistakes in how it does so shrink the key from a number no one could ever guess to one a normal computer can list in full.

As decompiled from the Hermes bytecode of build 8.0.15
// The digit source — generateRandomNumber  (bundle_decompiled.js 364276)
r0 = Math.random();                       // <-- ordinary RNG, not the phone's secure one
r1 = r0 * 899999;
r0 = r1 + 100000;                         // one 6-digit block: 100000..999998
return r0;

// createNewPadlock  (364394): eight blocks -> one space-joined string
padlock = { A, B, C, D, E, F, G, H };     // each = 6 digits from the line above
r6 =  A.join('') + ' ' + B.join('') + ' ' + C.join('') + ' ' + D.join('')
    + ' ' + E.join('') + ' ' + F.join('') + ' ' + G.join('') + ' ' + H.join('');
r5 = Wallet.fromEntropy;
r3 = r5.bind(Wallet)(r6);                 // a 55-char STRING is passed; fromEntropy wants 16 bytes

// generateNewWallet  (654121, recovery path) — the same call
r2 = Wallet.fromEntropy;
r5 = r2.bind(Wallet)(a1);
What it amounts to
// what xrpl + ripple-keypairs then do with that string
entropy = Uint8Array.from("528875 921568 528239 ...")  // '5'->5  '2'->2  ' '->0   (bytes 0-9)
entropy = entropy.slice(0, 16)                          // keep 16 chars, drop the other 39

// 14 digits are left to decide the wallet:
//   block A (6) + space(0) + block B (6) + space(0) + first 2 of block C
//   blocks D..H — 30 of the 48 random digits — never touch the key
//
//   899,999  x  899,999  x  90  =  72,899,838,000,090   (about 2^46)
Two independent faults on the same few lines: the digits come from Math.random(), not a secure generator, and they are handed to Wallet.fromEntropy() as text, so the library reads them as single-digit bytes and keeps only the first sixteen characters. Line numbers refer to our decompiled bundle; the APK hash in the sources lets anyone reproduce them.

Path one, the wrong type. The eight blocks are joined into a single line of text and handed to Wallet.fromEntropy(), a function that expects sixteen raw bytes, not text. The line is read one character at a time, so every byte ends up holding a value of only 0 to 9 instead of the full 0 to 255 a real key byte carries, and only the first sixteen characters are kept. Thirty of the forty-eight random digits never reach the key at all, and two of the sixteen survivors are spaces, which count as zero. Fourteen digits decide the entire wallet.

Path two, the wrong source. Even those fourteen digits come from Math.random(), the everyday generator a program uses to shuffle a list or drive an animation, not the operating system's secure one. It was never built to make a key, and on its own it can only make the real number of reachable wallets smaller, never larger.

Put together, the number of possible wallets falls from 2^128, a figure with thirty-nine digits that no one could search in many lifetimes, to about 2^46: 72,899,838,000,090, roughly seventy-three trillion. That is small enough to list every possible key on an ordinary computer and check each one against funded accounts on the ledger. Rebuilding a key needs nothing from the owner and nothing from any server. It fits every part of what the ledger showed on 3 September: the whole user base rather than one feature, each withdrawal signed with the account's own key, and one automated sweep.

XRP Healthcare's own developers reached the same finding and published it on 7 September, tracing the fault to a change made on 13 June 2023 and reproducing it against their code: three padlocks that agree only in their first fourteen digits produce one identical wallet address. Their root-cause report is the company's first explanation of why the wallets could be emptied. The same code sits in the build on Google Play today.

Staking was live for eight months

The "staking is no longer available" message is not a leftover. From December 2023 to July 2024 XRP Healthcare ran staking through the app, at 25% a year for a twelve-month lock and 20% for six, and because staking moves tokens, the ledger holds every stake. They all went to one wallet, r9ejnH…vK5b, created by the company's treasury the week before launch.

November 2023 to September 2026
13 to 30 Nov 2023
Announced, previewed, in final testing
"Staking Launching Soon", a preview of the staking screen, then "undergoing final testing". In the replies: 25% a year for a 12-month lock, 20% for six months; a shared screenshot later shows early-exit penalties of 12.5% and 10%.
27 Nov 2023, 07:59
The staking wallet is created
Funded by the treasury rQD582…tDo7. Twelve minutes later the first test stake of 100 XRPH; 156 test payments from six wallets that month.
1 Dec 2023, 05:15
The first user stakes 50 XRPH
214 wallets stake in December. Each one sent its seed to the server to do it.
24 Jan to 13 Apr 2024
Milestones posted, and what the ledger had
"10M total staked" on 24 January: the ledger has 8.8 million in stakes by then. "Over 16 million" on 2 April: 17.8 million. "Over 17 million" on 13 April: 20.5 million. April is the peak, 458 wallets and 992 stake payments.
22 and 23 Jul 2024
New staking halted
The last stake before the halt lands at 00:41 on the 22nd. The next day the company announces it is halting new staking "to preserve finite supply".
2025
Existing stakers carry on
680 payouts to 365 wallets during the year. The last new stake, a rollover of 66 XRPH, on 30 November 2025. Unstaking sends the seed to the server again.
3 Sep 2026, 23:45
The staking wallet is emptied too
A check cancelled to free its reserve, then 0.78 XRPH and 1.79 XRP into the collector. That key was never on a phone: the server that pays stakers holds it.
Dates of posts are taken from their status ids. Stake payments are signed by the server, which is why it needs the seed; the transactions carry the user's own key because the server signs with it.
1,225
wallets staked through the app
33.5M XRPH
staked in 3,531 payments; the largest 935,000
1,198
of those wallets were drained
Wallets that staked each month, and the XRPH they sent
214
Dec 23
6.4M XRPH
166
Jan 24
3.1M XRPH
148
Feb
3.8M XRPH
176
Mar
4.0M XRPH
458
Apr
4.6M XRPH
216
May
3.0M XRPH
229
Jun
4.7M XRPH
128
Jul
1.5M XRPH
Eight months, then a stop: one wallet in August 2024, and a few rollovers by existing stakers later. Every bar is a set of seeds arriving at the server.

New stakes stopped in July 2024. The seed did not stop leaving: unstaking and rollovers kept posting it until at least 30 November 2025, and the code is in the build in the stores today. The stake request names no token, only an amount, an address, the seed and a lock type, so a new staking round, for XRPH or for XRPHAI, the second token the company launched in January 2025 and promotes inside the app for "reward boosts", would send every new staker's seed to the server again without a line changing.

Every hacked wallet, checked

Of the 4,011 wallets that paid into the collector, one was the attacker's own, funding it with 15.4 XRP; the other 4,010 are the victims, and together they lost 267,664 XRP. We read the full history of each from a full-history node, accepted it only once its first transaction sat within a day of the day the wallet was created, and looked for the marks the XRPH Wallet leaves on the ledger.

The 4,010 hacked wallets
The 267,664 XRP they lost
Used staking: seed sent to the server
1,199 wallets (29.9%) · 148,397 XRP (55.4%)
The app’s fingerprint, never staked
2,755 wallets (68.7%) · 119,256 XRP (44.6%)
No mark
56 wallets (1.4%) · 12 XRP (0.0%)
Stakers are three in ten wallets and over half the money. The 56 with no mark are high-traffic wallets that lost 12 XRP between them.
All 4,010 hacked wallets, full histories, checked for the marks the app leaves on the ledger
Born in the XRPH Wallet3,830 of 4,010 · 95.5%
the app’s trustlines opened within a day of activation
Carrying the app’s fingerprint at all3,954 of 4,010 · 98.6%
that trustline shape, or the memo the app puts on every send; the 56 without either are high-traffic wallets that lost 12 XRP between them
Stakers that were drained1,198 of 1,225 · 97.8%
of the 27 others, 3 were deleted, 20 held nothing above the reserve, 4 are large holders with untouched master keys
Ever used any other wallet software0 of 4,010 · 0%
not one source tag on any transaction they ever sent; the app’s only backup is a “padlock combination” of eight six-digit numbers, which Xaman can import but few people ever move
Never sent a payment at all1,246 of 4,010 · 31%
1,204 of them born in the app; their seed existed only where the app put it
The hacked wallets are the app's user base, almost to the wallet. Nearly everyone who staked was drained; most of the drained never staked.
A stake, signed by the server
TransactionTypePayment
Destinationr9ejnHY…2vK5b, the staking wallet
DestinationTag6 or 12: the lock in months, from 5 Apr 2024
AmountXRPH
Memosnone
SourceTagnone
Payouts come back the same way: XRPH out of that wallet, no memo, no tag.
The app’s trustline
TransactionTypeTrustSet
LimitAmount.value10000000000
LimitAmount.issuerXRPH, XRPHAI, RLUSD or USDT
SourceTagnone
Opened by the app itself when the wallet is created, all four in a row. Our test wallet did it nine times.
The app’s send
TransactionTypePayment
Memos[{ Memo: { MemoData: "" } }]
SourceTagnone
Flags131072 on token sends
A memo on every payment, empty when the user typed none. Other wallets add none, and stamp a source tag.
Highlighted fields are the mark. Every example is a real transaction; open any of them on the explorer and compare.
Six stakers end to end: the stake, its tag, the payout, the drain
walletstakelockpayoutdrained, 3 Sep 2026
rDMqsn…o4y914 Apr 2024, 50 XRPHtag 620 Jan 20269,000.2 XRP
rw4VW6…HQwC5 Apr 2024, 3,636 XRPHtag 617 Jun 20253,013.2 XRP
ram8De…bbtW11 May 2024, 1,099.5 XRPHtag 630 Nov 20242,427.2 XRP
rBMgXW…WoTe6 Jun 2024, 1,244 XRPHtag 66 Dec 20242,021.8 XRP
rPn9z9…i72a10 Apr 2024, 1,687 XRPHtag 624 May 20251,559.5 XRP
rM5Cbp…T2Ed6 Jun 2024, 13,190 XRPHtag 67 Dec 20241,221 XRP
Three wallets born in the app that never sent a payment, and were drained all the same
walletcreatedthe app's trustlinedrained, 3 Sep 2026
rehrNN…fyJL22 Apr 2025A11E26AE74…5,745.5 XRP
rNLyFo…3TXG13 Dec 202398E68C769D…5,070.1 XRP
rJRTkj…z4sq22 Mar 202680DE99CE20…3,371.2 XRP
Every row is three or four public transactions. The full list is 4,010 wallets long and was built the same way.

Download all 4,010 addresses (CSV) — each with the XRP swept from it, the number of token transfers, and whether it staked or was created in the app. Sorted by amount; the columns are the checks above.

  • The staking wallet went the same way at 23:45 that night, by the same script: a check cancelled to free its reserve, then its 0.78 XRPH and 1.79 XRP into the collector. No phone ever held that key. The server that pays stakers does, and the attacker used it.
  • Stakers are three in ten victims and held more than half the XRP. The other seven in ten are the same kind of wallet in every other respect: born in the app, used nowhere else, and 1,204 of them never sent a payment in their life. Nothing in build 8.0.15 sends their seed anywhere.
  • "Used nowhere else" is partly the format. The app never shows a seed or a recovery phrase; its only backup is a "padlock combination", eight six-digit secret numbers, which Xaman can import but most people never carry across. Either way, every one of these wallets was signed for by the XRPH Wallet alone.

What the app explains, and what it does not

  • The seed is put on the wire to the operator by the staking feature: stake, unstake and claim all post it to the worker as the field "secret". The dormant swap-approval screen would send it too, as "walletKey" over a socket. The ordinary send does not: it signs on the device and keeps the seed. Staking is confirmed used from the ledger, so every one of the 1,225 stakers handed the operator their seed. This is a separate failure from the key defect that actually emptied the wallets.
  • The reason it is the whole user base and not just stakers is the key defect, not the seed-to-server paths. Weak keys reach every wallet the app ever made, staker or not; the seed-to-server paths only reach wallets that used those features. That is why 98.6% of the drained wallets carry the app's fingerprint, none ever used another wallet, and seven in ten never staked, yet all were emptied in one sweep.
  • The alternatives for that remainder are weak: theft from phones does not scale to 4,011 wallets in three hours, the app has no over-the-air update channel, and no new build reached either store between late July and the hack.

Asked on X on 5 September whether seed phrases are ever transmitted to or stored on its servers, XRP Healthcare answered: "XRPH is designed as a non-custodial wallet, with users retaining control of their wallet credentials and funds." The staking code sends the seed to their server. Asked a second time, more specifically, they replied later that day: "XRP Healthcare was not aware that any part of the wallet's staking functionality was less than fully non-custodial, and we would never knowingly market a product as non-custodial if that weren't the case. This is now being put directly to the developers responsible for the wallet's code." They add that they have filed a report with law enforcement and asked every exchange and swap service in the funds' path to flag and freeze. It is the first acknowledgement that the staking feature was not non-custodial.

Their root-cause report names the key defect but says nothing about the seed being sent to their server. These remain open:

  • Which earlier builds sent the seed to the server for sending, creating, importing or syncing a wallet, and until when?
  • Where were the seeds received for staking kept, and was the staking wallet's own key kept with them?
  • Was that store breached from outside, or used by someone who had access?

Do we recommend it?

Do we recommend the XRPH Wallet?
No. Every key it ever made is already weak, so existing wallets must be remade, not patched.
What it does right
  • Signs payments on the phone, and we watched it do so.
  • Sends no wallet data to crash reporting or analytics.
What rules it out today
  • Every wallet it ever made can be rebuilt from public information: it passed the key generator a line of text and drew the digits from Math.random(), shrinking the space of possible keys from 2^128 to about 2^46. This is what emptied 4,011 wallets in one night.
  • Its staking feature posted users’ seeds to the company’s server, and the server-held staking wallet was drained the same night.
  • The seed sits unencrypted on the phone.
  • An account system with a shared API key and a session token that never expires, behind a wallet sold as non-custodial.
  • No certificate pinning.
  • The staking code still ships and unstaking still uses it; a new staking round, for XRPH or XRPHAI, would send every staker’s seed to the server again unless the code changes.
What would change the verdict
  • A corrected key generator, and every existing wallet migrated to a new key: the old keys are already weak and cannot be patched.
  • No feature, in any build, that sends the seed off the phone.
  • The seed in the phone’s secure key store, and sessions that expire.
  • An independent audit of the rebuilt app, published.
XRP Healthcare itself told users on 4 September not to use the wallet until further notice. We agree, and the right-hand column is what would lift it.

Until then, anyone holding XRP or XRPH should use a wallet whose keys never leave the device, created with a new seed. That is the same advice for everyone, not only for people who were hit.

Why no alarm went off

A wallet emptying itself into a brand-new address is the most common thing on the ledger: it is what a user moving to a new wallet does. Every drain detector, ours included, therefore ignores a single sweep unless the destination is already known to be a drainer.

What the ledger showed on 3 September was different in one respect only: hundreds of unrelated wallets did it to the same address inside minutes. That shape has no innocent explanation.

Distinct wallets emptied into one address under a week old, inside six hours
0 / 25tripped at 22:14:31, seven minutes after the first sweep
Over the 48 hours around the hack, 2,320 addresses received a whole-balance sweep. Exactly one crossed this line: the collector.

The rule now runs live in our ledger feed. It needs 25 distinct wallets, funded from at least five different sources and created over a span of at least 90 days, emptying into one address under a week old within six hours. Exchanges consolidate into old, named wallets; a person moves one balance; a deposit address carries a tag. None of them trip it.

If you used XRPH Wallet

  • Treat every key that was ever in the app as compromised. Move anything left to a wallet created with a new seed in different software.
  • Do not import the old seed into a new app. The seed is the problem, not the app it lives in.
  • If you ever staked in the app, your seed was posted to XRP Healthcare's server as part of that feature. One more reason the old seed cannot be kept.
  • Ignore anyone offering recovery for a fee or a signature. The funds are on Ethereum; nobody can pull them back by having you sign something.
  • Check any address before you pay it: our drain tracker and the scam list behind it are updated live.

Sources

  • Collector rGGXaBdSRUfdarKDkt2csxL67F8MEGxHVB, hops raDNxjwbjGCUAix8rKCzrHYY2r9AsCjs1P and rEwVZRuLvAAo5PkkU7ZTRPJ7km4hX7o6J7, and every transaction linked above, read from a full-history XRPL node on 5 September 2026.
  • Victim profile from our ledger index: the wallet that funded each of the 4,011 senders, its creation date and its trustlines. The downloadable list of all 4,010 victims carries the XRP swept from each, from the on-chain drain records.
  • XRPH price from our 5-minute candles of the XRPH/XRP market.
  • Wallet count and dollar figure: XRP Healthcare's statements on X, 4 September 2026. The Ethereum side we read ourselves, below; their DAI figure matches it.
  • The app: com.xrphwallet 8.0.15 (build 815) installed from Google Play on 5 September 2026, base APK SHA-256 98ef9778d75a5a630816798c1458f25cb3715e266d5e91ef8c7fe5d6925e07ce, Hermes bytecode decompiled with hermes-dec. Run on a rooted Android 13 emulator with a mitmproxy certificate installed system-wide; every HTTP request and WebSocket frame logged in full and searched for the test wallet's seed, private key, entropy in hex and base64, secret-number groups and public key.
  • Test wallet rEtETFXqPDKzgkF7pVrbBN2vnM4fRw8JkS, funded from our own wallet and emptied by us afterwards: DEX buy AC28524C93…, payments 03F8C1E70B… (0.3 XRP) and 50B4D9EB8C… (100 XRPH), both captured leaving the phone already signed.
  • Store listings read on 5 September 2026: Google Play (updated 28 July 2026, 50K+ downloads) and the App Store lookup service (8.0.15, released 30 July 2026, seller XRP Healthcare Limited).
  • Staking: the full history of r9ejnHYRqpUcdcVpP9jrubpgMGBvV2vK5b (7,574 transactions) and of its treasury rQD582WZt9xx4SVKfBcpwRBYJhSpretDo7, read from a full-history node on 5 September 2026. A stake is an XRPH payment into that wallet; the treasury's own top-ups and two exchange wallets are excluded from the user counts.
  • The wallet check: the full history of each of the 4,010 victims (the 4,011th payment into the collector is the attacker's own funding), read from a full-history node on 5 September 2026 and accepted only when its first transaction falls within a day of the creation date in our index, which 3,962 did at once; the rest were re-read until they did, and the 48 with no recorded creation date were read from two nodes with the longer answer kept. A partial answer from a load-balanced node looks complete and carries no error, which is why the check exists. Marks as defined in the anatomy figure.
  • XRP Healthcare's posts on X, dated from their status ids: 13 Nov 2023, 22 Nov 2023, 30 Nov 2023 (with the APR replies), 3 Mar 2024, 2 Apr 2024, 13 Apr 2024 and 23 Jul 2024, which links the press release "XRP Healthcare Halts New Staking of XRPH Token to Preserve Finite Supply".
  • Past the bridge: NEAR Intents' own transactions, read from an archival NEAR node and NearBlocks (the credits F4HeE2d6qt… and H1fcCZoizC…, each naming the XRPL payment it came from; the swaps and withdrawals An8z3wD7eb… and 5nBcH9S8Ri…, each naming the Ethereum address); Ethereum from Blockscout and a public node, balances read 5 September 2026 at 15:00 UTC: 0x3DC7BFf29Fc5a051aAB250516BaBD74ae7068930 holds 445,197.999216 DAI and 0.4527 ETH.
  • Promotion by other accounts, for the record: 21 Dec 2022, 18 Jan 2023, 24 Jan 2024, 14 Apr 2024, 14 Jan 2025 and 30 Aug 2026.