Copy
Ask AI
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.29;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IMessageRecipient } from "./interfaces/IMessageRecipient.sol";
import { IOracleTrigger } from "./interfaces/oracle/IOracleTrigger.sol";
import { TypeCasts } from "./libs/TypeCasts.sol";
import { IInterchainSecurityModule, ISpecifiesInterchainSecurityModule } from "./interfaces/IInterchainSecurityModule.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
using TypeCasts for address;
contract OracleRequestRecipient is
Ownable,
IMessageRecipient,
ISpecifiesInterchainSecurityModule,
ReentrancyGuard
{
IInterchainSecurityModule public interchainSecurityModule;
mapping(uint32 => mapping(bytes32 => bool)) public whitelistedSenders;
address private oracleTriggerAddress;
event ReceivedCall(address indexed caller, string key);
event WhitelistUpdated(
uint32 indexed origin,
bytes32 indexed sender,
bool status
);
event OracleTriggerUpdated(
address indexed oldAddress,
address indexed newAddress
);
event InterchainSecurityModuleUpdated(
address indexed previousISM,
address indexed newISM
);
event TokensRecovered(address indexed recipient, uint256 amount);
error EmptyOracleRequestData();
error OracleTriggerNotSet();
error SenderNotWhitelisted(bytes32 sender, uint32 origin);
error UnauthorizedCaller(address caller);
error InvalidSenderAddress();
error AlreadyWhitelisted(bytes32 sender, uint32 origin);
error InvalidISMAddress();
error InvalidOracleTriggerAddress();
error InvalidReceiver();
error NoBalanceToWithdraw();
error TransferFailed();
function handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _data
) external payable virtual override nonReentrant {
if (_data.length == 0) revert EmptyOracleRequestData();
if (oracleTriggerAddress == address(0)) revert OracleTriggerNotSet();
if (!whitelistedSenders[_origin][_sender])
revert SenderNotWhitelisted(_sender, _origin);
address sender = address(uint160(uint256(_sender)));
if (msg.sender != IOracleTrigger(oracleTriggerAddress).getMailBox()) {
revert UnauthorizedCaller(msg.sender);
}
string memory key = abi.decode(_data, (string));
emit ReceivedCall(sender, key);
IOracleTrigger(oracleTriggerAddress).dispatch{ value: msg.value }(
_origin,
sender,
key
);
}
function addToWhitelist(
uint32 _origin,
bytes32 _sender
) external onlyOwner {
if (_sender == bytes32(0)) {
revert InvalidSenderAddress();
}
if (whitelistedSenders[_origin][_sender]) {
revert AlreadyWhitelisted(_sender, _origin);
}
whitelistedSenders[_origin][_sender] = true;
emit WhitelistUpdated(_origin, _sender, true);
}
function removeFromWhitelist(
uint32 _origin,
bytes32 _sender
) external onlyOwner {
if (_sender == bytes32(0)) {
revert InvalidSenderAddress();
}
whitelistedSenders[_origin][_sender] = false;
emit WhitelistUpdated(_origin, _sender, false);
}
function setInterchainSecurityModule(address _ism) external onlyOwner {
if (_ism == address(0)) {
revert InvalidISMAddress();
}
emit InterchainSecurityModuleUpdated(
address(interchainSecurityModule),
_ism
);
interchainSecurityModule = IInterchainSecurityModule(_ism);
}
function setOracleTriggerAddress(
address _oracleTrigger
) external onlyOwner {
if (_oracleTrigger == address(0)) {
revert InvalidOracleTriggerAddress();
}
emit OracleTriggerUpdated(oracleTriggerAddress, _oracleTrigger);
oracleTriggerAddress = _oracleTrigger;
}
receive() external payable {}
function retrieveLostTokens(address receiver) external onlyOwner {
if (receiver == address(0)) {
revert InvalidReceiver();
}
uint256 balance = address(this).balance;
if (balance == 0) {
revert NoBalanceToWithdraw();
}
(bool success, ) = payable(receiver).call{ value: balance }("");
if (!success) {
revert TransferFailed();
}
emit TokensRecovered(receiver, balance);
}
function getOracleTriggerAddress() external view returns (address) {
return oracleTriggerAddress;
}
}