139 lines
4.5 KiB
Solidity
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);
|
|
}
|
|
}
|
|
|