Skip to main content

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 uint256
  • decimals: Number of decimal places in the price

Process:

  1. Creates AggregatorV3Interface instance
  2. Calls latestRoundData() to get current price
  3. Validates price is non-negative
  4. 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

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

  1. Always check price freshness using Chainlink's round data
  2. Implement fallback mechanisms for aggregator failures
  3. Validate aggregator addresses before configuration
  4. Use appropriate conversion units for decimal normalization

For Maintainers

  1. Monitor Chainlink aggregator health and updates
  2. Set conservative maximum spreads initially
  3. Regular audits of price pair configurations
  4. Prepare for aggregator migrations and updates

Integration Guidelines

  1. Cache frequently accessed prices to reduce gas costs
  2. Implement price staleness checks in consuming contracts
  3. Use events to monitor price pair configuration changes
  4. 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.