chore: sync submodule state (parent ref update)
Made-with: Cursor
This commit is contained in:
139
contracts/ccip-integration/CCIPTxReporter.sol
Normal file
139
contracts/ccip-integration/CCIPTxReporter.sol
Normal file
@@ -0,0 +1,139 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user