The SwapTrade contract now includes a sophisticated price oracle integration system that protects users from front-running, price manipulation, and unfavorable swap rates. This integration follows industry best practices similar to Chainlink's oracle design.
- Integrates with external price oracle contracts
- Fetches real-time token prices for swap validation
- Supports multiple oracle providers per token
- Automatically calculates fair minimum output amounts based on oracle prices
- Compares swap outputs against oracle-backed minimums
- Reverts transactions that fall below acceptable thresholds
- Validates timestamp of oracle price updates
- Configurable maximum data age (default: 1 hour)
- Prevents swaps when price data is outdated
- Configurable slippage tolerance (default: 1% = 100 basis points)
- Maximum slippage capped at 5% for safety
- Enforced at the protocol level
OraclePriceUsed: Logs price data usageOracleStaleData: Alerts when data is too oldOracleFailure: Records oracle access failuresOracleConfigUpdated: Tracks configuration changesMinimumRateEnforced: Documents rate protection triggers
src/
├── interfaces/
│ └── IPriceOracle.cairo # Oracle interface (similar to Chainlink)
├── oracle/
│ └── price_oracle.cairo # Price validation and calculation logic
├── types.cairo # Oracle-related data structures
└── lib.cairo # Main contract with oracle integration
struct PriceData {
price: u256, // Price value
decimals: u8, // Decimal precision
timestamp: u64, // Update timestamp
round_id: u128, // Oracle round ID
}struct OracleConfig {
oracle_address: ContractAddress, // Oracle contract address
max_staleness: u64, // Maximum age in seconds
is_active: bool, // Active status
}struct SwapQuote {
amount_out: u256, // Expected output
price: u256, // Oracle price used
min_amount_out: u256, // Minimum acceptable output
slippage_bps: u256, // Slippage in basis points
}// Set oracle for a token
set_oracle_address(
token: felt252, // Token identifier
oracle_address: ContractAddress, // Oracle contract
max_staleness: u64 // Max age in seconds
)// Enable/disable oracle globally
set_oracle_enabled(enabled: bool)
// Set global staleness limit
set_global_max_staleness(max_staleness: u64)
// Set maximum slippage
set_max_slippage_bps(slippage_bps: u256)// Get quote with oracle prices
let quote = get_swap_quote(
token_in: felt252,
token_out: felt252,
amount_in: u256
)
// Returns: SwapQuote with expected output and minimum// Swap with oracle protection (if enabled)
swap(
token_in: felt252,
token_out: felt252,
amount_in: u256,
min_amount_out: u256, // Your minimum
recipient: ContractAddress
)
// If oracle is enabled, enforces MAX(min_amount_out, oracle_minimum)// Check oracle status
is_oracle_enabled() -> bool
// Get oracle configuration
get_oracle_config(token: felt252) -> OracleConfig
// Get settings
get_global_max_staleness() -> u64
get_max_slippage_bps() -> u256// 1. Set oracle for ETH
set_oracle_address(
0xETH, // ETH token
0x1234...ORACLE_ADDRESS, // Chainlink oracle
3600_u64 // 1 hour max staleness
)
// 2. Set oracle for USDC
set_oracle_address(
0xUSDC,
0x5678...ORACLE_ADDRESS,
3600_u64
)
// 3. Enable oracle protection
set_oracle_enabled(true)
// 4. Set 0.5% maximum slippage
set_max_slippage_bps(50_u256)// Short staleness window for high security
set_global_max_staleness(600_u64) // 10 minutes
// Lower slippage tolerance
set_max_slippage_bps(25_u256) // 0.25%// Longer staleness window
set_global_max_staleness(7200_u64) // 2 hours
// Higher slippage for volatile markets
set_max_slippage_bps(300_u256) // 3%The system prevents swaps when price data is outdated:
// Price must be recent
if (current_time - price_timestamp) > max_staleness {
// Transaction reverts
}All prices must meet minimum validity criteria:
// Price must be positive
assert(price >= MIN_VALID_PRICE, 'Invalid price')
// Price must not be stale
assert(validate_price_data(price_data), 'Stale data')Protocol enforces maximum slippage:
const MAX_SLIPPAGE_BPS: u256 = 500_u256; // 5% maximumThe IPriceOracle interface is designed to be compatible with Chainlink-style oracles:
trait IPriceOracle {
// Get latest price
fn get_latest_price(token: felt252) -> (u256, u8, u64);
// Get specific round data
fn get_round_data(token: felt252, round_id: u128) -> (u128, u256, u8, u64, u128);
// Get decimals
fn decimals(token: felt252) -> u8;
// Get description
fn description(token: felt252) -> felt252;
// Get version
fn version() -> u32;
}| Error | Cause | Solution |
|---|---|---|
Oracle: Stale or invalid price |
Price data is too old | Check oracle is updating regularly |
Oracle for token_in not active |
Oracle not configured for token | Set oracle address for token |
Oracle not enabled |
Global oracle disabled | Enable oracle with set_oracle_enabled(true) |
Slippage: amount_out < min |
Output below minimum | Increase slippage tolerance or retry |
Slippage too high |
Slippage exceeds 5% | Use lower slippage value |
When oracle is disabled, the contract operates in normal mode:
- No oracle price checks
- Only user-specified minimums enforced
- Standard slippage protection applies
Test oracle utility functions:
scarb test oracle_priceTest full oracle integration:
scarb test oracle_testsThe test suite covers:
- ✅ Oracle configuration
- ✅ Price validation
- ✅ Stale data detection
- ✅ Slippage calculation
- ✅ Minimum rate enforcement
- ✅ Access control
- ✅ Edge cases
Expected Output = (Amount In × Price In) / Price Out
Minimum Output = Expected Output × (10000 - Slippage BPS) / 10000
Amount In: 100 tokens
Price In: $200 per token
Price Out: $100 per token
Slippage: 1% (100 BPS)
Expected Output = (100 × 200) / 100 = 200 tokens
Minimum Output = 200 × (10000 - 100) / 10000 = 198 tokens
- Regular Oracle Updates: Ensure oracles update frequently
- Conservative Staleness: Use shorter windows for volatile assets
- Monitor Events: Watch for
OracleStaleDataandOracleFailureevents - Test Oracles: Verify oracle functionality before enabling
- Check Quotes: Use
get_swap_quote()before swapping - Set Reasonable Minimums: Don't set minimum too tight in volatile markets
- Monitor Slippage: Understand current slippage settings
- Verify Transactions: Check event logs for confirmation
- Implement IPriceOracle: Follow interface exactly
- Update Frequently: Keep price data fresh
- Handle Errors: Provide clear error messages
- Test Thoroughly: Cover all edge cases
- Deploy price oracle contracts
- Configure oracle addresses for all tokens
- Set appropriate staleness limits
- Configure slippage tolerance
- Test with small amounts first
- Enable oracle protection
- Monitor events and logs
- Document oracle sources
// Oracle module constants
DEFAULT_MAX_STALENESS: u64 = 3600_u64; // 1 hour
MIN_VALID_PRICE: u256 = 1_u256; // Minimum valid price
MAX_SLIPPAGE_BPS: u256 = 500_u256; // 5% maximum
BPS_DIVISOR: u256 = 10000_u256; // 100% in basis pointsPotential improvements for future versions:
- Multiple Oracle Support: Aggregate prices from multiple sources
- Dynamic Slippage: Adjust based on market volatility
- Circuit Breakers: Pause swaps during extreme price movements
- Oracle Reputation: Track oracle reliability
- Fallback Oracles: Use backup oracles if primary fails
For questions or issues:
- Check test files for usage examples
- Review event logs for debugging
- Consult the CONTRIBUTING.md guide
- Open an issue on GitHub
This oracle integration is part of the SwapTrade contract and follows the same license terms.