123 lines
3.7 KiB
Solidity
123 lines
3.7 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import "../ccip/IRouterClient.sol";
|
|
|
|
/**
|
|
* @title CCIPLogger
|
|
* @notice Receives and logs Chain-138 transactions via Chainlink CCIP
|
|
* @dev Implements replay protection via batch ID tracking; optional authorized signer for future use
|
|
*/
|
|
contract CCIPLogger {
|
|
IRouterClient public immutable router;
|
|
address public authorizedSigner;
|
|
uint64 public expectedSourceChainSelector;
|
|
address public owner;
|
|
|
|
mapping(bytes32 => bool) public processedBatches;
|
|
|
|
event RemoteBatchLogged(
|
|
bytes32 indexed messageId,
|
|
bytes32 indexed batchId,
|
|
uint64 sourceChainSelector,
|
|
address sender,
|
|
bytes32[] txHashes,
|
|
address[] froms,
|
|
address[] tos,
|
|
uint256[] values
|
|
);
|
|
event AuthorizedSignerUpdated(address oldSigner, address newSigner);
|
|
event SourceChainSelectorUpdated(uint64 oldSelector, uint64 newSelector);
|
|
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
|
|
|
modifier onlyRouter() {
|
|
require(msg.sender == address(router), "CCIPLogger: only router");
|
|
_;
|
|
}
|
|
|
|
modifier onlyOwner() {
|
|
require(msg.sender == owner, "CCIPLogger: only owner");
|
|
_;
|
|
}
|
|
|
|
constructor(
|
|
address _router,
|
|
address _authorizedSigner,
|
|
uint64 _expectedSourceChainSelector
|
|
) {
|
|
require(_router != address(0), "CCIPLogger: zero router");
|
|
router = IRouterClient(_router);
|
|
authorizedSigner = _authorizedSigner;
|
|
expectedSourceChainSelector = _expectedSourceChainSelector;
|
|
owner = msg.sender;
|
|
}
|
|
|
|
/**
|
|
* @notice Handle CCIP message (called by CCIP Router)
|
|
* @param message The received CCIP message
|
|
*/
|
|
function ccipReceive(
|
|
IRouterClient.Any2EVMMessage calldata message
|
|
) external onlyRouter {
|
|
require(
|
|
message.sourceChainSelector == expectedSourceChainSelector,
|
|
"CCIPLogger: invalid source chain"
|
|
);
|
|
|
|
(
|
|
bytes32 batchId,
|
|
bytes32[] memory txHashes,
|
|
address[] memory froms,
|
|
address[] memory tos,
|
|
uint256[] memory values,
|
|
bytes memory _extra
|
|
) = abi.decode(
|
|
message.data,
|
|
(bytes32, bytes32[], address[], address[], uint256[], bytes)
|
|
);
|
|
|
|
require(!processedBatches[batchId], "CCIPLogger: batch already processed");
|
|
processedBatches[batchId] = true;
|
|
|
|
address sender = message.sender.length >= 20
|
|
? address(bytes20(message.sender))
|
|
: address(0);
|
|
if (message.sender.length == 32) {
|
|
sender = address(bytes20(message.sender));
|
|
}
|
|
|
|
emit RemoteBatchLogged(
|
|
message.messageId,
|
|
batchId,
|
|
message.sourceChainSelector,
|
|
sender,
|
|
txHashes,
|
|
froms,
|
|
tos,
|
|
values
|
|
);
|
|
}
|
|
|
|
function getRouter() external view returns (address) {
|
|
return address(router);
|
|
}
|
|
|
|
function setAuthorizedSigner(address _signer) external onlyOwner {
|
|
address old = authorizedSigner;
|
|
authorizedSigner = _signer;
|
|
emit AuthorizedSignerUpdated(old, _signer);
|
|
}
|
|
|
|
function setExpectedSourceChainSelector(uint64 _selector) external onlyOwner {
|
|
uint64 old = expectedSourceChainSelector;
|
|
expectedSourceChainSelector = _selector;
|
|
emit SourceChainSelectorUpdated(old, _selector);
|
|
}
|
|
|
|
function transferOwnership(address newOwner) external onlyOwner {
|
|
require(newOwner != address(0), "CCIPLogger: zero address");
|
|
emit OwnershipTransferred(owner, newOwner);
|
|
owner = newOwner;
|
|
}
|
|
}
|