// 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;
}
}
Smart Contracts
OracleRequestRecipient.sol
The destination-chain entry point for Pull-based (request) Lumina oracles: it validates on-demand price requests and forwards them to the OracleTrigger for fulfilment from Lasernet. Use it when your dApp requests prices on demand instead of receiving scheduled pushes.
Was this page helpful?