Files
smom-dbis-138/contracts/ccip-integration/CCIPTxReporter.sol
2026-03-02 12:14:09 -08:00

140 lines
4.5 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "../ccip/IRouterClient.sol";
/**
* @title CCIPTxReporter
* @notice Sends Chain-138 transaction reports to Ethereum Mainnet via Chainlink CCIP
* @dev Encodes batch data for CCIPLogger.ccipReceive
*/
contract CCIPTxReporter {
IRouterClient public immutable router;
uint64 public destChainSelector;
address public destReceiver;
address public owner;
event SingleTxReported(bytes32 indexed txHash, address from, address to, uint256 value);
event BatchReported(bytes32 indexed batchId, uint256 count);
modifier onlyOwner() {
require(msg.sender == owner, "CCIPTxReporter: only owner");
_;
}
constructor(
address _router,
uint64 _destChainSelector,
address _destReceiver
) {
require(_router != address(0), "CCIPTxReporter: zero router");
require(_destReceiver != address(0), "CCIPTxReporter: zero receiver");
router = IRouterClient(payable(_router));
destChainSelector = _destChainSelector;
destReceiver = _destReceiver;
owner = msg.sender;
}
/**
* @notice Report a single transaction to the CCIPLogger on destination chain
*/
function reportTx(
bytes32 txHash,
address from,
address to,
uint256 value,
bytes calldata extraData
) external payable returns (bytes32 messageId) {
bytes32[] memory txHashes = new bytes32[](1);
address[] memory froms = new address[](1);
address[] memory tos = new address[](1);
uint256[] memory values = new uint256[](1);
txHashes[0] = txHash;
froms[0] = from;
tos[0] = to;
values[0] = value;
messageId = _reportBatch(txHash, txHashes, froms, tos, values, extraData);
emit SingleTxReported(txHash, from, to, value);
}
/**
* @notice Report a batch of transactions to the CCIPLogger on destination chain
*/
function reportBatch(
bytes32 batchId,
bytes32[] calldata txHashes,
address[] calldata froms,
address[] calldata tos,
uint256[] calldata values,
bytes calldata extraData
) external payable returns (bytes32 messageId) {
messageId = _reportBatch(batchId, txHashes, froms, tos, values, extraData);
emit BatchReported(batchId, txHashes.length);
}
function _reportBatch(
bytes32 batchId,
bytes32[] memory txHashes,
address[] memory froms,
address[] memory tos,
uint256[] memory values,
bytes memory extraData
) internal returns (bytes32 messageId) {
bytes memory data = abi.encode(
batchId,
txHashes,
froms,
tos,
values,
extraData
);
IRouterClient.EVM2AnyMessage memory message = IRouterClient.EVM2AnyMessage({
receiver: abi.encode(destReceiver),
data: data,
tokenAmounts: new IRouterClient.TokenAmount[](0),
feeToken: address(0),
extraArgs: ""
});
(messageId, ) = router.ccipSend{value: msg.value}(destChainSelector, message);
}
/**
* @notice Estimate fee for sending a batch
*/
function estimateFee(
bytes32[] calldata txHashes,
address[] calldata froms,
address[] calldata tos,
uint256[] calldata values
) external view returns (uint256 fee) {
// Use block.difficulty for London EVM compatibility (prevrandao is Paris+)
bytes32 batchId = keccak256(abi.encodePacked(block.timestamp, block.difficulty, txHashes.length));
bytes memory data = abi.encode(
batchId,
txHashes,
froms,
tos,
values,
bytes("")
);
IRouterClient.EVM2AnyMessage memory message = IRouterClient.EVM2AnyMessage({
receiver: abi.encode(destReceiver),
data: data,
tokenAmounts: new IRouterClient.TokenAmount[](0),
feeToken: address(0),
extraArgs: ""
});
return router.getFee(destChainSelector, message);
}
function setDestReceiver(address _destReceiver) external onlyOwner {
require(_destReceiver != address(0), "CCIPTxReporter: zero address");
destReceiver = _destReceiver;
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "CCIPTxReporter: zero address");
owner = newOwner;
}
}