ChainlinkPriceFeed
Overview
The ChainlinkPriceFeed contract (src/oracle/implementations/ChainlinkPriceFeed.sol) provides Chainlink oracle integration for the Citadel Finance protocol. It implements the standardized price feed interface while handling Chainlink-specific functionality like aggregator interfaces and price validation.
Purpose
- Chainlink Integration: Direct interface with Chainlink price aggregators
- Price Validation: Ensures price data integrity and prevents negative values
- Standardization: Implements common price feed interface for protocol compatibility
- Configuration Management: Manages price pair configurations and conversion units
Contract Details
- File:
src/oracle/implementations/ChainlinkPriceFeed.sol - Inheritance:
SynthereumPriceFeedImplementation - License: AGPL-3.0-only
Key Functions
Configuration
setPair
function setPair(
string calldata _priceId,
Type _kind,
address _source,
uint256 _conversionUnit,
bytes calldata _extraData,
uint64 _maxSpread
) public override
Purpose: Configures a Chainlink price pair for the oracle system.
Parameters:
_priceId: Human-readable identifier for the price pair (e.g., "EUR/USD")_kind: Type of pair (STANDARD or REVERSED)_source: Chainlink aggregator contract address_conversionUnit: Conversion factor for price normalization (0 = no conversion)_extraData: Additional configuration data (unused in Chainlink implementation)_maxSpread: Maximum allowed spread for the pair (must be > 0)
Access Control: Only maintainer Requirements:
_maxSpread > 0(dynamic spread not supported)- Valid Chainlink aggregator address
Price Retrieval
_getOracleLatestRoundPrice
function _getOracleLatestRoundPrice(
bytes32,
address _source,
bytes memory
) internal view override returns (uint256 price, uint8 decimals)
Purpose: Fetches the latest price from a Chainlink aggregator.
Parameters:
_source: Chainlink aggregator contract address- Other parameters unused but required by interface
Returns:
price: Latest price as uint256decimals: Number of decimal places in the price
Process:
- Creates
AggregatorV3Interfaceinstance - Calls
latestRoundData()to get current price - Validates price is non-negative
- Returns price and decimals
Security: Reverts on negative prices
Spread Management
_getDynamicMaxSpread
function _getDynamicMaxSpread(
bytes32,
address,
bytes memory
) internal view virtual override returns (uint64)
Purpose: Dynamic spread calculation (not supported in Chainlink implementation).
Returns: Always reverts with "Dynamic max spread not supported" Note: Chainlink implementation requires fixed maximum spreads
Price Types
Standard vs Reversed Pairs
- STANDARD: Direct price from aggregator (e.g., ETH/USD = 2000)
- REVERSED: Inverted price calculation (e.g., USD/ETH = 1/2000 = 0.0005)
Conversion Units
Conversion units allow price normalization:
0: No conversion (use raw aggregator price)> 0: Multiply aggregator price by conversion unit- Common use: Converting between different decimal representations
Integration with Chainlink
Aggregator Interface
interface AggregatorV3Interface {
function latestRoundData() external view returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function decimals() external view returns (uint8);
}
Supported Aggregators
The contract works with any Chainlink aggregator implementing AggregatorV3Interface:
- Currency pairs (EUR/USD, GBP/USD, etc.)
- Crypto pairs (ETH/USD, BTC/USD, etc.)
- Commodity pairs (Gold/USD, Oil/USD, etc.)
Access Control
Roles
- Admin: Can manage role assignments
- Maintainer: Can configure price pairs and update settings
Inherited Security
Inherits access control from SynthereumPriceFeedImplementation:
- Role-based permissions
- Secure initialization
- Protected configuration functions
Error Handling
Validation Checks
- Negative Prices: Reverts if Chainlink returns negative price
- Zero Spread: Reverts if maximum spread is set to 0
- Invalid Sources: Reverts if aggregator calls fail
Common Errors
"Negative value": Chainlink returned negative price"Max spread can not be dynamic": Attempted to set dynamic spread"Implementation not found": Invalid aggregator address
Usage Examples
Setting Up EUR/USD Pair
// Chainlink EUR/USD aggregator on Ethereum mainnet
address eurUsdAggregator = 0xb49f677943BC038e9857d61E7d053CaA2C1734C1;
chainlinkPriceFeed.setPair(
"EUR/USD", // Price identifier
Type.STANDARD, // Standard pair type
eurUsdAggregator, // Chainlink aggregator
0, // No conversion unit
"", // No extra data
500 // 5% max spread (500 basis points)
);
Setting Up Reversed Pair
// For USD/EUR (reversed from EUR/USD)
chainlinkPriceFeed.setPair(
"USD/EUR",
Type.REVERSED,
eurUsdAggregator, // Same aggregator, reversed calculation
0,
"",
500
);
Price Retrieval
// Get latest EUR/USD price
(uint256 price, uint8 decimals) = chainlinkPriceFeed._getOracleLatestRoundPrice(
bytes32(0), // Unused
eurUsdAggregator, // Aggregator address
"" // Unused
);
// Price is returned with 'decimals' decimal places
// Example: price = 118500000, decimals = 8 means 1.185 EUR/USD
Security Considerations
Price Validation
- Always validates against negative prices
- Chainlink aggregators can return stale data - implement staleness checks in consumer contracts
- Consider circuit breakers for extreme price movements
Aggregator Risks
- Chainlink aggregators can be deprecated or updated
- Monitor aggregator health and update sources as needed
- Validate aggregator responses before using prices
Maximum Spread
- Fixed spreads prevent dynamic manipulation
- Set appropriate spreads based on asset volatility
- Monitor spread violations and adjust as needed
Best Practices
For Developers
- Always check price freshness using Chainlink's round data
- Implement fallback mechanisms for aggregator failures
- Validate aggregator addresses before configuration
- Use appropriate conversion units for decimal normalization
For Maintainers
- Monitor Chainlink aggregator health and updates
- Set conservative maximum spreads initially
- Regular audits of price pair configurations
- Prepare for aggregator migrations and updates
Integration Guidelines
- Cache frequently accessed prices to reduce gas costs
- Implement price staleness checks in consuming contracts
- Use events to monitor price pair configuration changes
- Test with Chainlink testnet aggregators before mainnet deployment
The ChainlinkPriceFeed contract provides reliable, decentralized price data to the Citadel Finance protocol through battle-tested Chainlink infrastructure.