Add Oracle Aggregator and CCIP Integration
- 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.
This commit is contained in:
218
contracts/ccip/CCIPRouterOptimized.sol
Normal file
218
contracts/ccip/CCIPRouterOptimized.sol
Normal file
@@ -0,0 +1,218 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import "./IRouterClient.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
|
||||
/**
|
||||
* @title Optimized CCIP Router
|
||||
* @notice Optimized version with message batching and fee caching
|
||||
* @dev Performance optimizations for CCIP message handling
|
||||
*/
|
||||
contract CCIPRouterOptimized is IRouterClient {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
address public admin;
|
||||
uint256 public baseFee = 1 ether;
|
||||
uint256 public dataFeePerByte = 1000;
|
||||
uint256 public tokenFeePerToken = 1 ether;
|
||||
|
||||
mapping(uint64 => address[]) public supportedTokens;
|
||||
|
||||
// Fee caching
|
||||
mapping(bytes32 => uint256) public cachedFees;
|
||||
uint256 public cacheExpiry = 1 hours;
|
||||
mapping(bytes32 => uint256) public cacheTimestamp;
|
||||
|
||||
// Message batching
|
||||
struct BatchedMessage {
|
||||
bytes32[] messageIds;
|
||||
uint64 destinationChainSelector;
|
||||
uint256 totalFee;
|
||||
uint256 timestamp;
|
||||
}
|
||||
|
||||
mapping(uint256 => BatchedMessage) public batches;
|
||||
uint256 public batchId;
|
||||
uint256 public batchWindow = 5 minutes;
|
||||
uint256 public maxBatchSize = 100;
|
||||
|
||||
event RouterAdminChanged(address indexed oldAdmin, address indexed newAdmin);
|
||||
event BaseFeeUpdated(uint256 oldFee, uint256 newFee);
|
||||
event MessageBatched(uint256 indexed batchId, uint256 messageCount);
|
||||
event FeeCached(bytes32 indexed cacheKey, uint256 fee);
|
||||
|
||||
modifier onlyAdmin() {
|
||||
require(msg.sender == admin, "CCIPRouterOptimized: only admin");
|
||||
_;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
admin = msg.sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc IRouterClient
|
||||
*/
|
||||
function ccipSend(
|
||||
uint64 destinationChainSelector,
|
||||
EVM2AnyMessage memory message
|
||||
) external payable returns (bytes32 messageId, uint256 fees) {
|
||||
fees = getFee(destinationChainSelector, message);
|
||||
|
||||
// Handle fee payment
|
||||
if (fees > 0) {
|
||||
if (message.feeToken == address(0)) {
|
||||
// Native token (ETH) fees
|
||||
require(msg.value >= fees, "CCIPRouterOptimized: insufficient native token fee");
|
||||
} else {
|
||||
// ERC20 token fees
|
||||
IERC20(message.feeToken).safeTransferFrom(msg.sender, address(this), fees);
|
||||
}
|
||||
}
|
||||
|
||||
messageId = keccak256(abi.encodePacked(block.timestamp, block.number, message.data));
|
||||
|
||||
emit MessageSent(
|
||||
messageId,
|
||||
destinationChainSelector,
|
||||
msg.sender,
|
||||
message.receiver,
|
||||
message.data,
|
||||
message.tokenAmounts,
|
||||
message.feeToken,
|
||||
message.extraArgs
|
||||
);
|
||||
return (messageId, fees);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc IRouterClient
|
||||
*/
|
||||
function getFee(
|
||||
uint64 destinationChainSelector,
|
||||
EVM2AnyMessage memory message
|
||||
) public view override returns (uint256 fee) {
|
||||
// Check cache
|
||||
bytes32 cacheKey = keccak256(abi.encode(destinationChainSelector, message.receiver, message.data.length));
|
||||
if (cacheTimestamp[cacheKey] != 0 && block.timestamp < cacheTimestamp[cacheKey] + cacheExpiry) {
|
||||
return cachedFees[cacheKey];
|
||||
}
|
||||
|
||||
// Calculate fee
|
||||
fee = baseFee;
|
||||
fee += uint256(message.data.length) * dataFeePerByte;
|
||||
|
||||
for (uint256 i = 0; i < message.tokenAmounts.length; i++) {
|
||||
fee += message.tokenAmounts[i].amount * tokenFeePerToken;
|
||||
}
|
||||
|
||||
return fee;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Batch multiple messages
|
||||
*/
|
||||
function batchSend(
|
||||
uint64 destinationChainSelector,
|
||||
EVM2AnyMessage[] memory messages
|
||||
) external payable returns (uint256 batchId_, bytes32[] memory messageIds) {
|
||||
require(messages.length <= maxBatchSize, "CCIPRouterOptimized: batch too large");
|
||||
|
||||
batchId_ = batchId++;
|
||||
messageIds = new bytes32[](messages.length);
|
||||
uint256 totalFee = 0;
|
||||
|
||||
for (uint256 i = 0; i < messages.length; i++) {
|
||||
uint256 fee = getFee(destinationChainSelector, messages[i]);
|
||||
totalFee += fee;
|
||||
|
||||
bytes32 messageId = keccak256(abi.encodePacked(block.timestamp, block.number, i, messages[i].data));
|
||||
messageIds[i] = messageId;
|
||||
}
|
||||
|
||||
require(msg.value >= totalFee, "CCIPRouterOptimized: insufficient fee");
|
||||
|
||||
batches[batchId_] = BatchedMessage({
|
||||
messageIds: messageIds,
|
||||
destinationChainSelector: destinationChainSelector,
|
||||
totalFee: totalFee,
|
||||
timestamp: block.timestamp
|
||||
});
|
||||
|
||||
emit MessageBatched(batchId_, messages.length);
|
||||
return (batchId_, messageIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Cache fee calculation
|
||||
*/
|
||||
function cacheFee(
|
||||
uint64 destinationChainSelector,
|
||||
bytes memory receiver,
|
||||
uint256 dataLength
|
||||
) external returns (uint256 fee) {
|
||||
bytes32 cacheKey = keccak256(abi.encode(destinationChainSelector, receiver, dataLength));
|
||||
|
||||
// Calculate fee
|
||||
fee = baseFee + (dataLength * dataFeePerByte);
|
||||
|
||||
// Cache it
|
||||
cachedFees[cacheKey] = fee;
|
||||
cacheTimestamp[cacheKey] = block.timestamp;
|
||||
|
||||
emit FeeCached(cacheKey, fee);
|
||||
return fee;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc IRouterClient
|
||||
*/
|
||||
function getSupportedTokens(
|
||||
uint64 destinationChainSelector
|
||||
) external view override returns (address[] memory) {
|
||||
return supportedTokens[destinationChainSelector];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Update base fee
|
||||
*/
|
||||
function updateBaseFee(uint256 newFee) external onlyAdmin {
|
||||
require(newFee > 0, "CCIPRouterOptimized: fee must be greater than 0");
|
||||
emit BaseFeeUpdated(baseFee, newFee);
|
||||
baseFee = newFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Update cache expiry
|
||||
*/
|
||||
function setCacheExpiry(uint256 newExpiry) external onlyAdmin {
|
||||
cacheExpiry = newExpiry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Update batch window
|
||||
*/
|
||||
function setBatchWindow(uint256 newWindow) external onlyAdmin {
|
||||
batchWindow = newWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Update max batch size
|
||||
*/
|
||||
function setMaxBatchSize(uint256 newSize) external onlyAdmin {
|
||||
require(newSize > 0, "CCIPRouterOptimized: size must be greater than 0");
|
||||
maxBatchSize = newSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Change admin
|
||||
*/
|
||||
function changeAdmin(address newAdmin) external onlyAdmin {
|
||||
require(newAdmin != address(0), "CCIPRouterOptimized: zero address");
|
||||
emit RouterAdminChanged(admin, newAdmin);
|
||||
admin = newAdmin;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user