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