Security · Analysis
One Signature, Counted Twice, Emptied 2,321 Card Balances
An outdated Rain card contract on Solana required two independent approvals before money could move. It checked the first one, then looked for the second and found the first again. Four consumer brands carried the consequences; none of their customers had chosen Rain.
The instruction that failed was doing something banks have done for a century. Before a card-collateral account could release funds, the contract wanted two approvals — one from the account's owner, one from the program operating the card. Two signatures, two separate checks, the oldest control in finance.
The contract checked the first signature correctly. Then it went looking for the second, and what it found was the first one wearing a different label.
According to the security firm Blockaid, whose transaction-level reconstruction was reported by crypto.news, the attacker submitted a transaction containing two signature-verification instructions and simply pointed the second one's signature, public key and message offsets back at data sitting inside the first. The program dutifully verified the same approval twice and recorded two independent consents where one existed. Blockaid's description is that the attacker "reused one signature so it looked like two separate approvals."
This was not broken cryptography. Every signature checked in that transaction was mathematically valid. The failure was in bookkeeping — in the contract's assumption that two verification instructions must describe two different things.
From a duplicate to an administrator
What the flaw bought was not money directly. It was a role.
Having satisfied the authorization path through the affected program's SubmitSignatures operation, the attacker called AddCollateralAdmin and registered an address it controlled as an administrator on a user's card-collateral account. Only then did it call the withdrawal function. The exploit did not steal a key or defeat a wallet; it persuaded the contract to write the attacker into the list of people already trusted, and then used that trust normally.
The volume tells you it was automated well before it was executed. Blockaid counted 8,233 core exploit transactions across the episode: 2,945 administrator additions and 5,288 withdrawal calls. The first two successful withdrawals landed three seconds apart. The whole run took roughly two hours and twenty-nine minutes, beginning on August 28 — the attacker's wallet first touched the affected programs at about 16:49 UTC, per Gizmo Times. Nobody builds a 2,945-account admin-registration loop while improvising.
One caution on dates: a number of aggregators have placed this incident on August 30. The August 28 date is the one supported by the two accounts closest to the event — the on-chain first-contact timestamp above, and Tria's own notice to customers, which Spendnode reports went out that same day, with a fuller disclosure on August 29.
The proceeds moved the way proceeds move. Stolen USDC and USDT were swapped through decentralized exchanges into roughly 10,000 SOL, bridged via deBridge to Ethereum as about 455.9 ETH, and deposited into Tornado Cash in a window between 19:20 and 19:49 UTC.
Four brands, one contract, no customer choice
The part worth dwelling on is who was standing underneath this.
Rain is a card issuing platform. Consumers do not sign up with Rain, do not read Rain's documentation and, in most cases, have never encountered the name. They sign up with a consumer brand that runs its card program on Rain's rails. When one outdated version of Rain's Solana collateral contract turned out to accept a doubled signature, the failure surfaced simultaneously in several unrelated products.
What each program has actually disclosed
| Program | Users | Amount | Status |
|---|---|---|---|
| Avici | 1,685 | $500,859.22 | Refunded + 10% cashback |
| Tria | 636 | $431,945 | Refunded |
| Solayer Pay | not disclosed | not disclosed | Named as affected |
| Jupiter | none reported | none reported | Paused withdrawals as a precaution |
| Disclosed total | 2,321 | $932,804.22 | vs ~$1.1M moved |
Subtract one from the other and roughly $170,000 of the money that moved on August 28 belongs to no publicly disclosed victim list. That gap is not evidence of concealment — Solayer Pay has been named as affected without publishing a number, and it may account for all of it. But it does mean that the public accounting of this incident is, today, about 85 percent complete, and the missing piece sits with a program that has said the least.
To the credit of the operators, the customer-facing response was fast and unusually generous. Avici says affected Solana card balances were "restored in full," with an additional 10 percent cashback on top. Tria committed to full reimbursement of its 636 customers. Rain says it "upgraded every program that was still using the outdated contract version" and has committed to making affected users across its programs whole. Jupiter, which paused card balance withdrawals while the situation was assessed, has said its accounts and funds were never affected and that withdrawals returned to normal.
The word "outdated" is carrying the weight
Every account of this incident, including Rain's own, turns on a single adjective: the exploited contract was the outdated version. A newer, patched version existed. Some programs were still running the old code.
That framing answers one question and raises three better ones, none of which has been answered publicly. When was the fixed version published? What did the fix say it fixed — was the doubled-signature path a known defect, or did the upgrade close it incidentally? How were programs told to migrate, and how long had the ones that hadn't been sitting on vulnerable code?
Tria's message to its own customers was precise and, on its own terms, correct: "Your Tria wallet was never affected. Wallets and card balances live in different places." That distinction is real and worth understanding. Self-custodial wallets are governed by keys the user holds; a card-collateral account is governed by contract logic somebody else deploys and somebody else upgrades. Nothing about holding your own keys protects the second thing.
Rain has not published a full technical post-mortem — no source, no audit history, no statement of which versions were vulnerable and for how long. Nearly two weeks after the fact, the most detailed public account of what happened inside Rain's contract remains the one written by an outside firm reading the chain.
The Take
A two-of-two approval scheme that accepts the same signature twice is not an exotic bug. It is the kind of thing a competent audit of signature-verification handling is specifically for, and it is the kind of thing a test suite catches if anyone thought to write the test where both instructions point at the same offsets. That is worth saying plainly, and it is also not the most important thing here. The most important thing is that 2,321 people lost their card balances because of a contract version choice made by a company they had no relationship with, could not have evaluated, and in most cases could not have named. They were made whole quickly, which is genuinely to the credit of Avici, Tria and Rain — but being made whole is a matter of the operator's solvency and goodwill, not of any protection the arrangement gave them. Shared issuing infrastructure is how consumer crypto cards get built, and it will keep being how they get built, because the alternative is every brand writing its own collateral contract and getting it wrong separately. Fine. Then the price of that efficiency is disclosure: which version am I on, when was it patched, who audited it. Rain has upgraded the code. It has not yet published the history, and the history is the only part a customer could ever have used to protect themselves.