The Evolution of DeFi Oracles: From TWAP to RWAP and RVOL
An in-depth exploration of on-chain oracle mechanisms in DeFi, examining the progression from time-weighted to reserve-weighted price oracles and their application in volatility calculations
Note: This article discussed complex financial and DeFi concepts, a simple explainer companion piece is available here
Oracle Types in DeFi
DeFi protocols rely on oracles to obtain reliable price data for critical operations like lending, trading, and settlement. In this article, we’ll explore three key types of on-chain oracles that have evolved in the DeFi ecosystem:
- TWAP (Time-Weighted Average Price): The original oracle mechanism found in Uniswap V2 and incrementally adjusted innovations within Solidly & Uni V3
- RWAP (Reserve-Weighted Average Price): A more capital-efficient price calculation method, introduced in Solidly, and Flying Tulip
- RVOL (Realized Volatility): On-chain volatility calculations that enable options pricing and risk assessment
These oracle mechanisms have particular relevance today with the upcoming launch of Flying Tulip on Sonic, a new trading protocol founded by Andre Cronje that will incorporate all three oracle types plus an Implied Volatility (IV) oracle. This new protocol will feature “TWAP, RWAP, RVOL and IV oracles”
The oracle mechanisms serve as fundamental building blocks that will enable Flying Tulip to offer a suite of advanced DeFi features, including:
- Adaptive curve AMM that adjusts based on market conditions
- Dynamic & volatility-adjusted LTV money markets
- 1000x leverage capabilities
- Perpetuals & options on all liquidity pairs
This article traces the evolution of these oracle designs, examining how they work, what problems they solve, and why integrated oracles represent the future of AMM design.
Traditional Finance Foundations
It’s worth understanding the fundamentally different environment of traditional finance oracles. In TradFi, price data flows through trusted channels — Bloomberg, Reuters, exchange data feeds — with established regulatory frameworks ensuring integrity. These centralized systems operate with virtually unlimited computational resources, no storage constraints, and floating-point precision.
DeFi couldn’t simply import these TradFi approaches because blockchain environments present unique constraints: limited storage where every byte costs gas, absence of floating-point arithmetic, requirement for trustless operation, and vulnerability to new attack vectors like flash loans. These constraints necessitated innovative oracle designs that could deliver similar functionality while operating within an entirely different paradigm.
This foundational difference explains why DeFi oracles evolved through multiple generations, each addressing limitations of previous approaches while working within blockchain’s unique constraints.
Oracle Integration in AMMs: Why Direct Embedding Matters
Before we start exploring specific oracle implementations, it’s important to understand why embedding oracle functionality directly within Automated Market Makers (AMMs) is a preferred approach.
In the early days of DeFi, oracles and AMMs weren’t always embedded components. AMMs, like Uniswap V2, would maintain price data in its core contract, while external oracle contracts would fetch, store, and process this data for other protocols to use. This separation created several challenges:
- Data Synchronization: External oracles could fall out of sync with the actual AMM state
- Dependency Chains: Protocols relying on oracles had to trust both the AMM and the oracle
- Update Reliability: External oracles required separate incentive mechanisms to ensure timely updates
- Fragmented Security Models: The oracle could have different security properties than the AMM itself
By embedding oracle functionality directly within the AMM:
- Atomic Updates: Price data is updated in the same transaction as trades, ensuring perfect synchronization
- Simplified Dependency: Other protocols only need to interact with a single contract
- Guaranteed Updates: Price updates happen as a natural consequence of trading activity
- Unified Security Model: The security of price data is tied directly to the security of the AMM
This evolution toward embedded oracles represented a shift in DeFi design philosophy — moving from modular, composable pieces to more integrated, self-contained systems that provide better reliability guarantees at the cost of some flexibility.
Time Weighted Average Prices (TWAP)
TWAP in Uniswap V2: The Foundation of On-Chain Oracles
Time-Weighted Average Price (TWAP) oracles were first popularized in DeFi through Uniswap V2’s implementation. But what exactly is a TWAP oracle, and how does it work?
At its core, a TWAP oracle provides the average price of an asset over a specified time period, weighted by the duration each price persisted. This time-weighting mechanism is designed to make price manipulation more difficult, as an attacker would need to sustain a manipulated price for a significant portion of the measurement period.
TWAP: Mathematical Foundations
TWAP over a period [t₁, t₂] can be defined as:
> TWAP = (1/(t₂-t₁)) ∫(t₁ to t₂) P(t) dt
Where:
- P(t) is the price at time t
- t₁ is the start time
- t₂ is the end time
For implementation on blockchain with discrete time steps:
> TWAP = (∑(i=1 to n) P(tᵢ) × (tᵢ — tᵢ₋₁)) / (t₂ — t₁)
In Uniswap V2’s implementation using cumulative prices:
> CumulativePrice(t) = CumulativePrice(t-1) + P(t) × (t — t_lastupdate)
> TWAP from t₁ to t₂ = (CumulativePrice(t₂) — CumulativePrice(t₁)) / (t₂ — t₁)
Worked Example: ETH/USDC TWAP Calculation
Calculation for a 1-hour TWAP for ETH/USDC with price observations:
TWAP Calculation (direct method):
> TWAP = Sum(Price × Duration) / Total Duration
> TWAP = ($500 + $512.5 + $594 + $406) / 1.0
> TWAP = $2012.5
Cumulative Price Approach (Uniswap V2 method):
- At t=0: CumulativePrice = 0
- At t=0.25: CumulativePrice = 0 + $2000 × 0.25 = $500
- At t=0.5: CumulativePrice = $500 + $2050 × 0.25 = $1012.5
- At t=0.8: CumulativePrice = $1012.5 + $1980 × 0.3 = $1606.5
- At t=1.0: CumulativePrice = $1606.5 + $2030 × 0.2 = $2012.5
> TWAP = (CumulativePrice at t=1 — CumulativePrice at t=0) / (1–0)
> TWAP = ($2012.5 — $0) / 1.0 = $2012.5
This example shows how both the direct calculation and the cumulative price approach yield the same TWAP value, while the cumulative approach only requires storing the latest cumulative price rather than all historical prices.
Uniswap V2’s Implementation
Uniswap V2 implements TWAP through a clever cumulative price tracking mechanism:
// In the _update function
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
Rather than storing historical prices directly, Uniswap V2 maintains cumulative price values that continuously sum up:
price × timeElapsed
This approach has several notable advantages:
- Gas Efficiency: Only requires storing two cumulative values, regardless of the time window used
- Simplicity: Straightforward mathematical principle
- Minimal Storage: Doesn’t need to maintain historical price points
- Natural Updates: Prices update during normal trading activity
However, this implementation also has significant limitations:
- External Implementation Required: Protocols wanting to use the TWAP must record observations and calculate TWAPs themselves
- No Built-in Historical Data: The pair contract only stores the latest cumulative price
- Development Burden: Increases complexity for protocols integrating with the oracle
- Time Window Limitation: External contracts must record observations at the exact start and end of their desired time window
Keep3rV1Oracle: Extending Uniswap V2’s Oracle Capabilities
To address these limitations, Andre Cronje developed the Keep3rV1Oracle contract. This external oracle contract built on top of Uniswap V2 pairs to provide more sophisticated oracle functionality:
struct Observation {
uint timestamp;
uint price0Cumulative;
uint price1Cumulative;
}
mapping(address => Observation[]) public observations;
function _update(address pair) internal returns (bool) {
Observation memory _point = lastObservation(pair);
uint timeElapsed = block.timestamp - _point.timestamp;
if (timeElapsed > periodSize) {
(uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
observations[pair].push(Observation(block.timestamp, price0Cumulative, price1Cumulative));
return true;
}
return false;
}
The Keep3rV1Oracle enhanced Uniswap V2’s capabilities by:
- Storing Historical Data: Maintaining arrays of observations for each pair
- Regular Updates: Using the Keep3r Network to incentivize keepers to update these observations
- Built-in Queries: Providing functions like
current()
,quote()
, andsample()
to easily access TWAP data - Volatility Calculations: Even offering realized volatility calculations through the
stddev()
function (and as we’ll see later this aspect was further enhanced through the Keep3rV1Volatility contract)
However, this approach still relied on external keeper systems to maintain the oracle, creating a dependency that could potentially affect reliability during network congestion or if keeper incentives weren’t properly aligned.
TWAP in Solidly: Improving Usability
Solidly was an AMM implemented originally on Fantom Network and thereafter forked multiple times across various different chains. In earlier, pre-launch phases, the original Solidly included a TWAP implementation in the BaseV1Core contract. However, prior to launch a Reserve Weighted Average Price (RWAP) replaced this functionality within the BaseV1Factory contract (replacing the BaseV1Core). We’ll cover RWAP further below, but for now, we’ll focus on Solidly’s earliest (pre-release) code as it helps trace the lineage from Keep3rV1Oracle.
BaseV1 takes TWAP to the next level by addressing many of Uniswap V2’s limitations. It maintains the core time-weighting mechanism but makes significant improvements to usability and integration simplicity.
Key Innovations in BaseV1
Interestingly, the architecture of Solidly’s oracle system bears a striking resemblance to Keep3rV1Oracle, which is no coincidence — both were created by Andre Cronje (if you didn’t know why he’s dubbed the godfather of DeFi then wonder no more). However, Solidly makes a critical improvement: instead of relying on an external oracle contract, it integrates the observation storage directly into the AMM pair contract.
The most significant innovation in Soldily is the built-in storage of historical observations:
struct Observation {
uint timestamp;
uint price0Cumulative;
uint price1Cumulative;
}
// Array of historical observations
Observation[] public observations;
// Capture oracle reading every 30 minutes
uint public constant periodSize = 1800;
This structure allows the protocol to:
- Store Historical Data: Maintain an array of price observations over time
- Automatic Recording: Create new observations every 30 minutes
- Built-in Queries: Provide ready-to-use functions to calculate TWAPs
The update mechanism works by checking if enough time has elapsed since the last observation:
// In the _update function
timeElapsed = blockTimestamp - _point.timestamp;
if (timeElapsed > periodSize) {
observations.push(Observation(blockTimestamp, price0CumulativeLast, price1CumulativeLast));
}
BaseV1 also provides several built-in query functions that make accessing TWAP data much easier:
current()
: Gets the current TWAP since the most recent observationquote()
: Gets TWAP with configurable granularitysample()
: Returns multiple price points for specific time windows
In theory, it’d also be simple enough to add:
hourly()
,daily()
,weekly()
: Convenience wrappers aroundsample()
This approach drastically simplifies integration for protocols that need oracle data. Instead of implementing their own observation storage and TWAP calculation, they can simply call one of the built-in functions.
The Storage Problem
While Solidly’s approach greatly improves usability, it introduces a significant drawback: unbounded storage growth.
Since new observations are added every 30 minutes and there’s no mechanism to remove old ones, the observations
array grows indefinitely. This leads to several concerns:
- Gas Cost Increases: Functions that iterate through the array become more expensive over time
- Storage Bloat: Contract storage continuously expands
- Long-term Sustainability: Eventually, gas costs could become prohibitively expensive
By my own estimate, this could become problematic in the following timeframes:
- Each observation requires 68 bytes of storage
- 48 observations are added daily (24 hours / 0.5 hours)
- That’s approximately 1.2MB of storage per year per pool
I’d project that after 1–2 years, gas costs would become noticeably higher for functions that need to iterate through the observations array, and after 3–5 years, some functions might become too gas-intensive to use practically.
As Andre noted previously, this might be a non-issue for projects that aren’t actively using the TWAP functionality. If protocols aren’t actually using the TWAP functions, then they can simply update their forked contracts to remove the TWAP sections of code entirely . However, for projects that do utilize these features regularly, or have low-awareness of the storage requirements, exploring solutions to this problem would be beneficial.
Uniswap V3: The Cardinality Solution
Uniswap V3 addressed the storage growth problem with an elegant solution called “cardinality,” which combines fixed-size arrays with a dynamic usage mechanism.
The Cardinality Concept
Instead of an ever-growing array, Uniswap V3 uses a fixed-size array with a variable “active size” controlled by the cardinality parameter:
// Fixed maximum size array
Observation[65535] public observations;
// Actual number of active slots
uint16 public cardinality = 1;
// Target size after next expansion
uint16 public cardinalityNext = 1;
// Current write index
uint16 public observationIndex = 0;
The key innovations here are:
- Fixed Maximum Size: The array has a hard cap of 65,535 slots
- Minimal Initial Size: Pools start with just one active slot (cardinality = 1)
- Pay-to-Grow Model: Users can pay gas to increase the active portion
- Circular Buffer Pattern: New observations overwrite the oldest ones once full
How Cardinality Works
Let’s walk through a concrete example of how this works:
Initial State:
- cardinality = 1
- observationIndex = 0
- observations[0] = {timestamp: 1000, …}
First Trade:
- Write to index (0+1) % 1 = 0
- observations[0] = {timestamp: 1100, …} (overwrites previous)
- observationIndex = 0
User Increases Cardinality:
// User calls:
increaseObservationCardinalityNext(4);
// Now cardinalityNext = 4 (cardinality still = 1)
Next Trade:
- Since cardinalityNext > cardinality, initialize new slots
- cardinality = 4
- Write to index (0+1) % 4 = 1
- observations[1] = {timestamp: 1200, …}
- observationIndex = 1
Subsequent Trades:
- Write to index (1+1) % 4 = 2, then 3, then 0 (circular)
- Once full, oldest observations are overwritten
This approach has several significant advantages:
- Minimal Deployment Costs: Pools start small, keeping creation cheap
- Economic Alignment: Users who need longer history pay for it
- Sustainable Storage: The circular buffer prevents unbounded growth
- Flexible Sizing: Different pools can have appropriate sizes based on needs
Solution for Existing Solidly Forks
For projects that have already forked Solidly or implemented similar AMMs, the cardinality approach provides a potential solution to the unbounded storage growth problem. Here are some approaches these projects could explore:
- Implement Circular Buffers: Replace the unbounded array with a fixed-size array and circular buffer logic
- Add Cardinality Tracking: Maintain cardinality and index variables to manage buffer size
- Governance-Controlled Pruning: Allow governance to determine when and how to prune historical observations
- Pay-to-Grow Model: Let users who need longer history pay to increase the buffer size
These changes could maintain all the benefits of Solidly’s integrated oracle approach while solving the storage growth problem. Pools could still have appropriate observation density based on their needs, but without the risk of unbounded growth.
However, as mentioned earlier, this might not be a critical issue for projects where TWAP functions aren’t being actively used. Before implementing complex solutions, developers should assess whether the oracle functionality is actually being utilized in their forks.
The Flash Loan Challenge: A DeFi-Specific Vulnerability
While TWAP provided a solid foundation for DeFi oracles, it remained vulnerable to a challenge that simply doesn’t exist in traditional finance: flash loans. This DeFi innovation allows users to borrow unlimited capital without collateral, so long as the loan is repaid within a single transaction block.
Flash loans fundamentally change the security assumptions of financial systems. In traditional finance, manipulating markets requires substantial capital with opportunity costs and regulatory risks. With flash loans, attackers can momentarily control millions in capital without opportunity cost, creating price distortions that TWAP mechanisms can’t adequately protect against.
This uniquely DeFi vulnerability created the need for more manipulation-resistant oracle designs — ones that would make attacks prohibitively expensive even when flash loans remove traditional capital barriers. This challenge directly motivated the development of more sophisticated approaches like RWAP.
Reserve Weighted Average Price (RWAP)
While TWAP oracles provide manipulation resistance through time-weighting, they still have fundamental limitations. This led to the development of Reserve Weighted Average Price (RWAP) oracles, which take a fundamentally different approach to price calculation. As discussed above, the first on-chain implementation of RWAP was included in the BaseV1Factory contracts within the Solidly implementation on Fantom Network.
From TradFi Volume-Weighting to DeFi Reserve-Weighting
RWAP represents a DeFi-native innovation that addresses vulnerabilities not fully solved by adapting traditional concepts. Its closest TradFi analog would be Volume-Weighted Average Price (VWAP), which weights prices by the trading volume at each price level.
However, RWAP fundamentally differs from VWAP in several ways:
- Capital Commitment vs. Trade Volume: While VWAP considers how much was traded, RWAP considers how much capital was committed to the pool over time — a stronger economic security measure. This distinction is crucial because in DeFi, volume can be artificially inflated through multiple transactions using the same capital (e.g., flash loans).
- Integrated vs. Post-Trade: VWAP in traditional markets is calculated after trades occur, while RWAP is integrated into the AMM’s core pricing mechanism.
- Manipulation Resistance: VWAP can be manipulated by creating many small trades at extreme prices, while RWAP requires actual capital locked in the pool, making manipulation exponentially more expensive.
- Flash Loan Immunity: Unlike traditional finance where borrowing carries time costs, DeFi flash loans allow momentary access to massive capital without opportunity cost.
RWAP’s design specifically counters this unique DeFi vulnerability that has no TradFi equivalent. RWAP represents not just an adaptation of traditional finance concepts, but a fundamental reimagining of price calculation for the unique economic security requirements of permissionless blockchain environments.
RWAP: Mathematical Foundations
RWAP over a period [t₁, t₂] can be defined as:
> RWAP = P(R̄₀, R̄₁)
Where:
- R̄₀ = (1/(t₂-t₁)) ∫(t₁ to t₂) R₀(t) dt (time-weighted average of reserve 0)
- R̄₁ = (1/(t₂-t₁)) ∫(t₁ to t₂) R₁(t) dt (time-weighted average of reserve 1)
- P(R₀, R₁) is the AMM’s pricing function applied to reserves
For constant product AMM (x*y=k):
> P(R₀, R₁) = R₁/R₀ for token0→token1 price
For implementation on blockchain with discrete time steps:
> R̄₀ = (∑(i=1 to n) R₀(tᵢ) × (tᵢ — tᵢ₋₁)) / (t₂ — t₁)
> R̄₁ = (∑(i=1 to n) R₁(tᵢ) × (tᵢ — tᵢ₋₁)) / (t₂ — t₁)
> RWAP = R̄₁/R̄₀
Worked Example: ETH/USDC RWAP Calculation
Calculation for a 1-hour RWAP for ETH/USDC with the following reserve observations in a constant product AMM:
Direct TWAP Calculation (for comparison):
> TWAP = ($2000 × 0.25 + $2050 × 0.25 + $1980 × 0.3 + $2030 × 0.2) / 1.0 = $2012.5
RWAP Calculation:
First, calculate time-weighted average reserves:
- R̄₀ = (100 × 0.25 + 97.56 × 0.25 + 100.51 × 0.3 + 98.52 × 0.2) / 1.0 = 99.22 ETH
- R̄₁ = (200,000 × 0.25 + 200,000 × 0.25 + 199,000 × 0.3 + 200,000 × 0.2) / 1.0 = 199,700 USDC
Second, calculate RWAP by applying the AMM pricing function to average reserves:
- RWAP = R̄₁/R̄₀ = 199,700 / 99.22 = $2013.7
Cumulative Reserve Approach:
- At t=0: CumulativeReserve₀ = 0, CumulativeReserve₁ = 0
- At t=0.25: CumulativeReserve₀ = 0 + 100 × 0.25 = 25 CumulativeReserve₁ = 0 + 200,000 × 0.25 = 50,000
- At t=0.5: CumulativeReserve₀ = 25 + 97.56 × 0.25 = 49.39 CumulativeReserve₁ = 50,000 + 200,000 × 0.25 = 100,000
- At t=0.8: CumulativeReserve₀ = 49.39 + 100.51 × 0.3 = 79.542 CumulativeReserve₁ = 100,000 + 199,000 × 0.3 = 159,700
- At t=1.0: CumulativeReserve₀ = 79.542 + 98.52 × 0.2 = 99.246 CumulativeReserve₁ = 159,700 + 200,000 × 0.2 = 199,700
> R̄₀ = (CumulativeReserve₀ at t=1 — CumulativeReserve₀ at t=0) / (1–0) = 99.246 ETH
> R̄₁ = (CumulativeReserve₁ at t=1 — CumulativeReserve₁ at t=0) / (1–0) = 199,700 USDC RWAP = 199,700 / 99.246 = $2013.3
Notice that the RWAP ($2013.7 or $2013.3 depending on rounding) is slightly different from TWAP ($2012.5). This is a key distinction: RWAP reflects the price you would get by trading against the average reserves, which can differ from the average of spot prices when reserves change in non-uniform ways.
In manipulated markets, this difference becomes more pronounced — TWAP might be heavily influenced by brief price spikes, while RWAP would remain more stable as it requires actual capital commitment to change the reserves significantly.
How RWAP Works
Unlike TWAP which tracks cumulative prices directly, RWAP:
- Tracks cumulative reserves over time
- Calculates time-weighted average reserves for both tokens in the pair
- Applies the AMM’s pricing formula to these average reserves
The implementation looks similar to TWAP but with a critical difference:
// Update cumulative reserves
function _update(uint112 _reserve0, uint112 _reserve1) private {
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// Accumulate reserves weighted by time
reserve0Cumulative += uint(reserve0) * timeElapsed;
reserve1Cumulative += uint(reserve1) * timeElapsed;
}
// Record observation if needed
if (timeElapsed > periodSize) {
observations.push(Observation(
blockTimestamp,
reserve0Cumulative,
reserve1Cumulative
));
}
}
When calculating prices, RWAP first determines the average reserves over the period, then applies the pricing formula:
// Calculate average reserves over the period
uint avgReserve0 = (reserve0CumulativeEnd - reserve0CumulativeStart) / timeElapsed;
uint avgReserve1 = (reserve1CumulativeEnd - reserve1CumulativeStart) / timeElapsed;
// Calculate price from average reserves
uint price = tokenIn == token0 ?
(amountIn * avgReserve1 / avgReserve0) :
(amountIn * avgReserve0 / avgReserve1);
RWAP offers significant advantages in terms of manipulation resistance and capital efficiency compared to traditional TWAP approaches.
The RWAP Innovation
RWAP is one of the key features of Flying Tulip, and represents a next evolution in oracle design, applying the lessons learned from previous implementations.
Interestingly, the storage concerns highlighted for the Solidly forks are likely to be a lesser issue for Flying Tulip, thanks to its unique economic model. Flying Tulip operates on Sonic, a blockchain that returns 90% of gas fees to developers.
This innovative economic model allows Flying Tulip to implement more data-intensive oracle mechanisms like RWAP without concerns about user gas costs, effectively subsidizing the storage overhead that would otherwise make such systems prohibitively expensive.
Key Benefits of RWAP Over TWAP
RWAP provides several significant advantages over TWAP:
Superior Manipulation Resistance
- TWAP can be manipulated by briefly pushing prices to extreme values
- RWAP requires actually committing significant capital over the entire measurement period
- Makes attacks much more capital intensive and often unprofitable
Capital Efficiency Awareness
- RWAP inherently accounts for the “depth” behind each price
- Prices backed by more liquidity have greater influence
- Small trades in thin markets have proportionally less impact
Flash Loan Resistance
- Flash loan attacks would need to lock capital for the entire observation period
- The capital requirements make such attacks economically unviable in most cases
- Economic security scales with the value secured by the oracle
AMM Formula Flexibility
- RWAP adapts seamlessly to different AMM designs
- Works with constant product (x*y=k)
- Works with stable swap formulas (x³y+y³x=k)
- Works with the Adaptive curve formula
Liquidity Provider Impact Reflection
- RWAP captures changes in pool composition when LPs add or remove liquidity
- Large liquidity withdrawals are reflected in pricing
- Creates a more holistic view of market conditions
Real-World Comparison
Consider a scenario where a manipulator attempts to influence an oracle:
With TWAP:
- Attacker takes a flash loan
- Executes large trades to push price to extreme for a brief moment
- Oracle records the manipulated price in the cumulative
- Attack costs only transaction fees and flash loan interest (very cheap)
With RWAP:
- Attacker would need to significantly alter the pool’s reserves
- Capital must remain committed throughout the time window
- Cost scales with pool size and observation period
- Attack typically becomes economically unfeasible
Primary Use Cases for RWAP
RWAP is particularly valuable in several key DeFi applications:
Low-Liquidity Assets
- Provides greater security for pairs with limited liquidity
- Makes manipulation proportionally more expensive even for smaller pools
- Example: A governance token with limited liquidity could still be safely used as collateral if valued using RWAP
Lending Protocols
- Provides manipulation-resistant collateral valuation
- Reduces risk of unfair liquidations
- Makes flash loan attacks on liquidation systems unprofitable
- Example: A lending protocol using RWAP would have protection against short-term price manipulation that triggers unfair liquidations
Derivatives and Options
- Offers more reliable settlement prices for financial instruments
- Makes manipulation of expiry prices prohibitively expensive
- Example: An options protocol would have more reliable exercise prices, reducing the risk of manipulation at expiry
Additional potential applications that could benefit from RWAP include:
- Algorithmic Stablecoins: Improving price feed reliability for stability mechanisms and preventing manipulation of rebasing operations
- Cross-Chain Bridges: Ensuring accurate asset valuation when moving between blockchains and preventing cross-chain extraction attacks
The fundamental improvement RWAP provides over TWAP is a stronger economic security model that makes attack cost properly scale with both the value at risk and the potential profit from an attack, addressing a core vulnerability in traditional oracle designs.
The Price Oracle by Wonderland
It’s worth noting that RWAP isn’t the first attempt to address the vulnerabilities in TWAP oracles. Price by Wonderland also recognized the risks of oracle manipulation, particularly in the post-PoS environment where validators can control multiple sequential blocks.
Price builds directly on top of Uniswap V3’s infrastructure but approaches the problem by implementing:
- Manipulation Detection: monitors observation data for suspicious price movements. It identifies potential manipulation by looking for price changes exceeding certain thresholds (10% between consecutive observations) and comparing observations before and after suspected manipulation to determine if they’re abnormally different.
- Correction Mechanism: Rather than simply ignoring manipulated data points, Price actively corrects the observation array. When manipulation is detected, it calculates replacement values based on the average of surrounding non-manipulated observations, effectively “patching” the accumulator value. This ensures that queries overlapping with manipulation periods receive corrected data.
- Delay Window: Price introduced a 2-minute delay in oracle readings, which allows it to capture and correct up to 9-block manipulation patterns, including multi-block attacks where validators control consecutive blocks.
This solution also employed keeper jobs, in a similar way that the early implementation of Uni V2 leveraged keepers. However in this case, the keepers are incentivized to monitor and correct manipulations. With the jobs paid automatically with fees generated from position management.
Finally, this solution actually has a liquidity management component as unlike traditional oracle solutions that rely on external liquidity providers, Price ensures oracle quality by managing dedicated liquidity positions specifically for the oracle, making the liquidity more trustworthy and predictable.
While Price focused on detecting and correcting manipulation after it occurred, preserving the TWAP mechanism, RWAP takes a preventative approach by fundamentally redesigning how prices are calculated to make manipulation economically prohibitive in the first place. Both solutions recognize the same vulnerabilities but address them through different mechanisms.
Realized Volatility (RVOL)
Beyond just price oracles, DeFi protocols increasingly need volatility metrics for applications like options pricing, dynamic fees, and risk assessment. This is where Realized Volatility (RVOL) calculations become important.
What is Realized Volatility?
Realized volatility measures the historical price variation of an asset over a specific time period. In traditional finance, it’s calculated as the standard deviation of logarithmic returns, annualized to provide a comparable metric. This approach quantifies how much an asset’s price has actually fluctuated, providing a “rear-view mirror” perspective on price action.
RVOL: Mathematical Foundations
The formula for realized volatility can be written as:
σ = √[1/(n-1) ∑(i=1 to n-1) (ln(Pᵢ₊₁/Pᵢ) — μ)² ] · √N
Where:
- σ is the annualized volatility
- Pᵢ is the price at time i
- μ is the mean of the log returns (often approximated as 0 for short-term returns)
- n is the number of price observations
- N is the number of periods in a year (252 for daily trading data)
Note — since crypto markets operate 24/7/365, we should use 365 trading days per year, but since the Keep3rV1Volatility contract uses 252, we’ll stick with this in our examples.
This formula captures the magnitude of price movements relative to the asset’s price level. The logarithmic returns help normalize these movements regardless of the asset’s absolute price. For example, a $10 movement on a $100 asset (10%) has the same volatility impact as a $100 movement on a $1000 asset.
For blockchain implementation, this can be separated into two steps:
- Calculate the standard deviation of log returns: σ_stdev = √[1/(n-1) ∑(i=1 to n-1) (ln(Pᵢ₊₁/Pᵢ) — μ)² ]
- Annualize the standard deviation: σ_annualized = σ_stdev · √N
Standard Example: ETH/USDC Volatility Calculation
Consider a 5-day price series for ETH/USDC: [1500, 1600, 1550, 1650, 1700]
Step 1: Calculate log returns
- ln(1600/1500) = 0.0645
- ln(1550/1600) = -0.0317
- ln(1650/1550) = 0.0625
- ln(1700/1650) = 0.0299
Step 2: Calculate the variance (assuming mean ≈ 0 for simplicity)
- (0.064⁵² + (-0.0317)² + 0.062⁵² + 0.029⁹²) / 3
- ≈ 0.002491
Step 3: Calculate standard deviation
- √0.002491 = 0.0499
Step 4: Annualize
- 0.0499 × √252 ≈ 0.792 or 79.2%
This 79.2% annualized volatility means that, based on this price data, we would expect ETH’s price to typically fluctuate within a range of ±79.2% over the course of a year if this volatility pattern persisted.
Comparative Example: TWAP vs RWAP-based Volatility
To understand the difference between TWAP-based and RWAP-based volatility calculations, let’s use the same hourly ETH/USDC data from our earlier examples:
TWAP-based RVOL calculation:
- ln(2050/2000) = 0.02469, squared = 0.00061
- ln(1980/2050) = -0.03511, squared = 0.00123
- ln(2030/1980) = 0.02494, squared = 0.00062
- ln(2020/2030) = -0.00494, squared = 0.00002
> Standard deviation = √[(0.00061 + 0.00123 + 0.00062 + 0.00002)/3] = 0.02490
> Annualized TWAP-based volatility = 0.02490 × √8760 = 233%
RWAP-based RVOL calculation:
- ln(2040/2000) = 0.01980, squared = 0.00039
- ln(2000/2040) = -0.01980, squared = 0.00039
- ln(2015/2000) = 0.00746, squared = 0.00006
- ln(2013/2015) = -0.00099, squared = 0.000001
> Standard deviation = √[(0.00039 + 0.00039 + 0.00006 + 0.000001)/3] = 0.01313
> Annualized RWAP-based volatility = 0.01313 × √8760 = 123%
Notice that the RWAP-based volatility (123%) is significantly lower than the TWAP-based volatility (233%). This is because RWAP smooths out price movements that aren’t backed by significant capital commitment, filtering out “noise” from minor trades or temporary price spikes.
From Keep3rV1Volatility to Flying Tulip
The concept of on-chain volatility calculation is yet another area where Andre’s fingerprints are present as the first on-chain implementation seen was in the Keep3rV1Volatility contract, which worked in conjunction with Keep3rV1Oracle to provide realized volatility metrics for Uniswap V2 pairs. This represented a significant breakthrough in bringing sophisticated financial metrics on-chain.
The On-Chain Implementation Challenges
Implementing volatility calculations on-chain posed several significant challenges:
- Lack of Floating-Point Support: Solidity has no native support for floating-point arithmetic, requiring fixed-point math solutions
- Gas Efficiency: Calculations needed to be optimized to minimize gas costs
- Logarithm Calculation: Natural logarithms aren’t built into the EVM, requiring custom implementations
- Storage Limitations: Storing historical price data efficiently is challenging
The Keep3rV1Volatility contract tackled these challenges with several innovations:
While this implementation uses the same mathematical principles found in traditional finance, the technical approach differs dramatically. Where TradFi systems use specialized statistical packages with floating-point precision, DeFi required complete reinvention for blockchain environments. This gap between mathematical theory (directly borrowed from TradFi) and implementation (completely reimagined for DeFi) exemplifies why DeFi protocols couldn’t simply import traditional financial infrastructure but instead had to engineer novel solutions from first principles.
function vol(uint[] memory p) public pure returns (uint x) {
for (uint8 i = 1; i <= (p.length-1); i++) {
x += ((generalLog(p[i] * FIXED_1) - generalLog(p[i-1] * FIXED_1)))**2;
}
x = sqrt(uint(252) * sqrt(x / (p.length-1)));
return uint(1e18) * x / SQRT_1;
}
This volatility calculation depends on high-quality price data. The connection between the Keep3rV1Oracle and Keep3rV1Volatility contract is made through the rVol()
function:
function rVol(address tokenIn, address tokenOut, uint points, uint window) public view returns (uint) {
return vol(KV1O.sample(tokenIn, uint(10)**IERC20(tokenIn).decimals(), tokenOut, points, window));
}
The vol()
function:
- Takes an array of price points from the oracle
- Calculates log returns between consecutive prices using a custom
generalLog
function - Squares these returns (to get variance)
- Takes the square root of the average (to get standard deviation)
- Annualizes by multiplying by √252 (representing trading days in a year)
- Scales the result for fixed-point precision (18 decimals)
The final scaling step is particularly important for maintaining precision:
return uint(1e18) * x / SQRT_1;
Where SQRT_1
is defined as 13043817825332782212
, approximately sqrt(1e18 * 1e18). This scaling ensures the volatility is expressed as a fixed-point number with 18 decimal places of precision - the standard for most DeFi applications. Without this careful rescaling, the calculation would lose significant digits during the fixed-point operations, potentially leading to inaccurate volatility measurements.
The custom generalLog
function is a sophisticated implementation of logarithm calculation specifically designed for the EVM environment. Here’s the actual implementation:
function generalLog(uint256 x) public pure returns (uint) {
uint res = 0;
// If x >= 2, compute the integer part of log2(x)
if (x >= FIXED_2) {
uint8 count = floorLog2(x / FIXED_1);
x >>= count; // now x < 2
res = count * FIXED_1;
}
// Compute the fraction part of log2(x)
if (x > FIXED_1) {
for (uint8 i = 127; i > 0; --i) {
x = (x * x) / FIXED_1; // now 1 < x < 4
if (x >= FIXED_2) {
x >>= 1; // now 1 < x < 2
res += uint(1) << (i - 1);
}
}
}
return res * LOG_10_2 / BASE;
}
This function uses several important constants:
FIXED_1
: Represents 1.0 in fixed-point format (approximately 2^131)FIXED_2
: Represents 2.0 (or 2 * FIXED_1)LOG_10_2
: The value of log₁₀(2), used to convert from base-2 to base-10BASE
: A scaling factor for the fixed-point arithmetic
The first part calculates the integer portion of log₂(x):
if (x >= FIXED_2) {
uint8 count = floorLog2(x / FIXED_1);
x >>= count; // now x < 2
res = count * FIXED_1;
}
This works by:
- Checking if x ≥ 2.0
- Calculating
floorLog2(x / FIXED_1)
to find the largest integer n such that 2^n ≤ x - Right-shifting x by n bits (
x >>= count
), which divides x by 2^n - Adding n to the result, scaled by FIXED_1
For example, if x = 10.0 (in fixed-point format):
floorLog2(10)
= 3 (since 2³ = 8 is the largest power of 2 less than or equal to 10)- x becomes 10/8 = 1.25, which is now in the range [1, 2)
- res = 3 * FIXED_1
The second part calculates the fractional portion of log₂(x) using a binary approximation technique:
if (x > FIXED_1) {
for (uint8 i = 127; i > 0; --i) {
x = (x * x) / FIXED_1; // now 1 < x < 4
if (x >= FIXED_2) {
x >>= 1; // now 1 < x < 2
res += uint(1) << (i - 1);
}
}
}
This implements a brilliant algorithm for binary logarithm calculation:
- The loop iterates from i = 127 down to 1, providing high precision for the fractional part
- In each iteration:
- x is squared and then normalized:
x = (x * x) / FIXED_1
- If the squared value is ≥ 2.0, it means the next binary digit of the logarithm is 1
- In that case, we add 2^(i-1) to the result and divide x by 2
- Otherwise, the binary digit is 0, and we do nothing
Let’s trace through a simple example for x = 1.5:
- First iteration (i = 127):
- x = 1.⁵² = 2.25
- Since 2.25 ≥ 2.0, we add ²¹²⁶ to res and set x = 2.25/2 = 1.125 - Second iteration (i = 126):
- x = 1.1²⁵² = 1.265625
- Since 1.265625 < 2.0, we do nothing - Third iteration (i = 125):
- x = 1.2656²⁵² = 1.601807
- Since 1.601807 < 2.0, we do nothing - Fourth iteration (i = 124):
- x = 1.6018⁰⁷² = 2.565784
- Since 2.565784 ≥ 2.0, we add ²¹²³ to res and set x = 2.565784/2 = 1.282892
And so on for all 127 iterations.
This algorithm is effectively calculating the binary expansion of log₂(x) for x in [1, 2): log₂(x) = b₁/2 + b₂/4 + b₃/8 + … + bₙ/2^n + …
Where each bᵢ is either 0 or 1, determined by the algorithm above.
The third, and final part, is the base conversion. Where the result is converted from base-2 to base-10:
return res * LOG_10_2 / BASE;
This multiplies by log₁₀(2) (approximately 0.301029996) and divides by BASE to get the correct fixed-point representation.
This implementation is particularly well-suited for on-chain computation because:
- No Division: It mostly uses multiplications, bit-shifts, and comparisons, which are computationally efficient in the EVM
- No Floating Point: It works entirely with integers and fixed-point arithmetic
- Adjustable Precision: By changing the number of iterations, you can trade off precision vs. gas costs
- No External Libraries: It doesn’t rely on any math libraries or precompiles
- Gas Efficiency: The algorithm is optimized to minimize gas consumption while maintaining high precision
This ingenious approach to logarithm calculation proved that on-chain volatility calculation was feasible.
The RVOL implementation in these contracts follows a clear technical architecture:
- Uniswap V2 Pairs → Generate swap events and update cumulative prices
- Keep3rV1Oracle → Periodically records these cumulative prices as observations
- Keep3rV1Oracle.sample() → Converts observations into TWAP values for specific windows
- Keep3rV1Volatility.rVol() → Requests these TWAP samples and passes them to vol()
- Keep3rV1Volatility.vol() → Calculates volatility from the price samples
- Applications → Use the volatility metrics for options pricing, risk assessment, etc.
This separation of concerns allows each contract to specialize in its core functionality while maintaining a clean data flow between them.
It’s important to note that while the Keep3rV1Volatility contract implements TWAP-based RVOL calculation as described above, the RWAP-based approach represents a theoretical extension that isn’t implemented in these original contracts. This evolution from TWAP to RWAP for volatility calculation represents the next frontier in DeFi oracle design — taking the mathematical framework established in Keep3rV1Volatility but feeding it with more economically significant price data.
Flying Tulip’s implementation is likely to build upon this foundation, potentially with refinements to work with RWAP data instead of, OR together with, TWAP data.
TWAP vs. RWAP for RVOL Calculation
The choice between using TWAP or RWAP as the price input for RVOL calculations has significant implications:
TWAP-based RVOL:
- Captures all price movements, regardless of capital backing
- More sensitive to short-term price spikes
- Could include “noise” from manipulations or flash loans
- May overstate actual tradeable volatility
RWAP-based RVOL:
- Filters out price movements not backed by significant capital
- Focuses on “economically meaningful” volatility
- More resistant to manipulation
- Better represents actual tradeable volatility
When used together, they create a more comprehensive picture of market dynamics, similar to how traditional markets use both price volatility and volume analysis.
- Consistent readings across both indicates robust price discovery
- RWAP volatility trending higher than TWAP could indicate increasing capital commitment to directional bets
If TWAP and RWAP show significant divergence, it could indicate:
- Attempted price manipulation
- Flash loan activity
- Unusual trading patterns
- Liquidity fragmentation
Implementation in DeFi Contracts
The RVOL calculation in DeFi protocols can be integrated directly with the underlying oracle mechanism. While Keep3rV1Volatility demonstrated the first implementation using TWAP data, future protocols like Flying Tulip will expand these capabilities.
For TWAP-based volatility calculation, the implementation follows a straightforward pattern: request price samples from the time-weighted oracle, then process these samples through the volatility calculation. This pattern extends naturally to daily, weekly, and other time frames by adjusting the sampling parameters.
For RWAP-based volatility calculation, the architecture would be similar but with a crucial difference: instead of using price samples derived from cumulative price accumulators, it would use price samples calculated from cumulative reserve observations. The oracle would track and store reserve0Cumulative and reserve1Cumulative values over time, calculating average reserves for each period before applying the AMM’s pricing formula.
The beauty of this approach is that the core volatility calculation remains identical regardless of the price data source. The same mathematical formula that calculates standard deviation, annualizes it, and scales it for fixed-point precision can process either TWAP or RWAP price series. This allows protocols to implement both measurement types with minimal code duplication, enabling direct comparisons between the two volatility metrics.
Such a dual implementation would give protocols powerful insights into market dynamics, with TWAP-based RVOL capturing all price movements and RWAP-based RVOL focusing on economically significant price action. The divergence between these metrics itself becomes a valuable signal, potentially indicating manipulation attempts, flash loan activity, or unusual trading patterns.
Applications of On-Chain Volatility
Having reliable, manipulation-resistant volatility metrics on-chain enables several powerful applications.
One of the most significant applications will be in Flying Tulip’s adaptive curve AMM, which will use volatility data to automatically adjust curve parameters based on market conditions. Specifically, via an adjustment with EWMA (Exponentially Moving Weighted Averages) to time-weight the measure of volatility in determining the adjustment of the curve. This will play a key role in creating self-adjusting curves that optimize for capital efficiency across varying market conditions.
Side Note #1— EMWA has the attractive feature that very little data has to be stored, which makes it very suitable for on-chain conditions.
Side Note #2 — the decay factor (λ≈0.94) suggested is adapted from the RiskMetrics database originally created by JP Morgan and made publically available in 1994. 0.94 was selected because they found that across a range of different market variables this value gave the forecast of variance rates that came closest to the realized variance rate.
Beyond the adaptive curve AMM, on-chain volatility enables several other powerful applications:
- Options Pricing
- The Black-Scholes formula requires volatility as an input
- On-chain options can use RVOL for fair pricing
- IV (Implied Volatility) oracles will complement RVOL to create complete options pricing infrastructure
2. Dynamic Fee Adjustment
- AMMs can adjust swap fees based on recent volatility
- Higher volatility = higher fees to compensate for increased risk
- Example: A pool charging 0.01% base fee might scale upwards to 0.1% during high volatility periods
3. Risk Assessment
- Lending protocols can adjust collateral requirements based on asset volatility
- More volatile assets require higher collateralization
- Example: A lending protocol might require 150% collateralization for stable assets but 300% for assets showing high volatility
RWAP-based volatility metrics would be particularly valuable for these applications as they would better represent actual tradeable volatility, reducing the impact of manipulations and flash loan attacks.
Advanced Statistical Challenges: Stationarity and Volatility Clustering
Beyond the basic implementation challenges of on-chain oracles, DeFi protocols must contend with two critical statistical phenomena that affect financial markets: non-stationarity and volatility clustering.
Non-Stationarity in Crypto Markets
Time series non-stationarity — where statistical properties like mean and variance change over time — is particularly pronounced in cryptocurrency markets. This creates fundamental challenges for oracle designs:
- Regime Shifts: Crypto markets frequently transition between different volatility regimes, from periods of calm to extreme turbulence
- Structural Breaks: Protocol upgrades, regulatory news, or market structure changes can permanently alter price dynamics
- Evolving Correlations: Relationships between assets shift unpredictably, affecting cross-asset pricing
Traditional time-weighted mechanisms like TWAP implicitly assume some degree of stationarity, potentially reducing their effectiveness during regime transitions.
Volatility Clustering
Volatility clustering — the tendency of high volatility periods to be followed by more high volatility — creates another layer of complexity:
- Persistence: Large price movements are likely to be followed by more large movements
- Asymmetry: Downward price movements often generate more volatility than upward movements
- Fat Tails: Extreme price events occur more frequently than normal distributions would predict
These characteristics make standard variance calculations insufficient for risk management and precise price discovery in DeFi.
The IV Oracle Advantage
This is where Flying Tulip’s integrated oracle approach — particularly its Implied Volatility (IV) oracle — creates a significant breakthrough. Rather than attempting to model these complex statistical phenomena directly on-chain (which would require sophisticated time-series models like GARCH that are gas-intensive), the IV oracle elegantly outsources this complexity to options market participants.
Implied volatility from options markets naturally incorporates:
- Forward-Looking Assessment: Unlike backward-looking statistical measures, IV represents market expectations about future volatility
- Regime Change Detection: Options traders quickly price in regime shifts, causing IV to adjust rapidly
- Volatility Term Structure: Different option expirations provide a complete picture of expected volatility evolution
- Market Consensus: IV aggregates diverse trader perspectives about future market conditions
When combined with RWAP and RVOL, the IV oracle creates a comprehensive view of market dynamics that addresses both historical patterns and future expectations. By combining backward-looking metrics (RWAP, RVOL) with forward-looking market expectations (IV), Flying Tulip’s oracle suite creates a comprehensive solution to the non-stationarity and volatility clustering challenges.
Convergence or Continued Divergence?
As DeFi oracles mature, an interesting question emerges: will DeFi and TradFi oracle approaches eventually converge, or are they on permanently divergent trajectories?
Arguments for eventual convergence include Layer 2 scaling (reducing computational constraints), zero-knowledge proofs (enabling complex off-chain calculations with on-chain verification), and potential regulatory frameworks that might bridge both worlds.
However, fundamental differences suggest continued divergence: DeFi’s trustless requirement, the persistence of flash loans and other atomic transaction capabilities, and the fundamental difference between AMM and order book market structures.
The most likely outcome may be a hybrid approach — maintaining DeFi’s innovations in economic security while selectively incorporating TradFi’s statistical sophistication where blockchain constraints allow. This evolution will likely create systems that borrow from both traditions while remaining distinct from either, continuing the innovation cycle that has characterized DeFi oracle development.
Comprehensive Comparison of DeFi Oracle Mechanisms
The Evolution of Oracle Design
Looking at the progression from Uniswap V2’s basic TWAP, to Keep3rV1Oracle’s external implementation, to BaseV1/Solidly’s integrated approach, to Uniswap V3’s sustainable storage, to Flying Tulip’s RWAP innovation, we can see a clear evolution in oracle design philosophy driven primarily by Andre Cronje’s continued innovations in DeFi.
Each generation has addressed specific limitations of the previous approach:
- Basic TWAP (Uniswap V2)
- Simple but limited
- External implementation required
- Minimal storage requirements
2. External Oracle (Keep3rV1Oracle)
- Enhanced functionality
- Historical data storage
- Volatility calculation
- Keeper dependency
3. Sustainable TWAP (Uniswap V3)
- Efficient circular buffer
- Pay-to-grow model
- Storage limitations solved
4. Integrated TWAP (Solidly)
- Enhanced usability
- Built-in historical data
- Direct AMM integration
- Storage growth concerns
5. RWAP & RVOL (Flying Tulip)
- Fundamental security improvement
- Capital-weighted pricing
- Superior manipulation resistance
- Enhanced volatility metrics
The most recent advancements with RWAP and RVOL represent a shift from looking at price alone to considering the economic security behind that price, providing DeFi protocols with more reliable and meaningful data for critical operations.
What’s clear, and hopefully has been clearly demonstrated in this article, is that oracle design continues to evolve as DeFi matures. The progression from simple time-weighting to sophisticated reserve-weighting with integrated volatility calculations shows the innovation happening at the infrastructure layer of DeFi, with Andre Cronje consistently at the forefront of these advances.
As these oracle mechanisms are further refined and adopted, we can expect to see more secure, efficient, and capital-aware DeFi protocols that can scale to manage larger amounts of value with greater confidence.
See you in the future, where we’ll find out how it all plays out.
References
- Options, Futures, and Other Derivatives, John C. Hull
- Uniswap V2, TWAP Implementation — Documentation
- Keep3r Oracles, TWAP Implementation with Observations — Contract
- Solidly BaseV1Core, TWAP Implementation with Observations — GitHub fork of Soldily Code
- Uniswap V3, TWAP with Cardinality — Documentation
- Solidly BaseV1Factory, RWAP Implementation with Observations — Contract
- Price Oracle, a Solution for securing reserves within Uniswap V3 — Documentation
- Keep3r Volatility, TWAP Implementation of RVOL — Contract
- Flying Tulip, with TWAP, RWAP, RVOL & IV Oracles—Website
This article is for educational purposes only and does not constitute investment advice.