Partial Payments on XRPL: The Flag Was Never the Bug

SamEagle
Features

The data shows a payment instruction with a 10,000 XRP amount, and a settlement record that moved 9,981.32 XRP. The receiver's interface displays 10,000 XRP credited. The missing 18.68 XRP becomes a support ticket, then a post about a supposed XRP Ledger exploit. I have read similar postmortems for almost a decade. The ledger is not lying. The flag is not a bug. The integration is.

XRP Ledger has supported partial payments as a native transaction type for years. The design is simple when you stop reading the amount field as a promise and start reading it as a ceiling. The amount field is a cap, not a delivery contract. A sender can execute a payment that delivers less than the amount specified, provided the transaction carries the Partial Payment flag. That flag matters in pathfinding and cross-currency settlement. If a route cannot deliver the full target due to thin liquidity or exchange-rate movement, the transaction can still settle part of the value instead of failing outright. This is not a vulnerability. It is a mechanism to reduce failed-swap, failed-payment cascades.

Partial payments are tightly coupled to pathfinding. When a payment crosses currencies, the network searches order books for a route. The sender sets SendMax as a cap on total cost; the target amount is the desired outcome. If the route cannot fully satisfy the target, the Partial Payment flag allows the network to settle what is actually available. Without the flag, the transaction fails. With it, the transaction succeeds but delivers less than the stated target. That distinction is the entire design.

Partial Payments on XRPL: The Flag Was Never the Bug

But here is the mechanical problem. The XRPL transaction metadata includes a field called delivered_amount. When Partial Payment is set, the amount field shows the target request; delivered_amount shows what actually arrived. The entire safety question is whether the downstream receiver uses delivered_amount as the source of truth. Many do not.

That is the real finding. The "not a bug" conclusion is correct, but only at layer one. At the integration layer, partial payments are a foot-gun. A wallet, exchange, or payment processor that reads the amount field and credits the recipient the target value creates a transaction that says one thing and settles another. The gap is not theft by the protocol. It is an accounting error by the integrator. And in a system where payments often represent real money, an accounting error on the receiving side is functionally an exploit.

The attack pattern is simple. An attacker sends a partial payment with a legitimate-looking invoice, relies on the receiver to read the amount field, and walks away with the difference credited as full value. This is not a theoretical edge case. It is a normal payment with one flag set, and the only reason it becomes an attack is because the receiving system never checks delivered_amount. The ledger does not hide anything. The metadata returns exactly what arrived. The integration chooses to ignore it.

I ran a weekend of testnet simulations before writing this. I sent payments with partial paths, with fee spikes, with competing offers in the order book. The ledger behaved consistently: the target amount was never guaranteed; the delivered amount was always explicit. The failure mode was not in the ledger. It was in the client-side accounting that assumed full delivery.

Partial Payments on XRPL: The Flag Was Never the Bug

From my audit experience, the first thing I look for in any XRP payment integration is the callback logic. Does it read amount or delivered_amount? I have seen production systems fail this test. The fix is not to disable partial payments; the fix is to make delivered_amount the only number that drives a credit.

The contrarian angle: the community's defensive reflex makes this worse. The phrase "not a bug" is technically accurate, yet strategically dangerous. It tells developers they can stop worrying. The opposite is true. Every payment integrator should be forced to simulate a partial payment on testnet before mainnet deployment. If their callback reads delivered_amount, the system is safe. If it reads amount, the system is not. No consensus layer can fix a reconciliation layer that reads the wrong field. Structure defines value; chaos destroys it. The structure is in the metadata. The chaos begins when an integrator decides to ignore it.

This is where I depart from the usual XRP defense posts. The feature should not be removed, and it should not be reclassified as a flaw. It should be taught as a constraint. A partial payment is not a guarantee of full settlement. Treating it as such is the actual bug. The developers who built the protocol understood this. The developers integrating it too often do not.

Some will argue that the blame belongs to the integrators, not the protocol. I agree. But assigning blame is not a security posture. The ecosystem improves only when the receiving side treats partial settlement as a normal state and builds UI and accounting around it. The next wave of XRP adoption will not be driven by speculative volume. It will be driven by whether payment processors can settle real invoices without reconciliation surprises.

Partial Payments on XRPL: The Flag Was Never the Bug

The solution is unglamorous. Every wallet that accepts XRP should display both the quoted amount and delivered_amount when they differ. Every exchange deposit page should include the delivered amount in the transaction confirmation. Every payment API should include a warning when a transaction settles below its target. These are not protocol changes. They are integration hygiene. They are also the only durable defense against the next "XRP exploit" headline.

Takeaway: We do not predict the future; we hedge against it. The hedge for partial payments is simple: read delivered_amount, stress-test every approval flow, and never treat amount as a delivery contract. If more teams do that, the feature stops being a risk and becomes what it was designed to be: a settlement fallback for routing problems. If they do not, the flag will keep producing the same loss events, and each one will be called a bug by people who never checked the metadata. The flag is safe. The accounting layer is not. That is where the next audit should look. Start there.