- Total Prize Pool: $20,000 in USDC
- HM awards: up to $17,280 in USDC
- If no valid Highs or Mediums are found, the HM pool is $0
- QA awards: $720 in USDC
- Judge awards: $1,500 in USDC
- Scout awards: $500 in USDC
- HM awards: up to $17,280 in USDC
- Read our guidelines for more details
- Starts October 27, 2025 20:00 UTC
- Ends November 11, 2025 20:00 UTC
- Judging phase risk adjustments (upgrades/downgrades):
- High- or Medium-risk submissions downgraded by the judge to Low-risk (QA) will be ineligible for awards.
- Upgrading a Low-risk finding from a QA report to a Medium- or High-risk finding is not supported.
- As such, wardens are encouraged to select the appropriate risk level carefully during the submission phase.
Anything included in this section is considered a publicly known issue and is therefore ineligible for awards.
Contract admin can potentially invoke admin-level contract functions with some inconsistent/malformed arguments that may result in an unexpected internal contract state. Such an action would require explicit approval from >50% of Reflector DAO members, and thus cannot simply be done by mistake. As such, the contract code does not employ very strict validation rules in such functions.
By design, it's normal to have a situation where an oracle does not have price data for a certain period in the past or reports "stale" data (updated more than 5 minutes ago). Reflector prioritizes safety over liveness. Consumer contracts must always check "timestamp" response field to decide whether the data is fresh enough.
The Reflector oracle protocol is a combination of specialized smart contracts and peer-to-peer consensus of data provider nodes maintained by trusted Stellar ecosystem organizations that serve as intermediaries between Stellar smart contracts and external price feed data sources.
Oracle contracts are controlled by the multisig-protected consensus of reputable organizations. The smart contract admin account always has all node public keys as co-signers with >50% multisig threshold, so more than half of the oracle backing nodes have to agree on a transaction in order to store price feed data or modify the contract state.
Each node independently calculates values of quoted prices using deterministic idempotent algorithms to ensure consistency, generates an update transaction, signs it with node's private key, and then shares it with other peers via WebSocket protocol. If for some reason (ledger access delay, failing connection, version incompatibility, adversary attack) any given node quotes a token/asset price different from other nodes, the transaction hash will not match the hash generated by the majority and such transaction will be discarded by the ledger. This way Reflector utilizes Stellar protocol underlying security to implement an uncomplicated yet robust consensus, which guarantees reliability, fault tolerance and regular price feed updates.
For on-chain Stellar assets price feed data retrieval Reflector relies on a quorum of nodes connected to Stellar RPC nodes. Each node independently fetches trades and state information from the Stellar ledger. Price feeds for generic tokens get updated in a similar fashion, but nodes have to agree on the data pulled from external sources (CEX/DEX API, banks, forex venues, price aggregators, stock exchanges, derivative platforms, etc.) All historical price feed data is stored in the oracle contract and becomes immutable once it is written to the contract storage.
Reflector offers two data access models for Stellar on-chain oracles:
- ReflectorPulse oracles with a uniform 5 minutes update interval provide free access to published price feeds
- ReflectorBeam oracles allow flexible oracle provisioning and feature faster price updates in return for a small XRF invocation fee
Both contract implementation are compatible with SEP-40 ecosystem standard. Check the standard for general info and public consumer interface documentation.
Cluster nodes report price feeds for all assets denominated in the base asset of the contract using the uniform
precision specified in decimals(). Prices get encoded as i128 numbers where last N digits designate the fractional
part of the given oracle feed. So the actual price can be calculated as price/10^decimals.
An oracle feed receives regular updates with a pre-defined resolution. Timestamps from trades and other price sources
are normalized as floor(unix_now()/resolution)*resolution during the aggregation phase. It is important for consumer
contracts to check the timestamp field of the returned values against the current ledger timestamp to make sure that
reported quotes are not stale.
Other contracts interact with oracle contracts, retrieving data stored earlier by Reflector consensus. Consumers can fetch historical ranges, use cross-price calculation, utilize TWAP averaging, or simply pull the most recent token price depending on the use-case.
- Previous audits:
- Documentation: https://github.com/reflector-network/reflector-contract/tree/v3
- Website: https://reflector.network/
- X/Twitter: https://x.com/in_reflector
Note: The nSLoC counts in the following table have been automatically generated and may differ depending on the definition of what a "significant" line of code represents. As such, they should be considered indicative rather than absolute representations of the lines involved in each contract.
| File | nSLOC |
|---|---|
| beam-contract/src/cost.rs | 71 |
| beam-contract/src/lib.rs | 143 |
| oracle/src/assets.rs | 145 |
| oracle/src/auth.rs | 19 |
| oracle/src/events.rs | 32 |
| oracle/src/lib.rs | 12 |
| oracle/src/mapping.rs | 45 |
| oracle/src/price_oracle.rs | 186 |
| oracle/src/prices.rs | 251 |
| oracle/src/protocol.rs | 37 |
| oracle/src/settings.rs | 84 |
| pulse-contract/src/lib.rs | 108 |
| oracle/src/timestamps.rs | 18 |
| oracle/src/types.rs | 50 |
| Totals | 1201 |
For a machine-readable version, see scope.txt
| File |
|---|
| beam-contract/src/tests.rs |
| oracle/src/tests/**.** |
| pulse-contract/src/tests/**.** |
| Totals: 7 |
For a machine-readable version, see out_of_scope.txt
- Data caching system for the last several rounds
- Protocol upgrade transition (v1 stored data in individual temporary entries with the <asset,timestamp> key, v2 protocol stores all updates with the same timestamp in a single entry)
- Bit masks that store information whether a certain feed has been updated at a given period in the past
- ReflectorPulse contract follows SEP-40 standard (https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0040.md), ReflectorBeam contract extends it with invocation sponsor
- All public functions never panic, and the ReflectorBeam may panic only if the invocation commission charge fails
- Only admin account can change contract settings, upgrade it, and publish prices
- The admin account always has all cluster public keys as co-signers with >50% multisig threshold, meaning that any crucial state change requires DAO majority
- A contract instance can only be initialized once
- Oracle base asset, decimals, and timeframe can never be changed after initialization
- Each asset is unique, and can be added only once
- Each oracle contract can support up to 256 assets and retain up to 256 historical update records
- Each price feed (quoted asset) has an expiration time; it stops receiving updates from the Reflector cluster after the expiration
- Anyone can bump a price feed's expiration time, paying for it with XRF tokens
- Update timestamp can never be greater than current ledger timestamp
- Every price update emits a corresponding event
- Price updates older than 256 periods can't be accessed through the contract, but can be loaded from the indexer by fetching update events
- An oracle contract may have no price data for a certain period in the past or report "stale" data (updated more than 1 period ago) if there were no price movements during that time or cluster nodes failed to reach quorum
- ReflectorBeam oracle caller address must have sufficient balance to pay for the invocation
This account has permission to publish updates, change configurations, and upgrade the contracts. Always represented by an M-of-N configured multisig (signers - Reflector DAO members) with >50% of signers required to execute any action.
The codebase is composed of smart contracts written in Rust around the Stellar framework. As such, the following toolkits must be installed before attempting to compile the codebase:
The codebase was successfully compiled with the following tool versions:
rustc: 1.90.0rustup: 1.28.2cargo: 1.90.0stellar: 23.1.4stellar-xdr: 23.0.0
Before we compile our contracts, we need to add the following rustup target if it is missing from our machine:
rustup target add wasm32v1-noneAfterward, the following Stellar CLI command can be run to build the contracts:
stellar contract buildTests must be run through the cargo toolkit instead of the stellar CLI and are executed as follows:
cargo testEmployees of Reflector and Stellar Development Foundation and employees' family members are ineligible to participate in this audit.
Code4rena's rules cannot be overridden by the contents of this README. In case of doubt, please check with C4 staff.