Contract audit · 컨트랙트 감사
gpt-5.6-sol-medium
3 October 2026 · MyXRPBridge on Flare
MyXRPBridge is a compact, non-custodial Solidity bridge on Flare mainnet. It accepts FXRP and mints an equal quantity of myXRP. A user can burn myXRP to recover the same nominal quantity of FXRP.
The public site also describes a custodial desk and a custodial cabinet. Those are a different object from this contract. This letter reviews only MyXRPBridge. On the contract, a user locks FXRP and a burn returns the same FXRP in that transaction. There is no operator key on the contract. The cabinet credit is a desk ledger entry. It is not a function of this bytecode. The heading “Why myXRP is custodial — on purpose” on the security page names the desk. It does not rename this bridge.
The reviewed source contains:
At reference block 71,254,525, the contract held 25.000000 FXRP against a total supply of 25.000000 myXRP, giving exact nominal 1:1 collateralization at that block.
No critical or high-severity issue was identified. The principal residual risk is dependence on the separately upgradeable FXRP token.
| Item | Value |
|---|---|
| Contract | MyXRPBridge |
| Source | MyXRPBridge.sol |
| Network | Flare mainnet |
| Chain ID | 14 |
| Address | 0x14b9DD90edaAff6527c1b168550B3FAA15b9F6AF |
| Underlying | FXRP |
| FXRP address | 0xAd552A648C74D49E10027AB8a618A3ad4901c5bE |
| Solidity version | ^0.8.20 |
The review covered the supplied Solidity source and read-only calls to the deployed addresses. A reproducible source-to-runtime-bytecode build comparison was not performed.
Read-only calls returned the following values:
| Property | Result |
|---|---|
name() | myXRP |
symbol() | myXRP |
decimals() | 6 |
underlying() | 0xAd552A648C74D49E10027AB8a618A3ad4901c5bE |
maxAssets() | 10,000,000.000000 FXRP |
totalSupply() | 25.000000 myXRP |
room() | 9,999,975.000000 FXRP |
OPENS_AT() | 1790780400 |
| Opening time | 30 September 2026, 15:00:00 UTC |
| FXRP held by bridge | 25.000000 FXRP |
The opening timestamp had elapsed by the audit date. Deposits were therefore enabled, subject to the fixed ceiling and successful FXRP transfer.
The bridge address contains deployed runtime code. Its ERC-1967 implementation slot was zero at reference block 71,254,562, consistent with a direct, non-proxy deployment.
The intended accounting invariant is:
FXRP held by bridge >= totalSupply of myXRP
Each unit uses six decimals. Depositing one FXRP unit mints one myXRP unit; withdrawing one myXRP unit returns one FXRP unit.
The contract has no method that:
maxAssetsOPENS_ATThe ceiling applies to current totalSupply, not lifetime deposits. A withdrawal reduces supply and consequently restores deposit room.
constructor(address fxrp, uint256 maxAssets_)Sets the immutable FXRP token and deposit ceiling.
It rejects:
Both values become immutable after deployment.
name(), symbol(), and decimals()Constant metadata getters returning:
myXRPmyXRP6OPENS_AT()Returns the fixed opening timestamp. There is no method to modify it.
underlying()Returns the immutable FXRP contract address.
maxAssets()Returns the immutable maximum outstanding myXRP supply.
totalSupply()Returns the current myXRP supply. Supply increases only after a successful deposit and decreases during withdrawal.
balanceOf(address account)Returns an account’s myXRP balance.
allowance(address owner, address spender)Returns the quantity a spender may transfer from an owner.
room()Returns zero when supply has reached the ceiling. Otherwise, it returns:
maxAssets - totalSupply
The subtraction is guarded by the preceding comparison.
deposit(uint256 amount)The function:
1. Enters the reentrancy guard. 2. Requires the opening timestamp to have elapsed. 3. Rejects zero. 4. Rejects deposits that would exceed maxAssets. 5. Calls FXRP transferFrom to move the requested quantity into the bridge. 6. Increases totalSupply. 7. Credits the caller with an equal quantity of myXRP. 8. Emits Transfer from the zero address and Deposit.
State accounting occurs only after transferFrom returns successfully. A revert rolls back the complete transaction.
The caller must approve the bridge at the FXRP contract before depositing.
withdraw(uint256 amount)The function:
1. Enters the reentrancy guard. 2. Rejects zero. 3. Verifies the caller’s myXRP balance. 4. Verifies that the bridge reports enough FXRP liquidity. 5. Debits the caller. 6. Reduces totalSupply. 7. Emits a burn-style Transfer. 8. Transfers the same quantity of FXRP to the caller. 9. Emits Withdraw.
If the FXRP transfer fails or reverts, all preceding state changes and events are reverted atomically.
Only the account owning the myXRP being burned can invoke this withdrawal path.
approve(address spender, uint256 amount)Sets the caller’s allowance for a spender and emits Approval.
This implements conventional ERC-20 allowance replacement semantics.
transfer(address to, uint256 amount)Moves myXRP from the caller to another nonzero address.
It rejects:
Zero-value transfers are permitted.
transferFrom(address from, address to, uint256 amount)Allows an approved spender to transfer myXRP.
Finite allowances are reduced by the transferred amount. An allowance equal to uint256.max is treated as unlimited and is not reduced.
The function does not itself withdraw FXRP. A spender can transfer approved myXRP into an address it controls and subsequently redeem it from that address.
_transfer(address from, address to, uint256 amount)Internal balance-transfer logic. It checks the recipient and source balance, performs arithmetic after establishing the source balance bound, and emits Transfer.
Self-transfers preserve the account’s final balance.
nonReentrantProtects deposit and withdraw from nested execution. This is relevant because both functions call the external FXRP contract.
The reviewed bridge source provides only one outbound FXRP path: withdraw.
A caller must hold and burn an equal amount of myXRP. There is no owner, operator, rescue address, administrator, or arbitrary FXRP transfer function in MyXRPBridge.
A myXRP owner can:
An approved spender can move myXRP through transferFrom. Once the spender controls those tokens, it can redeem them normally.
MyXRPBridge cannot prevent actions implemented by the FXRP token itself. The live FXRP address resolves through a nonzero ERC-1967 implementation slot, meaning its behavior can be changed through FXRP’s own governance and authorization system.
The bridge’s solvency and availability depend on FXRP continuing to behave as expected.
Relevant risks include:
MyXRPBridge checks the Boolean return value of transfer and transferFrom, but deposit does not measure the bridge’s FXRP balance before and after the transfer. If FXRP were changed to deduct a transfer fee while still returning true, the bridge could mint more myXRP than the FXRP actually received.
At the reference block, this condition was not present: the bridge’s 25.000000 FXRP balance exactly matched its 25.000000 myXRP supply.
| Severity | Count |
|---|---|
| Critical | 0 |
| High | 0 |
| Medium | 1 |
| Low | 2 |
| Informational | 2 |
| ID | Severity | Finding | Status |
|---|---|---|---|
| M-01 | Medium | Solvency and redemption depend on the upgradeable FXRP token | Open external dependency |
| L-01 | Low | Direct FXRP transfers do not mint myXRP and cannot be recovered through a rescue function | By design |
| L-02 | Low | myXRP transferred directly to the bridge address becomes inaccessible | By design |
| I-01 | Informational | Allowance replacement has the conventional ERC-20 transaction-ordering risk | Acknowledged standard behavior |
| I-02 | Informational | Exact source-to-runtime-bytecode equivalence was not reproduced a second time | Verification limitation |
MyXRPBridge’s underlying address is immutable, but the FXRP address itself resolves through an ERC-1967 implementation slot.
Consequently, MyXRPBridge cannot guarantee that future FXRP behavior will retain the transfer properties assumed by its accounting. Pausing, restrictions, balance-changing privileged operations, or fee-charging transfers could impair deposits, withdrawals, or collateralization.
The most direct accounting concern is that deposit mints the requested amount after a successful Boolean return without confirming the amount actually received.
Recommendation: Continuously monitor the FXRP implementation address, upgrade events, bridge FXRP balance, and the relationship between FXRP balance and myXRP supply. Any future bridge version should compare pre-transfer and post-transfer balances and require the received amount to equal the requested amount.
Sending FXRP directly to the bridge address does not call deposit, does not mint myXRP, and does not credit the sender.
Because there is no rescue function, directly transferred surplus FXRP cannot be specifically recovered by its sender.
Recommendation: Users and integrations should call deposit after approving FXRP rather than transferring FXRP directly.
The bridge can hold myXRP like any address, but it has no function that invokes its own withdraw method or transfers its myXRP balance elsewhere.
myXRP sent directly to the bridge address therefore becomes inaccessible, while the corresponding FXRP remains locked.
Recommendation: Interfaces and integrations should reject or warn against selecting the bridge itself as a myXRP transfer recipient.
approve replaces an existing allowance directly. As with conventional ERC-20 approval, a spender may use an existing allowance before a pending replacement transaction is confirmed and then use the replacement allowance afterward.
Recommendation: When changing a nonzero allowance, first set it to zero and wait for confirmation before setting the new amount, or use uint256.max only for deliberately trusted integrations.
The live getter results, deployment shape, immutable values, and runtime behavior examined through read-only calls were consistent with the supplied source. However, an exact compiler-settings reconstruction and byte-for-byte comparison was outside this review.
Recommendation: Publish compiler version, optimizer configuration, constructor arguments, and verified source metadata sufficient for reproducible compilation.
MyXRPBridge has a narrow design and a small attack surface. The supplied source provides fixed 1:1 minting and redemption, a hard supply ceiling, reentrancy protection, and no bridge-level privileged fund-moving authority.
At the reference block, the contract was nominally fully collateralized, with 25.000000 FXRP held against 25.000000 myXRP outstanding. No critical or high-severity issue was identified.
The security conclusion remains conditional on the FXRP token preserving compatible transfer behavior and the bridge’s FXRP balance remaining at least equal to myXRP supply.
This is a technical opinion on one contract, not a guarantee and not a statement about any company.