← Case Studies

myXRP Formal Review

Formal-methods case study

Formal review of the sealed Flare bridge. Properties held. This is a code review. The note stays with the published bridge.

Protocol Defence reviewed the live myXRP bridge on Flare as a closed system: one immutable FXRP pipe, one 1:1 receipt ledger, one immutable ceiling, and a public desk that must name the same contract. This is a formal-methods engagement — we wrote the properties first, then asked whether the deployed Solidity and the published site satisfy them.

This is a code review. The note stays with the published bridge. Every property we stated holds on the live bytecode, or is an accepted observation that does not move value. There is no hidden mint, no retargetable underlying, no admin key, and no stranger-reachable drain.

Engagement

Desk: Protocol Defence. Method: source-assisted bytecode review, predicate discharge on every state-changing path, and a walk of the public surfaces a counterparty actually clicks. Report closed 1 October 2026 at 14:25 UTC, after this address was deployed on 30 September 2026.

  • Network. Flare C-Chain, chainId = 14.
  • Compiler. Solidity ^0.8.20, overflow checks on, custom errors on the hot paths.
  • Contract. MyXRPBridge at 0x14b9DD90edaAff6527c1b168550B3FAA15b9F6AF.
  • Underlying. Official Flare FXRP 0xAd552A648C74D49E10027AB8a618A3ad4901c5bE, 6 decimals, bound immutable.
  • Ceiling. maxAssets immutable at 10,000,000.000000 FXRP (10_000_000_000_000 raw). Read live with maxAssets(). It is not a figure from an earlier deployment.
  • Public desk. myxrp.fi — deposit panel, add-to-wallet, /explorer-verify/, /token.json.

Severity at close: 0 critical, 0 high, 0 medium, 0 low. Two formal notes (KF-N1, KF-N2) record the constant name and the withdraw order. Neither requires a patch.

What we specified

The bridge is not a rebasing vault and not ERC-4626. We treated it as a small machine with one balance map and two immutables:

  • underlying — the FXRP pipe, fixed at deploy.
  • maxAssets — the mint ceiling, fixed at deploy.
  • External units: balanceOf(u) is the stored balance. There is no index and no preview of unapplied yield.

Properties we discharged against the live selectors:

  1. P-Bind. underlying is immutable. After deploy, nobody can retarget the bridge onto a spoof ERC-20.
  2. P-Cap. maxAssets is immutable. deposit reverts CapExceeded when totalSupply + amount would pass it. There is no setter.
  3. P-OneToOne. deposit(a) pulls a FXRP and mints a myXRP. withdraw(a) burns a and pushes a FXRP. A zero amount reverts ZeroAmount.
  4. P-Cash. If underlying.balanceOf(this) < a, withdraw reverts InsufficientLiquidity before the burn is committed.
  5. P-NoAdmin. The runtime has no owner, no admin sweep, no accrual, no supply setter, and no pause.
  6. P-Reenter. deposit and withdraw are nonReentrant. A nested call reverts Reentered.
  7. P-Room. room() equals maxAssets - totalSupply.
  8. P-NoProxy. No upgrade proxy, no delegatecall, no selfdestruct, no leftover initialize, no tx.origin auth, no fee-on-transfer.
  9. P-Label. name() and symbol() are the constant myXRP. The receipt is a Flare bridge claim on FXRP locked in this contract. It is not native XRP and not an XRPL IOU.
  10. P-StuckGift. A raw FXRP transfer into the contract does not mint. Surplus above totalSupply cannot be extracted, because the only outbound path pays 1:1 against a burn.

All ten hold on the published source and the runtime at this address. A reader can open the Flare explorer contract tab and repeat the check. The bytecode at this address did not exist before 30 September 2026, 15:00 UTC.

The cash path

Deposit order: reentrancy lock, amount check, ceiling check, transferFrom, mint, unlock. Withdraw order: reentrancy lock, amount check, balance check, FXRP-cash check, burn, transfer, unlock. A failed FXRP transfer reverts TransferFailed and rolls the burn back. We did not find a path that burns the receipt and leaves the FXRP behind.

The predicate that stops deposits is P-Cap against maxAssets, 10,000,000.000000 FXRP. We did not discharge a dollar constant as if it were an opcode.

Public desk

We walked myxrp.fi as a stranger: connect, read the deposit panel, add the receipt to a wallet, open explorer-verify, fetch https://myxrp.fi/token.json. The UI binds this bridge. Official name on the record is myXRP. The panel’s room line is room() on this contract.

Out of scope for the predicates above: cabinet snapshot credits, off-chain key custody of holders, FAssets agent economics. Those do not change the 1:1 math.

Notes

KF-N1 — constants for the name. name() and symbol() are the literal myXRP. A search for XRP on the XRP Ledger does not return this receipt. Informational. Accepted. No patch: the strings are not setters.

KF-N2 — cash is checked before the burn. withdraw reads the FXRP balance, then burns, then pushes. A failed push reverts TransferFailed and the burn does not stay. Informational. Accepted. The order is the property; we are not asking for a second path.

Results

Properties P-Bind through P-StuckGift hold. No critical, high, medium, or low finding remains open. Protocol Defence classifies the live bridge and the public desk as a reading of the published bytecode. Counterparties who need a sealed Flare FXRP receipt can treat the deployment at the address above and myxrp.fi as the deployment that was read.

Ask for a quote