Files
smom-dbis-138/contracts/oracle/OracleWithCCIP.sol
2025-12-12 14:56:07 -08:00

139 lines
4.5 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "./Aggregator.sol";
import "../ccip/CCIPSender.sol";
/**
* @title Oracle Aggregator with CCIP Integration
* @notice Extends Aggregator with CCIP cross-chain messaging capabilities
* @dev Automatically sends oracle updates to other chains via CCIP when updates occur
*/
contract OracleWithCCIP is Aggregator {
CCIPSender public ccipSender;
bool public ccipEnabled;
// Destination chain configurations (using CCIPSender's destinations)
uint64[] public ccipDestinationChains;
event CCIPUpdateSent(
bytes32 indexed messageId,
uint64 indexed destinationChainSelector,
uint256 answer,
uint256 roundId
);
event CCIPEnabled(bool enabled);
event CCIPSenderUpdated(address oldSender, address newSender);
constructor(
string memory _description,
address _admin,
uint256 _heartbeat,
uint256 _deviationThreshold,
address _ccipSender
) Aggregator(_description, _admin, _heartbeat, _deviationThreshold) {
require(_ccipSender != address(0), "OracleWithCCIP: zero sender address");
ccipSender = CCIPSender(_ccipSender);
ccipEnabled = true;
}
/**
* @notice Update the answer and send to CCIP destinations
* @param answer New answer value
*/
function updateAnswer(uint256 answer) external override onlyTransmitter whenNotPaused {
uint256 currentRound = latestRound;
Round storage round = rounds[currentRound];
// Check if we need to start a new round
if (round.updatedAt == 0 ||
block.timestamp >= round.startedAt + heartbeat ||
shouldUpdate(answer, round.answer)) {
currentRound = latestRound + 1;
latestRound = currentRound;
rounds[currentRound] = Round({
answer: answer,
startedAt: block.timestamp,
updatedAt: block.timestamp,
answeredInRound: currentRound,
transmitter: msg.sender
});
emit NewRound(currentRound, msg.sender, block.timestamp);
// Send to CCIP destinations if enabled
if (ccipEnabled) {
_sendToCCIP(currentRound, answer);
}
} else {
// Update existing round
round.updatedAt = block.timestamp;
round.transmitter = msg.sender;
}
emit AnswerUpdated(int256(answer), currentRound, block.timestamp);
}
/**
* @notice Send oracle update to all CCIP destinations
*/
function _sendToCCIP(uint256 roundId, uint256 answer) internal {
uint64[] memory destinations = ccipSender.getDestinationChains();
for (uint256 i = 0; i < destinations.length; i++) {
uint64 chainSelector = destinations[i];
try ccipSender.sendOracleUpdate(
chainSelector,
answer,
roundId,
block.timestamp
) returns (bytes32 messageId) {
emit CCIPUpdateSent(messageId, chainSelector, answer, roundId);
} catch {
// Log error but don't revert
// In production, consider adding error tracking
}
}
}
/**
* @notice Enable/disable CCIP
*/
function setCCIPEnabled(bool enabled) external {
require(msg.sender == admin, "OracleWithCCIP: only admin");
ccipEnabled = enabled;
emit CCIPEnabled(enabled);
}
/**
* @notice Update CCIP sender
*/
function updateCCIPSender(address newSender) external {
require(msg.sender == admin, "OracleWithCCIP: only admin");
require(newSender != address(0), "OracleWithCCIP: zero address");
address oldSender = address(ccipSender);
ccipSender = CCIPSender(newSender);
emit CCIPSenderUpdated(oldSender, newSender);
}
/**
* @notice Get CCIP destinations
*/
function getCCIPDestinations() external view returns (uint64[] memory) {
return ccipSender.getDestinationChains();
}
/**
* @notice Get fee for sending to CCIP destination
*/
function getCCIPFee(uint64 chainSelector) external view returns (uint256) {
bytes memory data = abi.encode(uint256(0), uint256(0), uint256(0));
return ccipSender.calculateFee(chainSelector, data);
}
}