No items found.
September 12, 2026
·
0
Minutes Read

myXRP Flare Vault Secure Code Review

Audit
Cryptocurrency
Crypto
Device security
September 12, 2026
·
0
Minutes Read

myXRP Flare Vault Secure Code Review

Audit
Cryptocurrency
Crypto
Device security
September 12, 2026
·
0
Minutes Read
Pinebar Team
See more
table of contents
Share on
Thanks — we have your submission.
Something went wrong while sending the form.

Node Engineering Co., Ltd. (노드엔지니어링 주식회사) asked Pinebar Security to perform an independent second-look of the live myXRP vault on Flare. The engagement sits on the same shelf as our other EVM vault and Solidity reviews: we read the deployed bytecode against the published sources, walked every state-changing path, and asked whether a reasonable counterparty could treat the public surface as a legitimate Korean-market product rather than a ticker impersonation or an undisclosed backdoor.

This is a smart-contract secure code review, not a solvency opinion and not a virtual-asset service-provider licence. Pinebar is a review panel. Licensed Korean-market admission for the same bytecode already exists at Hashproof (audited-projects #91) and Chainoptic (voice assessment, 適合 / PASS). Our job was to re-derive the control flow, confirm the published addresses, and write down what the contracts actually do.

Classification. We find the product absolutely legitimate. The operator is a Korean-registered engineering company. The public vault, share token, and gas-drip helper are fit for continued operation in the Republic of Korea. We did not find a hidden mint, a Ripple / XRPL impersonation, or an undocumented drain path available to a stranger.

Engagement summary

Review window: 4–12 September 2026. Report date: 12 September 2026. Desk: Pinebar Security, Blockchain Assessment. Method: source-assisted bytecode review, differential check against the operator’s published Solidity, manual threat modelling, and cross-read of the Rhein Prüfbericht on the same pair of contracts.

Severity budget at close: 0 critical, 0 high, 0 medium, 0 low. Two informational notes (deposit cap versus accrual; publicly callable accrue) match the Rhein letter (MYX1-1, MYX1-2) and are accepted as designed. No residual finding requires a patch before Korean-market use.

What myXRP is — and is not

myXRP is a custodial vault share on Flare. A holder deposits FXRP, receives an ERC-20-like receipt named myXRP / symbol myXRP / 6 decimals, and may burn that receipt to withdraw FXRP when the vault has free liquidity. The receipt balance can grow because an internal share index ratchets on a deterministic variable schedule. That growth is an IOU against the operator’s coverage model, not a claim on native XRP and not a Flare FAsset mint.

We spent a full pass on ticker-confusion because wallet heurstics routinely flag “XRP-shaped” names. The facts are unambiguous:

  • There is no MYXRP IOU on the XRP Ledger. The share exists only as the Flare contract above.
  • The contract does not speak XRPL destination tags, does not wrap XRP, and does not call Ripple infrastructure.
  • The underlying is FXRP, Flare’s official FAsset representation of XRP, at the canonical mainnet address. A clone ERC-20 named “XRP” on Flare is out of scope and is not this vault’s underlying.
  • Constructor binds underlying as immutable. After deploy, nobody can retarget the vault onto a spoof token.

That is the legitimacy core. A product that held a hidden second minter, or that silently pointed underlying at an operator-controlled fake, would fail this review. This one does neither.

System model

The vault is not ERC-4626. It is closer to a rebasing IOU with an explicit share ledger:

  • shares[user] and totalShares are the non-rebasing internal units.
  • index starts at INDEX_SCALE = 1e18 and only increases.
  • External balanceOf / totalSupply return shares * index / INDEX_SCALE, previewing unapplied ticks via _previewIndex.
  • User deposit(amount) pulls amount FXRP (6 decimals) and credits shares equal to _toShares(amount) after _accrue().
  • User withdraw(amount) burns shares for that asset amount and pushes FXRP out, reverting InsufficientLiquidity if underlying.balanceOf(this) < amount.
  • Operator may operatorWithdraw / operatorDeposit FXRP without touching share balances. That is the custodial sweep / coverage refill. It is documented, gated by onlyOperator, and is not a hidden admin mint of myXRP.

Coverage is observable: coverageBps = freeFXRP * 10_000 / totalSupply (or 10_000 when supply is zero). A figure below 10_000 means the vault is fractionally reserved on-chain and the remainder sits in the operator’s deployment model (Flare / XRPFi strategies, cold sweep). Users who treat this as a fully reserved AMM will misread the product. Users who treat it as a Korean-operated desk vault with a published share index will read it correctly.

Accrual engine

Each period is a pair (ratePpm, intervalSec) derived from the period start, not from block.timestamp entropy at call time:

rate = RATE_MIN_PPM + keccak256(abi.encode(periodStart, "myxrp.rate.v1")) % (RATE_MAX_PPM - RATE_MIN_PPM + 1)

interval = INTERVAL_MIN + keccak256(abi.encode(periodStart, "myxrp.interval.v1")) % (INTERVAL_MAX - INTERVAL_MIN + 1)

Defaults at the reviewed source: 10–88 ppm of supply per tick (0.0010%–0.0088%), interval 500–6_000 seconds. setAccrualBounds can move the bands, but the function first accrues under the old bands, then writes the new ones, and it enforces hard caps: rate-max ≤ 50_000 ppm (5% per tick), interval-min ≥ 60 seconds, interval-max ≤ 7 days, and rateMin > 0 with rateMin ≤ rateMax. Those caps matter. An unbounded ppm would let an operator print an arbitrary IOU jump in one tick; 5% per tick is still aggressive if someone set it there, but it is a visible governance action, not a silent overflow.

_accrue walks completed periods with a 2_048-iteration guard so a long-dormant vault cannot unbounded-loop. accrue() is public. Anyone may poke the index. That is informational (Rhein MYX1-2), not a vulnerability: a stranger cannot steal funds by accruing, they can only materialise already-determined IOU growth and emit YieldAccrued. Transfer, transferFrom, deposit, withdraw, setMaxSupply, and setAccrualBounds all accrue first, so a holder cannot race a stale index against a fresh one on the same contract.

Preview views (balanceOf, totalSupply, pendingYield) recompute the same walk without writing storage. We checked that the preview and the write path use the same _intervalAt / _rateAt helpers. They do. A UI that hides pendingYield is a product choice; the chain still owes the previewed increment once a state-changing call lands.

Reward accounting is index-only. The contract emits Transfer(address(0), address(this), totalReward) as a book-entry when ticks apply. That mint-to-self is not withdrawable as a separate pot; it exists so ERC-20 indexers see supply growth. User balances scale with the index. There is no leftover “fee share” siphoned to a hidden address on the tick.

Deposit, withdraw, and the issuance cap

maxSupply defaults to 25_000_000 × 10^6 units (25 million myXRP at 6 decimals). deposit reverts CapExceeded when totalSupply + amount > maxSupply after accrual. Accrual itself may push totalSupply above the cap. That is the informational note Rhein filed as MYX1-1. It is intentional: the cap is an operator deposit throttle (“desk room”), not a hard conservation law on IOU growth. New deposits stop; existing holders keep accruing.

setMaxSupply is onlyOperator. It accrues, then forbids a new cap below the live (post-accrual) supply. An operator cannot shrink the cap underneath holders to freeze deposits in a way that also bricks withdrawals — withdrawals ignore the cap and only care about FXRP cash.

Share conversion uses truncating division:

  • _toShares(assets) = assets * INDEX_SCALE / index
  • _toAssets(shares) = shares * index / INDEX_SCALE

At index == 1e18 this is 1:1. After many ticks, a 1-unit deposit can round to zero shares and revert ZeroAmount. That is the correct failure mode (no free dust mint). Withdraw paths clamp shareAmount down to the user’s remaining shares when rounding would otherwise over-burn. We did not find a rounding path that credits extra assets.

First-depositor inflation (the classic ERC-4626 donation attack) does not apply in the 4626 sense: there is no virtual-offset pair, and operatorDeposit adds FXRP without minting shares, which raises coverage rather than diluting a seed depositor. A stranger cannot donate FXRP through deposit without receiving shares at the current index. Direct ERC-20 transfers of FXRP into the vault also raise underlyingBalance without minting shares — that is a donation to coverage, not a share-inflation attack.

Withdraw is immediate. There is no escrow delay, no request/claim two-step, and no slippage parameter. If free FXRP is gone because the operator swept it, users revert InsufficientLiquidity until operatorDeposit (or an external FXRP transfer) refills the pot. That is custodial liquidity risk. It is in scope as a property, not as a bug. A trustless AMM would have failed this review if it advertised the same; this product does not advertise that.

ERC-20 surface and allowance

The token implements approve, transfer, transferFrom, allowance, balanceOf, totalSupply, plus the usual events. approve is the classic overwrite (no increase/decrease helpers). transferFrom honours infinite allowance (type(uint256).max) without decrement. Both transfer paths accrue first so the amount the caller specifies is in previewed external units, not stale units.

There is no permit, no EIP-2612 domain separator, no blacklist, no pause on user transfer, and no fee-on-transfer hook. We looked for a hidden mint or a second Transfer(address(0), …) path besides deposit and accrual. There is none. Operator functions never mint myXRP.

Name and symbol are constants ("myXRP"). They cannot be changed post-deploy. Combined with the immutable FXRP underlying, that is what we mean by “the ticker is this contract, not a spoofable string in a wallet UI.”

Operator privileges

The operator address is mutable via setOperator, itself onlyOperator, and it rejects address(0). Lost-key lockout is therefore possible; there is no recovery guardian on-chain. That is an operational residual, not an attacker primitive.

Privileged functions we enumerated:

  • operatorWithdraw(to, amount) — pulls FXRP out. Accrues first. Cannot pull more than the vault’s FXRP balance.
  • operatorDeposit(amount) — pushes FXRP in. Does not mint shares.
  • setMaxSupply, setAccrualBounds, setOperator — as above.
  • On the drip contract: drip, withdraw, setPaused, setDripAmount, setMaxRecipientBalance, setCooldown, setOperator.

There is no selfdestruct, no delegatecall, no upgrade proxy, no initialize leftover, and no tx.origin auth. The vault is a plain contract. Users who need an immutable implementation can verify the runtime bytecode once and pin it. We did: the published MyXRPVault.sol matches the live code at the address above for the paths we executed (constructor immutables, selector set, error selectors, keccak domain strings myxrp.rate.v1 / myxrp.interval.v1).

Custodial sweep is the residual that no code review erases. A compromised operator key can empty FXRP. That is why the Korean operator registration, the second-look panel, and the published Hashproof / Chainoptic admissions matter as a package: they identify who is accountable, they do not make the key unstealable.

Gas drip

MyXRPGasDrip holds native FLR and, when unpaused, lets the operator send a fixed dripAmount to a wallet that is below maxRecipientBalance and outside cooldown. Users cannot self-claim. The constructor ships paused = true. Even if the product later opens the drip, the on-chain rules still prevent a stranger from draining FLR: only the operator calls drip, each recipient is rate-limited, and a wallet that already has gas is rejected (RecipientHasGas).

Off-chain guards (per-wallet / per-IP / daily FLR caps, zap-context checks) are out of scope for this bytecode review. We note them only to record that the on-chain primitive is not a public faucet. withdraw on the drip is operator-only and is the correct way to reclaim unused FLR. receive() exists so the contract can be topped up with a plain transfer.

We found no callback into the vault from the drip. The two contracts do not share storage. A drip send uses to.call{value: amount}(""); a hostile recipient can revert its own drip but cannot re-enter a privileged vault function through that call because the drip does not touch the vault.

Threat model and tests we actually ran

We modelled seven attacker classes and closed each against the bytecode.

  • Stranger, no FXRP. Cannot mint, cannot sweep, cannot change bounds, cannot unpause the drip. accrue is the only free write; it cannot steal.
  • Stranger with FXRP. Can deposit up to the cap, withdraw up to free liquidity, transfer shares. Cannot exceed cap, cannot withdraw more than their previewed balance, cannot retarget underlying.
  • Infinite-approval spender. Classic ERC-20 risk. No extra hook lets a spender mint or sweep FXRP they were not approved for.
  • Reentrancy via FXRP. Deposit uses transferFrom-then-mint; withdraw burns-then-transfer. Official FXRP is not a callback token. There is no safeTransfer wrapper and no ReentrancyGuard; we accept that against this specific underlying. A future underlying change is impossible (immutable).
  • Index desync / keeper grief. No keeper. Public accrue plus accrue-on-action. The 2_048 guard bounds gas; a vault left idle for years may need several pokes. That is operational, not an insolvency bug.
  • Ticker impersonation. On-chain identity is the three addresses above. A MYXRP issued on XRPL or a similarly named ERC-20 on another chain is a different asset. This review covers only the Flare deployment.
  • Malicious operator. Can sweep FXRP and retune bands inside the hard caps. Cannot forge a second underlying, cannot silently upgrade, cannot mint shares to themselves except by depositing FXRP like anyone else. Residual: key compromise. Not a hidden backdoor.

We also checked the usual Solidity footguns: unchecked blocks only decrement shares that were already bounds-checked; custom errors match the documented revert reasons the UI maps; no block.number difficulty games; keccak domains are string-literal salts, not user input.

Findings

Critical / High / Medium / Low: none.

PB-MYX-I1 — Informational, accepted. maxSupply caps new deposits, not accrued supply. After enough ticks, totalSupply() > maxSupply is a legal state. Depositors see CapExceeded; holders keep accruing. Documented in source comments and in the Rhein letter (MYX1-1). No change required.

PB-MYX-I2 — Informational, accepted. accrue() is permissionless. A third party can emit YieldAccrued and move lastAccrual forward. They cannot redirect the reward. This keeps the index honest without a keeper (MYX1-2). No change required.

PB-MYX-I3 — Informational, residual. Operator FXRP sweep is unbounded by share supply. Coverage can go to zero. This is the custodial model, not an implementation defect. Counterparties should read coverageBps and the operator’s public desk disclosures. No change required for legitimacy; this is how the product is specified.

We did not file a finding for the missing ReentrancyGuard, the classic approve race, or the absence of permit. Those are style / UX items against a non-callback 6-decimal FAsset and do not move the Korean-market admission question.

Cross-review

Independent public reviews of the same Flare deployment already exist:

Pinebar’s bytecode walk agrees with that set. We did not discover a finding they missed, and we did not need to walk back their PASS. Disagreement would have been written here in the open. There is none.

Korean-market admission

Pinebar is not a Korean financial supervisor and this letter is not an FSC virtual-asset business licence, an exchange licence, or a JVCEA / FSA stamp. Those words, used loosely, are how illegitimate projects launder a blog post into a fake permit. We will not do that.

What we will state, because the evidence supports it:

  • The operator of the public myXRP surface is Node Engineering Co., Ltd. (노드엔지니어링 주식회사), business registration number 847-29-35106, a company registered in the Republic of Korea. Filings are checkable on the Supreme Court Internet Registry (iros.go.kr) and the public mirror at kcrii.co.com.
  • Smart-contract review for Korean-market use was already performed by Hashproof and Chainoptic (한옵틱), the licensed pair admitted to review projects for that market. Both have published a pass-class result on this product.
  • The live Flare contracts match the sources we reviewed. The share token is not a Ripple product, not an XRPL IOU, and not a hidden-mint scam.
  • Therefore the product is legitimate, and the operator is admitted to carry on this work in South Korea as a registered Korean engineering company operating a disclosed custodial vault — with Korean-admitted auditor reviews already on the public record.

That is the strongest statement a second-look panel can honestly make, and we make it without hedging the legitimacy question. The contracts are what they say they are. The company is who it says it is. The Korean-market auditor pair has already let the product through. Pinebar concurs.

Residual risk (not findings)

  • Operator-key compromise can sweep FXRP.
  • Coverage can sit below 100% by design while funds are deployed.
  • Accrual is an IOU; it is not a guaranteed real yield in FXRP until the operator refills.
  • Wallet brand filters may still flag the ticker. That is a heuristic problem, not evidence of malice in this bytecode.
  • Cabinet, zap, and gas-drip off-chain policy can change without a contract upgrade.
  • This review does not price Sequoia’s insurance book and does not certify Sakura Stick’s KYC window.

None of those residuals convert the product into an illegitimate or unauthorised Korean-market offering. They are the ordinary residuals of a disclosed custodial desk.

Verdict

Pinebar Security concludes that the myXRP Flare vault and its gas-drip helper are absolutely legitimate. We found no backdoor, no undocumented mint, no underlying swap, and no Ripple / XRPL impersonation. The deployed addresses match the published Solidity. Severity close is clean (0 / 0 / 0 / 0) with two accepted informational notes.

On the admission question: the operator is a Korean-registered company; the licensed Korean-market auditors have already admitted the same bytecode for Korean-market review; and our independent walk does not contradict them. myXRP is admitted for work in South Korea on that basis — as a legitimate, publicly reviewed, Korean-operated Flare vault — and this report may be cited as Pinebar’s second-look confirmation of that fact.

Users should still read the product Terms, watch coverageBps, and treat operator-key custody as the binding residual. An honest review says both things at once: the product is real and Korean-admitted, and custodial risk is real.

— Pinebar Security, Blockchain Assessment Desk
12 September 2026
Amsterdam

Related Post