Back to Technology Insights
2026.08.28

One shared card contract, several fintechs affected at once — Rain's Solana implementation hit 1,685 Avici users and 636 Tria users

One shared card contract, several fintechs affected at once — Rain's Solana implementation hit 1,685 Avici users and 636 Tria users

What happened

In an announcement on 2026-08-28, Avici disclosed that Rain, its card-issuing partner, had identified a vulnerability in a version of the Solana card contract that holds user card balances. Per Avici, the contract has since been upgraded across all programs, and no further unauthorised activity has been observed after the fix. 1,685 users were affected across $500,859 in balances (about ¥78.1 million at 1 USD = 156 JPY; rate from Trading Economics, 2026-09-03), with full refunds pledged.

Tria, which flagged the issue the same day, put numbers to it in a follow-up on 3 September: the flaw sat in the Solana card contract run by its issuing partner Rain, and reconciliation to date shows 636 users affected across $431,945 in card balances (about ¥67.4 million at the same rate). Tria is also refunding in full and said remediation with Rain and its security partners is ongoing.

Both firms drew the same line, and it is a line about where money was sitting. Balances held in a self-custodial wallet were never in scope. When a user tops up a card with Solana assets, the funds leave the wallet and move into a separate contract, and only that contract was touched. Card payments at the point of sale continued to work across the affected programs; what broke was withdrawals and balance integrity. As of writing, the technical nature of the flaw — which account validation or authority check was missing — has not been published. With the fix already distributed, withholding detail is a defensible call, but it leaves developers wanting to check their own code with little to work from.

The real story is the shared component

The technically interesting part is not the bug but how the impact spread. Avici and Tria are separate companies with their own applications and brands. Both built on the same card-issuing partner's onchain component. The result: two firms became incident owners on the same day, from the same root cause, without changing anything in their own repositories.

This is not unique to crypto. Payments has long had shared dependencies in BIN sponsors, processors and KYC vendors. What differs is that when the shared piece is an onchain contract, the state of funds deposited there is public — and so is the fact, visible to an attacker, that one codebase is simultaneously guarding several companies' customer balances. The economics of attacking it are better than for a conventional shared vendor.

One design decision limited the damage: the separation of responsibilities was clear. Because wallet balances and card balances lived in different contracts, the affected scope could be stated as "topped-up funds only", and users could work out from their own behaviour whether they were affected. Had both sat in a single pool, the same flaw would have taken days to scope. Separation earned its keep here as explicability during an incident.

Working with what has not been disclosed

The awkward part for engineers is that the public record supports neither reproduction nor self-assessment. What is known amounts to: a version of a Solana card-balance contract had a vulnerability, a fix has been applied across all programs, and no further unauthorised activity has been seen. Which check was missing is not stated. Solana programs commonly fail through unvalidated account owners or PDA derivations, missing signer checks, or trusting the caller in a cross-program invocation — but there is no basis for asserting that any of these applies here. Building an internal checklist on speculation is poor practice.

What does work is reasoning backwards from how the impact spread. First: does your fund flow include a segment where balances sit in a partner's program? Second: can you control the ceiling and the dwell time of funds in that segment? Third: when a partner patches, do you have an independent way to confirm the fixed version is deployed? The third is the one most often skipped. A program's executable data account carries an update history, so the fact of an upgrade can be verified onchain without relying on the partner's word.

For clarity, this disclosure is unrelated to the other Solana-adjacent event of the same period — the 20 August crates.io compromise and the resulting concern about build pipelines. Proximity in time has led to the two being discussed together; no causal link has been established.

Pitfalls to watch

"Our code is audited" does not cover shared dependencies. Audit scope is normally the programs you deployed. If a partner-operated program ID appears in your fund flow, it sits in an audit blind spot.

You should be able to explain who holds upgrade authority. The fix here was described as rolled out across all programs — which means the partner could replace a program holding user funds. That enables fast remediation and is also a risk if the same authority is abused or misused. Know who holds it, and whether a timelock or multisig sits in front of it.

"Payments kept working" travels too easily as evidence of no impact. Point-of-sale spending continued while withdrawals and balance integrity were broken. When a path that looks healthy to users remains up, both internal detection and external reports arrive late. The question is whether you monitor "is the service usable" or "does the ledger reconcile".

A refund pledge is not completion. Tria has said reconciliation and refunds are ongoing. Users still need to confirm the refunded amount matches the pre-incident balance, and the operator needs to be able to state the reconciliation basis — which block or snapshot is treated as authoritative.

[Technical Insights & Actions]

  1. An onchain component you did not write may be holding your users' funds. Even when the flaw belongs to a vendor, the incident owner from the user's perspective is you — and this is exactly where audit scope and contractual responsibility tend to diverge. [Product / contract developers] This month, enumerate every program ID appearing in your fund flow and record, for each, who deployed it, who holds upgrade authority, and when it was last audited.
  2. Separating funds by purpose lets you scope an incident immediately. Wallet and card balances living in different contracts is what made the disclosure fast and the explanation clear. Separation costs something in normal operation and repays it in explicability during a failure. [Contract developers] By your next design review, identify pools that mix funds of different purposes and estimate how long scoping would take with and without separation.
  3. The upgrade authority that enables a fast fix is also a single point of failure. "We can patch it immediately" and "it can be swapped without you" are the same mechanism; a timelock or multisig is what distinguishes them. [Integrators / risk] This quarter, verify the upgrade authority of external programs holding your funds — single signer versus multisig, timelock present or not — and set an internal deposit ceiling for those that fall short.

[Sources]

This article is a technical commentary based on publicly available information and is not investment advice.