- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control. - Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities. - Created .gitmodules to include OpenZeppelin contracts as a submodule. - Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment. - Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks. - Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring. - Created scripts for resource import and usage validation across non-US regions. - Added tests for CCIP error handling and integration to ensure robust functionality. - Included various new files and directories for the orchestration portal and deployment scripts.
131 lines
4.6 KiB
Solidity
131 lines
4.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "./IRouterClient.sol";
|
|
import "./CCIPMessageValidator.sol";
|
|
import "../oracle/IAggregator.sol";
|
|
|
|
// Note: This contract must be added as a transmitter to the oracle aggregator
|
|
// to be able to update oracle answers. The aggregator's updateAnswer function
|
|
// requires the caller to be a transmitter.
|
|
|
|
/**
|
|
* @title CCIP Receiver with Oracle Integration
|
|
* @notice Receives CCIP messages and updates oracle aggregator
|
|
* @dev Implements CCIP message receiving and oracle update logic with validation
|
|
*/
|
|
contract CCIPReceiver {
|
|
using CCIPMessageValidator for IRouterClient.Any2EVMMessage;
|
|
|
|
IRouterClient public immutable router;
|
|
address public oracleAggregator;
|
|
address public admin;
|
|
|
|
mapping(bytes32 => bool) public processedMessages;
|
|
mapping(uint64 => uint256) public lastNonce; // Track nonces per source chain
|
|
|
|
event MessageReceived(
|
|
bytes32 indexed messageId,
|
|
uint64 indexed sourceChainSelector,
|
|
address sender,
|
|
bytes data
|
|
);
|
|
event OracleUpdated(uint256 answer, uint256 roundId);
|
|
event OracleAggregatorUpdated(address oldAggregator, address newAggregator);
|
|
|
|
modifier onlyAdmin() {
|
|
require(msg.sender == admin, "CCIPReceiver: only admin");
|
|
_;
|
|
}
|
|
|
|
modifier onlyRouter() {
|
|
require(msg.sender == address(router), "CCIPReceiver: only router");
|
|
_;
|
|
}
|
|
|
|
constructor(address _router, address _oracleAggregator) {
|
|
require(_router != address(0), "CCIPReceiver: zero router address");
|
|
require(_oracleAggregator != address(0), "CCIPReceiver: zero aggregator address");
|
|
|
|
router = IRouterClient(_router);
|
|
oracleAggregator = _oracleAggregator;
|
|
admin = msg.sender;
|
|
}
|
|
|
|
/**
|
|
* @notice Handle CCIP message (called by CCIP Router)
|
|
* @param message The received CCIP message
|
|
*/
|
|
function ccipReceive(
|
|
IRouterClient.Any2EVMMessage calldata message
|
|
) external onlyRouter {
|
|
// Replay protection: check if message already processed
|
|
require(!processedMessages[message.messageId], "CCIPReceiver: message already processed");
|
|
|
|
// Validate message format
|
|
require(
|
|
CCIPMessageValidator.validateMessageFormat(message),
|
|
"CCIPReceiver: invalid message format"
|
|
);
|
|
|
|
// Validate oracle data format
|
|
(
|
|
bool valid,
|
|
uint256 answer,
|
|
uint256 roundId,
|
|
uint256 timestamp
|
|
) = CCIPMessageValidator.validateOracleData(message.data);
|
|
|
|
require(valid, "CCIPReceiver: invalid oracle data");
|
|
|
|
// Mark message as processed (replay protection)
|
|
processedMessages[message.messageId] = true;
|
|
|
|
// Update last nonce for source chain (additional replay protection)
|
|
lastNonce[message.sourceChainSelector] = roundId;
|
|
|
|
// Update oracle aggregator
|
|
// Note: The aggregator's updateAnswer function can be called directly
|
|
// The aggregator will handle access control (onlyTransmitter)
|
|
// We need to ensure this receiver is added as a transmitter
|
|
try IAggregator(oracleAggregator).updateAnswer(answer) {
|
|
address sender = abi.decode(message.sender, (address));
|
|
emit MessageReceived(message.messageId, message.sourceChainSelector, sender, message.data);
|
|
emit OracleUpdated(answer, roundId);
|
|
} catch {
|
|
// If update fails, emit error event
|
|
// In production, consider adding error tracking
|
|
address sender = abi.decode(message.sender, (address));
|
|
emit MessageReceived(message.messageId, message.sourceChainSelector, sender, message.data);
|
|
// Don't emit OracleUpdated if update failed
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @notice Update oracle aggregator address
|
|
*/
|
|
function updateOracleAggregator(address newAggregator) external onlyAdmin {
|
|
require(newAggregator != address(0), "CCIPReceiver: zero address");
|
|
|
|
address oldAggregator = oracleAggregator;
|
|
oracleAggregator = newAggregator;
|
|
|
|
emit OracleAggregatorUpdated(oldAggregator, newAggregator);
|
|
}
|
|
|
|
/**
|
|
* @notice Change admin
|
|
*/
|
|
function changeAdmin(address newAdmin) external onlyAdmin {
|
|
require(newAdmin != address(0), "CCIPReceiver: zero address");
|
|
admin = newAdmin;
|
|
}
|
|
|
|
/**
|
|
* @notice Check if message has been processed
|
|
*/
|
|
function isMessageProcessed(bytes32 messageId) external view returns (bool) {
|
|
return processedMessages[messageId];
|
|
}
|
|
}
|