Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { IWorker } from "./IWorker.sol";
import { ILayerZeroExecutor } from "./ILayerZeroExecutor.sol";
import { ILayerZeroReadExecutor } from "./ILayerZeroReadExecutor.sol";
interface IExecutor is IWorker, ILayerZeroExecutor, ILayerZeroReadExecutor {
struct DstConfigParam {
uint32 dstEid;
uint64 lzReceiveBaseGas;
uint64 lzComposeBaseGas;
uint16 multiplierBps;
uint128 floorMarginUSD;
uint128 nativeCap;
}
struct DstConfig {
uint64 lzReceiveBaseGas;
uint16 multiplierBps;
uint128 floorMarginUSD; // uses priceFeed PRICE_RATIO_DENOMINATOR
uint128 nativeCap;
uint64 lzComposeBaseGas;
}
struct ExecutionParams {
address receiver;
Origin origin;
bytes32 guid;
bytes message;
bytes extraData;
uint256 gasLimit;
}
struct NativeDropParams {
address receiver;
uint256 amount;
}
event DstConfigSet(DstConfigParam[] params);
event NativeDropApplied(Origin origin, uint32 dstEid, address oapp, NativeDropParams[] params, bool[] success);
function dstConfig(uint32 _dstEid) external view returns (uint64, uint16, uint128, uint128, uint64);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IExecutor } from "./IExecutor.sol";
interface IExecutorFeeLib {
struct FeeParams {
address priceFeed;
uint32 dstEid;
address sender;
uint256 calldataSize;
uint16 defaultMultiplierBps;
}
struct FeeParamsForRead {
address priceFeed;
address sender;
uint16 defaultMultiplierBps;
}
error Executor_NoOptions();
error Executor_NativeAmountExceedsCap(uint256 amount, uint256 cap);
error Executor_UnsupportedOptionType(uint8 optionType);
error Executor_InvalidExecutorOptions(uint256 cursor);
error Executor_ZeroLzReceiveGasProvided();
error Executor_ZeroLzComposeGasProvided();
error Executor_ZeroCalldataSizeProvided();
error Executor_EidNotSupported(uint32 eid);
function getFeeOnSend(
FeeParams calldata _params,
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external returns (uint256 fee);
function getFee(
FeeParams calldata _params,
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external view returns (uint256 fee);
function getFeeOnSend(
FeeParamsForRead calldata _params,
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external returns (uint256 fee);
function getFee(
FeeParamsForRead calldata _params,
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external view returns (uint256 fee);
function version() external view returns (uint64 major, uint8 minor);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface ILayerZeroExecutor {
// @notice query price and assign jobs at the same time
// @param _dstEid - the destination endpoint identifier
// @param _sender - the source sending contract address. executors may apply price discrimination to senders
// @param _calldataSize - dynamic data size of message + caller params
// @param _options - optional parameters for extra service plugins, e.g. sending dust tokens at the destination chain
function assignJob(
uint32 _dstEid,
address _sender,
uint256 _calldataSize,
bytes calldata _options
) external returns (uint256 price);
// @notice query the executor price for relaying the payload and its proof to the destination chain
// @param _dstEid - the destination endpoint identifier
// @param _sender - the source sending contract address. executors may apply price discrimination to senders
// @param _calldataSize - dynamic data size of message + caller params
// @param _options - optional parameters for extra service plugins, e.g. sending dust tokens at the destination chain
function getFee(
uint32 _dstEid,
address _sender,
uint256 _calldataSize,
bytes calldata _options
) external view returns (uint256 price);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface ILayerZeroReadExecutor {
// @notice query price and assign jobs at the same time
// @param _sender - the source sending contract address. executors may apply price discrimination to senders
// @param _options - optional parameters for extra service plugins, e.g. sending dust tokens at the destination chain
function assignJob(address _sender, bytes calldata _options) external returns (uint256 fee);
// @notice query the executor price for executing the payload on this chain
// @param _sender - the source sending contract address. executors may apply price discrimination to senders
// @param _options - optional parameters for extra service plugins, e.g. sending dust tokens
function getFee(address _sender, bytes calldata _options) external view returns (uint256 fee);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IWorker {
event SetWorkerLib(address workerLib);
event SetPriceFeed(address priceFeed);
event SetDefaultMultiplierBps(uint16 multiplierBps);
event SetSupportedOptionTypes(uint32 dstEid, uint8[] optionTypes);
event Withdraw(address lib, address to, uint256 amount);
error Worker_NotAllowed();
error Worker_OnlyMessageLib();
error Worker_RoleRenouncingDisabled();
function setPriceFeed(address _priceFeed) external;
function priceFeed() external view returns (address);
function setDefaultMultiplierBps(uint16 _multiplierBps) external;
function defaultMultiplierBps() external view returns (uint16);
function withdrawFee(address _lib, address _to, uint256 _amount) external;
function setSupportedOptionTypes(uint32 _eid, uint8[] calldata _optionTypes) external;
function getSupportedOptionTypes(uint32 _eid) external view returns (uint8[] memory);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @dev should be implemented by the ReceiveUln302 contract and future ReceiveUln contracts on EndpointV2
interface IReceiveUlnE2 {
/// @notice for each dvn to verify the payload
/// @dev this function signature 0x0223536e
function verify(bytes calldata _packetHeader, bytes32 _payloadHash, uint64 _confirmations) external;
/// @notice verify the payload at endpoint, will check if all DVNs verified
function commitVerification(bytes calldata _packetHeader, bytes32 _payloadHash) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IMessageLibManager } from "./IMessageLibManager.sol";
import { IMessagingComposer } from "./IMessagingComposer.sol";
import { IMessagingChannel } from "./IMessagingChannel.sol";
import { IMessagingContext } from "./IMessagingContext.sol";
struct MessagingParams {
uint32 dstEid;
bytes32 receiver;
bytes message;
bytes options;
bool payInLzToken;
}
struct MessagingReceipt {
bytes32 guid;
uint64 nonce;
MessagingFee fee;
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
struct Origin {
uint32 srcEid;
bytes32 sender;
uint64 nonce;
}
interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);
event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);
event PacketDelivered(Origin origin, address receiver);
event LzReceiveAlert(
address indexed receiver,
address indexed executor,
Origin origin,
bytes32 guid,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
event LzTokenSet(address token);
event DelegateSet(address sender, address delegate);
function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);
function send(
MessagingParams calldata _params,
address _refundAddress
) external payable returns (MessagingReceipt memory);
function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;
function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);
function initializable(Origin calldata _origin, address _receiver) external view returns (bool);
function lzReceive(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
bytes calldata _message,
bytes calldata _extraData
) external payable;
// oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;
function setLzToken(address _lzToken) external;
function lzToken() external view returns (address);
function nativeToken() external view returns (address);
function setDelegate(address _delegate) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { SetConfigParam } from "./IMessageLibManager.sol";
enum MessageLibType {
Send,
Receive,
SendAndReceive
}
interface IMessageLib is IERC165 {
function setConfig(address _oapp, SetConfigParam[] calldata _config) external;
function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);
function isSupportedEid(uint32 _eid) external view returns (bool);
// message libs of same major version are compatible
function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);
function messageLibType() external view returns (MessageLibType);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
struct SetConfigParam {
uint32 eid;
uint32 configType;
bytes config;
}
interface IMessageLibManager {
struct Timeout {
address lib;
uint256 expiry;
}
event LibraryRegistered(address newLib);
event DefaultSendLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
event SendLibrarySet(address sender, uint32 eid, address newLib);
event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);
function registerLibrary(address _lib) external;
function isRegisteredLibrary(address _lib) external view returns (bool);
function getRegisteredLibraries() external view returns (address[] memory);
function setDefaultSendLibrary(uint32 _eid, address _newLib) external;
function defaultSendLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function defaultReceiveLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;
function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);
function isSupportedEid(uint32 _eid) external view returns (bool);
function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);
/// ------------------- OApp interfaces -------------------
function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);
function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);
function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);
function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;
function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);
function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;
function getConfig(
address _oapp,
address _lib,
uint32 _eid,
uint32 _configType
) external view returns (bytes memory config);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingChannel {
event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
function eid() external view returns (uint32);
// this is an emergency function if a message cannot be verified for some reasons
// required to provide _nextNonce to avoid race condition
function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;
function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);
function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);
function inboundPayloadHash(
address _receiver,
uint32 _srcEid,
bytes32 _sender,
uint64 _nonce
) external view returns (bytes32);
function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingComposer {
event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
event LzComposeAlert(
address indexed from,
address indexed to,
address indexed executor,
bytes32 guid,
uint16 index,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
function composeQueue(
address _from,
address _to,
bytes32 _guid,
uint16 _index
) external view returns (bytes32 messageHash);
function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;
function lzCompose(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData
) external payable;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingContext {
function isSendingMessage() external view returns (bool);
function getSendContext() external view returns (uint32 dstEid, address sender);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { MessagingFee } from "./ILayerZeroEndpointV2.sol";
import { IMessageLib } from "./IMessageLib.sol";
struct Packet {
uint64 nonce;
uint32 srcEid;
address sender;
uint32 dstEid;
bytes32 receiver;
bytes32 guid;
bytes message;
}
interface ISendLib is IMessageLib {
function send(
Packet calldata _packet,
bytes calldata _options,
bool _payInLzToken
) external returns (MessagingFee memory, bytes memory encodedPacket);
function quote(
Packet calldata _packet,
bytes calldata _options,
bool _payInLzToken
) external view returns (MessagingFee memory);
function setTreasury(address _treasury) external;
function withdrawFee(address _to, uint256 _amount) external;
function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
library AddressCast {
error AddressCast_InvalidSizeForAddress();
error AddressCast_InvalidAddress();
function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {
if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();
result = bytes32(_addressBytes);
unchecked {
uint256 offset = 32 - _addressBytes.length;
result = result >> (offset * 8);
}
}
function toBytes32(address _address) internal pure returns (bytes32 result) {
result = bytes32(uint256(uint160(_address)));
}
function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {
if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();
result = new bytes(_size);
unchecked {
uint256 offset = 256 - _size * 8;
assembly {
mstore(add(result, 32), shl(offset, _addressBytes32))
}
}
}
function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {
result = address(uint160(uint256(_addressBytes32)));
}
function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {
if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();
result = address(bytes20(_addressBytes));
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
library Transfer {
using SafeERC20 for IERC20;
address internal constant ADDRESS_ZERO = address(0);
error Transfer_NativeFailed(address _to, uint256 _value);
error Transfer_ToAddressIsZero();
function native(address _to, uint256 _value) internal {
if (_to == ADDRESS_ZERO) revert Transfer_ToAddressIsZero();
(bool success, ) = _to.call{ value: _value }("");
if (!success) revert Transfer_NativeFailed(_to, _value);
}
function token(address _token, address _to, uint256 _value) internal {
if (_to == ADDRESS_ZERO) revert Transfer_ToAddressIsZero();
IERC20(_token).safeTransfer(_to, _value);
}
function nativeOrToken(address _token, address _to, uint256 _value) internal {
if (_token == ADDRESS_ZERO) {
native(_to, _value);
} else {
token(_token, _to, _value);
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
import { Packet } from "../../interfaces/ISendLib.sol";
import { AddressCast } from "../../libs/AddressCast.sol";
library PacketV1Codec {
using AddressCast for address;
using AddressCast for bytes32;
uint8 internal constant PACKET_VERSION = 1;
// header (version + nonce + path)
// version
uint256 private constant PACKET_VERSION_OFFSET = 0;
// nonce
uint256 private constant NONCE_OFFSET = 1;
// path
uint256 private constant SRC_EID_OFFSET = 9;
uint256 private constant SENDER_OFFSET = 13;
uint256 private constant DST_EID_OFFSET = 45;
uint256 private constant RECEIVER_OFFSET = 49;
// payload (guid + message)
uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)
uint256 private constant MESSAGE_OFFSET = 113;
function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {
encodedPacket = abi.encodePacked(
PACKET_VERSION,
_packet.nonce,
_packet.srcEid,
_packet.sender.toBytes32(),
_packet.dstEid,
_packet.receiver,
_packet.guid,
_packet.message
);
}
function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {
return
abi.encodePacked(
PACKET_VERSION,
_packet.nonce,
_packet.srcEid,
_packet.sender.toBytes32(),
_packet.dstEid,
_packet.receiver
);
}
function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {
return abi.encodePacked(_packet.guid, _packet.message);
}
function header(bytes calldata _packet) internal pure returns (bytes calldata) {
return _packet[0:GUID_OFFSET];
}
function version(bytes calldata _packet) internal pure returns (uint8) {
return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));
}
function nonce(bytes calldata _packet) internal pure returns (uint64) {
return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));
}
function srcEid(bytes calldata _packet) internal pure returns (uint32) {
return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));
}
function sender(bytes calldata _packet) internal pure returns (bytes32) {
return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);
}
function senderAddressB20(bytes calldata _packet) internal pure returns (address) {
return sender(_packet).toAddress();
}
function dstEid(bytes calldata _packet) internal pure returns (uint32) {
return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));
}
function receiver(bytes calldata _packet) internal pure returns (bytes32) {
return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);
}
function receiverB20(bytes calldata _packet) internal pure returns (address) {
return receiver(_packet).toAddress();
}
function guid(bytes calldata _packet) internal pure returns (bytes32) {
return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);
}
function message(bytes calldata _packet) internal pure returns (bytes calldata) {
return bytes(_packet[MESSAGE_OFFSET:]);
}
function payload(bytes calldata _packet) internal pure returns (bytes calldata) {
return bytes(_packet[GUID_OFFSET:]);
}
function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {
return keccak256(payload(_packet));
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {IERC165, ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/IAccessControl.sol)
pragma solidity >=0.8.4;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted to signal this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol"; <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol"; <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-FileCopyrightText: 2025 Molecula <info@molecula.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
import {ConstantsCoreV2} from "./../coreV2/Constants.sol";
/// @title ValueValidator.
/// @notice Contract for validating common value-based conditions.
/// @dev Provides modifiers to check for zero values, zero addresses, and `msg.value` conditions.
contract ValueValidator {
/// @dev Error: `msg.sender` is not authorized for this function.
error ENotAuthorized();
/// @dev Error thrown when a value is zero but must be non-zero.
error EZeroValue();
/// @dev Error thrown when an address is zero but must be non-zero.
error EZeroAddress();
/// @dev Error thrown when `msg.value` is not zero but must be zero.
error EMsgValueIsNotZero();
/// @dev Error: Provided array is empty.
error EEmptyArray();
/// @dev Error: Percentage is invalid.
error EInvalidPercentage();
/**
* @dev Ensures the function is called by the expected sender.
* @param expectedSender Address that is allowed to call the function.
* @custom:revert ENotAuthorized Check if the caller is not the expected sender.
*/
modifier only(address expectedSender) {
if (msg.sender != expectedSender) {
revert ENotAuthorized();
}
_;
}
/// @dev Modifier that checks if a value is not zero.
/// @param value Value to check.
modifier notZero(uint256 value) {
// slither-disable-next-line incorrect-equality
if (value == 0) {
revert EZeroValue();
}
_;
}
/// @dev Modifier that checks if an address is not zero.
/// @param addr Address to check.
modifier notZeroAddress(address addr) {
if (addr == address(0)) {
revert EZeroAddress();
}
_;
}
/// @dev Modifier that ensures `msg.value` is zero.
modifier zeroMsgValue() {
if (msg.value != 0) {
revert EMsgValueIsNotZero();
}
_;
}
/// @dev Modifier that checks if the array is empty.
/// @param arrayLength Array length to check.
modifier notEmpty(uint256 arrayLength) {
if (arrayLength == 0) {
revert EEmptyArray();
}
_;
}
/// @dev Modifier that validates if the basis points value is within the valid range (0-10000).
/// @param bps Basis points value to validate.
/// @custom:revert EInvalidPercentage Check if bps exceeds the maximum allowed value (10000).
modifier checkBPS(uint16 bps) {
if (bps > ConstantsCoreV2.PERCENTAGE_FACTOR) {
revert EInvalidPercentage();
}
_;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-FileCopyrightText: 2025 Molecula <info@molecula.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
/// @title ConstantsCoreV2.
/// @notice Library containing core constants used across the protocol.
/// @dev Contains immutable values that are frequently used in other contracts.
library ConstantsCoreV2 {
/// @notice Special address used to represent the native token (e.g., ETH) according to EIP-7528:
/// https://eips.ethereum.org/EIPS/eip-7528
/// @dev This pseudo-address is commonly used to differentiate between ERC20 tokens and the native tokens.
address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev Basis points factor, where 100% = 10,000.
/// Used as a denominator in percentage calculations.
uint16 public constant PERCENTAGE_FACTOR = 10_000;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: LZBL-1.2
// copied and modified from https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/packages/layerzero-v2/evm/messagelib/contracts/Executor.sol
pragma solidity ^0.8.24;
import {IExecutor} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/interfaces/IExecutor.sol";
import {IExecutorFeeLib} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/interfaces/IExecutorFeeLib.sol";
import {IReceiveUlnE2} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/interfaces/IReceiveUlnE2.sol";
import {Origin, ILayerZeroEndpointV2} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import {PacketV1Codec} from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {ILayerZeroEndpointV2Extended} from "./interfaces/ILayerZeroEndpointV2Extended.sol";
import {IReceiveUlnView, VerificationState, ExecutionState} from "./interfaces/ILayerZeroReceiveUlnView.sol";
import {Worker} from "./Worker.sol";
/// @title Executor Contract.
/// @notice Executes LayerZero V2 operations and handles the job fee logic for messaging libraries.
/// @dev Extends `Worker`, `ReentrancyGuard`, and implements `IExecutor`.
contract Executor is Worker, ReentrancyGuard, IExecutor {
using PacketV1Codec for bytes;
/// @dev Represents an empty payload hash.
bytes32 public constant EMPTY_PAYLOAD_HASH = bytes32(0);
/// @dev Represents a nil (max value) payload hash.
bytes32 public constant NIL_PAYLOAD_HASH = bytes32(type(uint256).max);
/// @dev Configuration per destination endpoint ID.
mapping(uint32 dstEid => DstConfig) public dstConfig;
/// @dev LayerZero V2 endpoint address.
address public immutable ENDPOINT;
/// @dev Local endpoint ID for LayerZero V2.
uint32 public immutable LOCAL_EID_V2;
/// @dev LayerZero V2 ReceiveUln302 contract address.
address public immutable RECEIVE_ULN302;
/// @dev Error thrown when `commitVerification` is called with an invalid state (i.e. Packet not verifiable).
error Executor_InvalidVerificationState();
/**
* @dev Initializes the Executor contract with endpoint, ULN302, admin, and libraries.
* @param _endpoint Address of LayerZeroV2 endpoint contract.
* @param _receiveUln302 LayerZeroV2 RecieveUln302 contract address to commit verifications.
* @param _messageLibs Addresses of messaging fee libraries allowed for job assignment.
* @param _priceFeed Address of price feed contract used for fee calculations.
* @param _roleAdmin Address assigned `DEFAULT_ADMIN_ROLE`.
* @param _admins Additional addresses to be granted `ADMIN_ROLE`.
*/
constructor(
address _endpoint,
address _receiveUln302,
address[] memory _messageLibs,
address _priceFeed,
address _roleAdmin,
address[] memory _admins
)
Worker(_messageLibs, _priceFeed, 12000, _roleAdmin, _admins)
notZeroAddress(_endpoint)
notZeroAddress(_receiveUln302)
{
ENDPOINT = _endpoint;
LOCAL_EID_V2 = ILayerZeroEndpointV2Extended(_endpoint).eid();
RECEIVE_ULN302 = _receiveUln302;
}
/**
* @dev Sets configuration parameters for multiple destination EIDs.
* This function allows `ADMIN_ROLE` to set various parameters for each destination EID.
* It updates the `dstConfig` mapping with new values for each destination EID.
* @param _params Array of the `DstConfigParam` structs containing destination settings.
* Emits a `DstConfigSet` event with the updated parameters.
*/
function setDstConfig(DstConfigParam[] calldata _params) external onlyRole(_ADMIN_ROLE) {
uint256 paramsLength = _params.length;
for (uint256 i = 0; i < paramsLength; ++i) {
// Write each destination config into the mapping, overwriting any previous value.
DstConfigParam memory param = _params[i];
dstConfig[param.dstEid] = DstConfig(
param.lzReceiveBaseGas,
param.multiplierBps,
param.floorMarginUSD,
param.nativeCap,
param.lzComposeBaseGas
);
}
emit DstConfigSet(_params);
}
/**
* @dev Sends native tokens to specified addresses on the target chain.
* It can only be called by an address with `ADMIN_ROLE` and is protected against reentrancy attacks.
* @param _origin Origin information for the message context.
* @param _dstEid Destination endpoint EID where native tokens should be dropped.
* @param _oapp Address of the target OApp contract on the destination chain.
* @param _nativeDropParams Array of `NativeDropParams` specifying receivers and amounts.
* @param _nativeDropGasLimit Gas limit for each native transfer call.
*/
function nativeDrop(
Origin calldata _origin,
uint32 _dstEid,
address _oapp,
NativeDropParams[] calldata _nativeDropParams,
uint256 _nativeDropGasLimit
) external payable onlyRole(_ADMIN_ROLE) nonReentrant {
// Internal call ensures an event is emitted and the total amount spent is tracked.
_nativeDrop(_origin, _dstEid, _oapp, _nativeDropParams, _nativeDropGasLimit);
}
/**
* @dev Commits a packet verification to the `ReceiveUln302` contract.
* It checks the verification state of the packet header and payload hash.
* If the packet is in a verifiable state, it commits the verification.
* If the packet is in a verifying state, it reverts with a `LzExecutor_Verifying` error.
* @param _packetHeader Encoded packet header bytes.
* @param _payloadHash Keccak256 hash of the packet payload.
*/
function commitVerification(
bytes calldata _packetHeader,
bytes32 _payloadHash
) external nonReentrant {
VerificationState verificationState = verifiable(_packetHeader, _payloadHash);
if (verificationState == VerificationState.Verifiable) {
// Verification passed, commit to the verifier contract.
IReceiveUlnE2(RECEIVE_ULN302).commitVerification(_packetHeader, _payloadHash);
} else {
// Revert if a Packet is still verifying or not verifiable.
revert Executor_InvalidVerificationState();
}
}
/**
* @notice Executes LayerZero V2 receive logic (`lzReceive`).
* @dev This function is used to execute messages received from LayerZero V2.
* It wraps the call in a try/catch block to handle any errors that may occur.
* @param _executionParams Struct containing `origin`, `receiver`, `guid`, `message`, `extraData`, and `gasLimit`.
*/
function execute302(ExecutionParams calldata _executionParams) external payable nonReentrant {
try
ILayerZeroEndpointV2Extended(ENDPOINT).lzReceive{
value: msg.value,
gas: _executionParams.gasLimit
}(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.message,
_executionParams.extraData
)
// solhint-disable-next-line no-empty-blocks
{
// Do nothing.
} catch (bytes memory reason) {
// On error, call the alert method on the endpoint for logging.
ILayerZeroEndpointV2Extended(ENDPOINT).lzReceiveAlert(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.gasLimit,
msg.value,
_executionParams.message,
_executionParams.extraData,
reason
);
}
}
/**
* @notice Composes LayerZero V2 packets (lzCompose).
* @dev This function is used to compose messages for LayerZero V2.
* It wraps the call in a try/catch block to handle any errors that may occur.
* @param _from Address from which the message originates.
* @param _to Address to which the message is destined.
* @param _guid Unique GUID for this message.
* @param _index Sequence index for multi-packet messages.
* @param _message Payload to send.
* @param _extraData Additional data for the endpoint.
* @param _gasLimit Gas limit provided for lzCompose.
*/
function compose302(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData,
uint256 _gasLimit
) external payable nonReentrant {
try
ILayerZeroEndpointV2Extended(ENDPOINT).lzCompose{value: msg.value, gas: _gasLimit}(
_from,
_to,
_guid,
_index,
_message,
_extraData
)
// solhint-disable-next-line no-empty-blocks
{
// Do nothing.
} catch (bytes memory reason) {
ILayerZeroEndpointV2Extended(ENDPOINT).lzComposeAlert(
_from,
_to,
_guid,
_index,
_gasLimit,
msg.value,
_message,
_extraData,
reason
);
}
}
/**
* @dev Executes a native drop then LayerZero V2 receive logic in a single transaction.
* This function allows for a native drop to be performed before executing the LayerZero V2 receive logic.
* It combines the native drop and lzReceive into a single transaction to save on gas
* and ensure atomicity of the operations.
* This function can only be called by an address with ADMIN_ROLE and is protected against reentrancy attacks.
* @param _nativeDropParams Array of `NativeDropParams` for native drop.
* @param _nativeDropGasLimit Gas limit for each native transfer.
* @param _executionParams Struct containing parameters for `lzReceive`.
*/
function nativeDropAndExecute302(
NativeDropParams[] calldata _nativeDropParams,
uint256 _nativeDropGasLimit,
ExecutionParams calldata _executionParams
) external payable onlyRole(_ADMIN_ROLE) nonReentrant {
// Spend as much of `msg.value` as needed for drops; leftover is used for receive.
uint256 spent = _nativeDrop(
_executionParams.origin,
LOCAL_EID_V2,
_executionParams.receiver,
_nativeDropParams,
_nativeDropGasLimit
);
// Only remaining value is used for the receive call.
uint256 value = msg.value - spent;
try
ILayerZeroEndpointV2Extended(ENDPOINT).lzReceive{
value: value,
gas: _executionParams.gasLimit
}(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.message,
_executionParams.extraData
)
// solhint-disable-next-line no-empty-blocks
{
// Do nothing.
} catch (bytes memory reason) {
// If the receive fails, report for monitoring.
ILayerZeroEndpointV2Extended(ENDPOINT).lzReceiveAlert(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.gasLimit,
value,
_executionParams.message,
_executionParams.extraData,
reason
);
}
}
// --- Message Lib ---
/**
* @dev Assigns a job to the Executor and calculates the fee. Used by message libraries.
* @param _dstEid Destination EID.
* @param _sender Address sending the job.
* @param _calldataSize Size of calldata in bytes.
* @param _options Additional options as bytes.
* @return fee Fee calculated for the job.
*/
function assignJob(
uint32 _dstEid,
address _sender,
uint256 _calldataSize,
bytes calldata _options
) external onlyRole(_MESSAGE_LIB_ROLE) onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
// Construct fee parameters for the fee library call.
IExecutorFeeLib.FeeParams memory params = IExecutorFeeLib.FeeParams(
priceFeed,
_dstEid,
_sender,
_calldataSize,
defaultMultiplierBps
);
// Call external fee library (pluggable) for fee computation.
fee = IExecutorFeeLib(workerFeeLib).getFeeOnSend(params, dstConfig[_dstEid], _options);
}
/**
* @dev Assigns a job for CmdLib (`destination = localEidV2`).
* @param _sender Address sending the job.
* @param _options Additional options as bytes.
* @return fee Fee calculated for the job.
*/
function assignJob(
address _sender,
bytes calldata _options
) external onlyRole(_MESSAGE_LIB_ROLE) onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
// Construct fee parameters for the fee library call.
IExecutorFeeLib.FeeParamsForRead memory params = IExecutorFeeLib.FeeParamsForRead(
priceFeed,
_sender,
defaultMultiplierBps
);
// Call external fee library (pluggable) for fee computation.
fee = IExecutorFeeLib(workerFeeLib).getFeeOnSend(params, dstConfig[LOCAL_EID_V2], _options);
}
/**
* @dev Returns the current execution state for a packet.
* @param _packetHeader Encoded packet header.
* @param _payloadHash Hash of the packet payload.
* @return ExecutionState Current execution state of the packet.
*/
function executable(
bytes calldata _packetHeader,
bytes32 _payloadHash
) public view returns (ExecutionState) {
// Decode the receiver and origin details from the header.
address _receiver = _packetHeader.receiverB20();
Origin memory _origin = Origin(
_packetHeader.srcEid(),
_packetHeader.sender(),
_packetHeader.nonce()
);
// Query the payload hash stored in the endpoint for the packet.
bytes32 payloadHash = ILayerZeroEndpointV2(ENDPOINT).inboundPayloadHash(
_receiver,
_origin.srcEid,
_origin.sender,
_origin.nonce
);
// 1. Already executed if the payload hash has been cleared and the nonce is less than or equal to `lazyInboundNonce`.
if (
payloadHash == EMPTY_PAYLOAD_HASH &&
_origin.nonce <=
ILayerZeroEndpointV2(ENDPOINT).lazyInboundNonce(
_receiver,
_origin.srcEid,
_origin.sender
)
) {
return ExecutionState.Executed;
}
// 2. Executable: if nonce has not been executed and has not been nilified and nonce is less than or equal to `inboundNonce`.
if (
payloadHash != NIL_PAYLOAD_HASH &&
payloadHash == _payloadHash &&
_origin.nonce <=
ILayerZeroEndpointV2(ENDPOINT).inboundNonce(_receiver, _origin.srcEid, _origin.sender)
) {
return ExecutionState.Executable;
}
// 3. Pending but verified: only start active executable polling if payload hash is not empty nor `nil`.
if (payloadHash != EMPTY_PAYLOAD_HASH && payloadHash != NIL_PAYLOAD_HASH) {
return ExecutionState.VerifiedButNotExecutable;
}
// 4. Not executable: `catch-all`.
return ExecutionState.NotExecutable;
}
/**
* @dev Checks if a packet is in a verifiable state.
* @param _packetHeader Encoded packet header bytes.
* @param _payloadHash Keccak256 hash of packet payload.
* @return state Current `VerificationState` of the packet.
*/
function verifiable(
bytes calldata _packetHeader,
bytes32 _payloadHash
) public view returns (VerificationState) {
// Assert the header is valid for the local endpoint ID.
IReceiveUlnView(RECEIVE_ULN302).assertHeader(_packetHeader, LOCAL_EID_V2);
// Decode the receiver and origin details from the header.
address receiver = _packetHeader.receiverB20();
Origin memory origin = Origin(
_packetHeader.srcEid(),
_packetHeader.sender(),
_packetHeader.nonce()
);
// 1. Endpoint must support initialization (config present, etc).
if (!_initializable(origin, receiver)) {
return VerificationState.NotInitializable;
}
// 2. Endpoint verifiable (e.g. not already verified).
if (!_endpointVerifiable(origin, receiver, _payloadHash)) {
return VerificationState.Verified;
}
// 3. ULN verifiable (DVN or ULN, packet is verifiable).
if (
IReceiveUlnView(RECEIVE_ULN302).verifiable(
IReceiveUlnView(RECEIVE_ULN302).getUlnConfig(receiver, origin.srcEid),
keccak256(_packetHeader),
_payloadHash
)
) {
return VerificationState.Verifiable;
}
// 4. Still verifying.
return VerificationState.Verifying;
}
// --- Only ACL ---
/**
* @dev Returns the fee for a given destination and calldata size (for `msg.sender`).
* @param _dstEid Destination EID.
* @param _sender Address of message sender.
* @param _calldataSize Calldata size.
* @param _options Options encoded as bytes.
* @return fee Quoted fee for the job.
*/
function getFee(
uint32 _dstEid,
address _sender,
uint256 _calldataSize,
bytes calldata _options
) external view onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
// Construct fee parameters for the fee library call.
IExecutorFeeLib.FeeParams memory params = IExecutorFeeLib.FeeParams(
priceFeed,
_dstEid,
_sender,
_calldataSize,
defaultMultiplierBps
);
// Call external fee library (pluggable) for fee computation.
fee = IExecutorFeeLib(workerFeeLib).getFee(params, dstConfig[_dstEid], _options);
}
/**
* @dev Returns the fee for a read job (for `CmdLib`, `msg.sender`).
* @param _sender Address of message sender.
* @param _options Options encoded as bytes.
* @return fee Quoted fee for the job.
*/
function getFee(
address _sender,
bytes calldata _options
) external view onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
// Construct fee parameters for the fee library call.
IExecutorFeeLib.FeeParamsForRead memory params = IExecutorFeeLib.FeeParamsForRead(
priceFeed,
_sender,
defaultMultiplierBps
);
// Call external fee library (pluggable) for fee computation.
fee = IExecutorFeeLib(workerFeeLib).getFee(params, dstConfig[LOCAL_EID_V2], _options);
}
/// @dev Internal function to distribute native tokens to multiple recipients.
/// @param _origin Origin information for the message context.
/// @param _dstEid Destination endpoint ID.
/// @param _oapp Target contract on remote chain.
/// @param _nativeDropParams Array of receivers and amounts.
/// @param _nativeDropGasLimit Gas limit for each transfer call.
/// @return spent Total amount of native tokens sent.
function _nativeDrop(
Origin calldata _origin,
uint32 _dstEid,
address _oapp,
NativeDropParams[] calldata _nativeDropParams,
uint256 _nativeDropGasLimit
) internal returns (uint256 spent) {
uint256 paramsLength = _nativeDropParams.length;
bool[] memory success = new bool[](paramsLength);
for (uint256 i = 0; i < paramsLength; ++i) {
NativeDropParams memory param = _nativeDropParams[i];
// slither-disable-next-line arbitrary-send-eth,low-level-calls,return-bomb
(bool sent, ) = param.receiver.call{value: param.amount, gas: _nativeDropGasLimit}("");
success[i] = sent;
spent += param.amount;
}
emit NativeDropApplied(_origin, _dstEid, _oapp, _nativeDropParams, success);
}
/// @dev Checks whether the endpoint can be verified and the payload hash is not already stored.
/// @param _origin Origin information for the message context.
/// @param _receiver Address of the endpoint’s receiver contract.
/// @param _payloadHash Keccak256 hash of the message payload.
/// @return boolean `True` if the endpoint is verifiable and hasn’t seen this payload before.
function _endpointVerifiable(
Origin memory _origin,
address _receiver,
bytes32 _payloadHash
) internal view returns (bool) {
// check endpoint verifiable
if (!_verifiable(_origin, _receiver, RECEIVE_ULN302, _payloadHash)) return false;
// if `endpoint.verifiable`, also check if the payload hash matches
// the endpoint that allows re-verification. Check if this payload has already been verified.
if (
ILayerZeroEndpointV2(ENDPOINT).inboundPayloadHash(
_receiver,
_origin.srcEid,
_origin.sender,
_origin.nonce
) == _payloadHash
) return false;
return true;
}
/**
* @dev Internal helper to verify library and endpoint readiness.
* Checks if the receive library is valid for the receiver,
* if the endpoint is verifiable for the origin and receiver,
* and if the payload hash is non‐zero.
* @param _origin Origin context for verification.
* @param _receiver Receiver address on this chain.
* @param _receiveLib Address of the ULN receive library to check.
* @param _payloadHash Payload hash that must be non‐zero.
* @return True if the receive library is registered, the endpoint is verifiable, and `payloadHash ≠ 0`.
*/
function _verifiable(
Origin memory _origin,
address _receiver,
address _receiveLib,
bytes32 _payloadHash
) internal view returns (bool) {
// Library must be a valid receive library for this endpoint.
if (
!ILayerZeroEndpointV2(ENDPOINT).isValidReceiveLibrary(
_receiver,
_origin.srcEid,
_receiveLib
)
) return false;
// Endpoint must report itself as verifiable for this origin.
if (!ILayerZeroEndpointV2(ENDPOINT).verifiable(_origin, _receiver)) return false;
// Reject zero‐value hashes (not a real message).
if (_payloadHash == EMPTY_PAYLOAD_HASH) return false;
return true;
}
/**
* @dev Checks if the endpoint supports initialization for a given origin and receiver.
* @param _origin Origin context to query.
* @param _receiver Address of the receiver contract to check.
* @return boolean `True` if the endpoint supports initialization for this origin and receiver.
*/
function _initializable(Origin memory _origin, address _receiver) internal view returns (bool) {
try ILayerZeroEndpointV2(ENDPOINT).initializable(_origin, _receiver) returns (
bool initializable
) {
return initializable;
} catch {
return false;
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: LZBL-1.2
// copied from https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/packages/layerzero-v2/evm/messagelib/contracts/Executor.sol
pragma solidity ^0.8.24;
import {Origin} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
/**
* @dev Extended interface for LayerZero Endpoint v2 for message processing, composing, and alerting.
*/
interface ILayerZeroEndpointV2Extended {
/**
* @dev Returns the endpoint ID (EID) for this contract.
* @return Endpoint EID (unique uint32 identifier)
*/
function eid() external view returns (uint32);
/**
* @notice Receives and processes a cross-chain message.
* @dev Called by Executor or LayerZero infrastructure to deliver a message.
* @param _origin Struct describing the source chain and sender.
* @param _receiver Destination receiver contract address.
* @param _guid Unique message GUID (32 bytes).
* @param _message Serialized message payload.
* @param _extraData Extra data (optional, may be empty).
*/
function lzReceive(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
bytes calldata _message,
bytes calldata _extraData
) external payable;
/**
* @notice Alerts about a failed message delivery or execution attempt.
* @dev Used for off-chain monitoring and reprocessing flows.
* @param _origin Struct describing the source chain and sender.
* @param _receiver Destination receiver contract address.
* @param _guid Unique message GUID (32 bytes).
* @param _gas Gas provided for the failed attempt.
* @param _value Native token value sent.
* @param _message Serialized message payload.
* @param _extraData Extra data passed during the call.
* @param _reason ABI-encoded revert reason or error data.
*/
function lzReceiveAlert(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
uint256 _gas,
uint256 _value,
bytes calldata _message,
bytes calldata _extraData,
bytes calldata _reason
) external;
/**
* @notice Composes (initiates) a new cross-chain message from one contract to another.
* @dev Used for multi-hop or chained message execution.
* @param _from Originating contract address.
* @param _to Destination contract address.
* @param _guid Unique message GUID.
* @param _index Message sequence index for ordering.
* @param _message Serialized payload.
* @param _extraData Extra data (optional, may be empty).
*/
function lzCompose(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData
) external payable;
/**
* @notice Alerts about a failed compose attempt.
* @dev Used for off-chain monitoring and diagnostics.
* @param _from Originating contract address.
* @param _to Destination contract address.
* @param _guid Unique message GUID.
* @param _index Message sequence index.
* @param _gas Gas provided for the failed attempt.
* @param _value Native token value sent.
* @param _message Serialized payload.
* @param _extraData Extra data passed during the call.
* @param _reason ABI-encoded revert reason or error data.
*/
function lzComposeAlert(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
uint256 _gas,
uint256 _value,
bytes calldata _message,
bytes calldata _extraData,
bytes calldata _reason
) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title LayerZero ULN Verification and Execution State Enums & View Interface.
/// @dev These enums and interface are used for verifying and tracking LayerZero Ultra Light Node packet states.
/**
* @dev Represents the verification status of a LayerZero packet.
* - Verifying: Packet is still undergoing DVN verification.
* - Verifiable: Packet is ready to be committed after successful DVN verification.
* - Verified: Packet has been verified.
* - NotInitializable: Packet cannot be initialized for verification due to config/state.
*/
enum VerificationState {
Verifying,
Verifiable,
Verified,
NotInitializable
}
/**
* @dev Represents the execution status of a LayerZero packet.
* - NotExecutable: Packet cannot be executed (waiting for verification).
* - VerifiedButNotExecutable: Packet has been verified but is not yet executable (active polling).
* - Executable: Packet is executable (can be processed by the executor).
* - Executed: Packet has already been executed.
*/
enum ExecutionState {
NotExecutable,
VerifiedButNotExecutable,
Executable,
Executed
}
/**
* @dev Interface for reading Ultra Light Node (ULN) verification config and status.
*/
interface IReceiveUlnView {
/**
* @dev ULN DVN configuration for a given remote chain and OApp.
* @param confirmations Number of block confirmations required for DVN.
* @param requiredDVNCount Number of required DVNs (0 = default, special NIL_DVN_COUNT disables).
* @param optionalDVNCount Number of optional DVNs (0 = default, special NIL_DVN_COUNT disables).
* @param optionalDVNThreshold Minimum number of optional DVN confirmations required.
* @param requiredDVNs List of required DVN addresses (sorted, unique, may overlap with optional).
* @param optionalDVNs List of optional DVN addresses (sorted, unique, may overlap with required).
*/
struct UlnConfig {
uint64 confirmations;
uint8 requiredDVNCount;
uint8 optionalDVNCount;
uint8 optionalDVNThreshold;
address[] requiredDVNs;
address[] optionalDVNs;
}
/**
* @dev Checks if a packet is verifiable given its ULN config, header hash, and payload hash.
* @param _config ULN configuration to check against.
* @param _headerHash keccak256 hash of the packet header.
* @param _payloadHash keccak256 hash of the packet payload.
* @return True if the packet is currently verifiable.
*/
function verifiable(
UlnConfig memory _config,
bytes32 _headerHash,
bytes32 _payloadHash
) external view returns (bool);
/**
* @dev Returns the current ULN configuration for a given OApp and remote EID.
* @param _oapp Address of the OApp (on this chain).
* @param _remoteEid Remote LayerZero endpoint ID.
* @return rtnConfig Current ULN config for this OApp and remote endpoint.
*/
function getUlnConfig(
address _oapp,
uint32 _remoteEid
) external view returns (UlnConfig memory rtnConfig);
/**
* @dev Asserts that the provided packet header is valid for the local endpoint ID.
* @param _packetHeader Encoded packet header bytes.
* @param _localEid Local LayerZero endpoint ID.
*/
function assertHeader(bytes calldata _packetHeader, uint32 _localEid) external pure;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: LZBL-1.2
// copied from https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/packages/layerzero-v2/evm/messagelib/contracts/Worker.sol
pragma solidity ^0.8.24;
import {IWorker} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/interfaces/IWorker.sol";
import {ISendLib} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol";
import {Transfer} from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {ValueValidator} from "../common/ValueValidator.sol";
abstract contract Worker is AccessControl, Pausable, IWorker, ValueValidator {
/// @dev Role for contracts allowed to assign jobs (messaging libraries).
bytes32 internal constant _MESSAGE_LIB_ROLE = keccak256("MESSAGE_LIB_ROLE");
/// @dev Role representing addresses in the allowlist.
bytes32 internal constant _ALLOWLIST = keccak256("ALLOWLIST");
/// @dev Role representing addresses in the denylist.
bytes32 internal constant _DENYLIST = keccak256("DENYLIST");
/// @dev Admin role for configuration changes.
bytes32 internal constant _ADMIN_ROLE = keccak256("ADMIN_ROLE");
/// @dev Address of the ExecutorFeeLib contract used for fee computations.
address public workerFeeLib;
/// @dev Number of addresses currently in the allowlist.
uint64 public allowlistSize;
/// @dev Default multiplier (in basis points) applied to computed fees.
uint16 public defaultMultiplierBps;
/// @dev Address of the external price feed used by ExecutorFeeLib.
address public priceFeed;
/// @dev Mapping of remote LZ EIDs to supported option type codes.
mapping(uint32 eid => uint8[] optionTypes) internal _supportedOptionTypes;
// ========================= Constructor =========================
/**
* @dev Initialize Worker with initial libraries, admin, price feed, and roles.
* @param _messageLibs Array of message lib addresses that are granted the MESSAGE_LIB_ROLE.
* @param _priceFeed Price feed address (for fee quoting).
* @param _defaultMultiplierBps Default fee multiplier (bps).
* @param _roleAdmin Address that is granted the DEFAULT_ADMIN_ROLE (can grant and revoke all roles).
* @param _admins Array of admin addresses that are granted the ADMIN_ROLE.
*/
constructor(
address[] memory _messageLibs,
address _priceFeed,
uint16 _defaultMultiplierBps,
address _roleAdmin,
address[] memory _admins
) {
defaultMultiplierBps = _defaultMultiplierBps;
priceFeed = _priceFeed;
if (_roleAdmin != address(0x0)) {
// _roleAdmin can grant and revoke all roles
_grantRole(DEFAULT_ADMIN_ROLE, _roleAdmin);
}
// solhint-disable-next-line gas-length-in-loops
for (uint256 i = 0; i < _messageLibs.length; ++i) {
// Grant _MESSAGE_LIB_ROLE to all provided message libraries
_grantRole(_MESSAGE_LIB_ROLE, _messageLibs[i]);
}
// solhint-disable-next-line gas-length-in-loops
for (uint256 i = 0; i < _admins.length; ++i) {
// Grant _ADMIN_ROLE to all provided admin addresses
_grantRole(_ADMIN_ROLE, _admins[i]);
}
}
// ========================= Modifier =========================
/**
* @dev Modifier that restricts access to only those passing the ACL check.
* Can be used to guard fee queries, job assignments, etc.
* @param _sender Address to check ACL against.
*/
modifier onlyAcl(address _sender) {
if (!hasAcl(_sender)) {
revert Worker_NotAllowed();
}
_;
}
/**
* @dev Access control list logic.
* - If sender is in denylist: always denied
* - If allowlist is empty or sender is in allowlist: allowed
* - Else: denied
* @param _sender Address to check for ACL.
* @return True if allowed, false otherwise.
*/
function hasAcl(address _sender) public view returns (bool) {
if (hasRole(_DENYLIST, _sender)) {
return false;
} else if (allowlistSize == 0 || hasRole(_ALLOWLIST, _sender)) {
// If allowlist is empty (open mode) or sender is in allowlist
return true;
} else {
return false;
}
}
// ========================= OnyDefaultAdmin =========================
/**
* @dev Pauses or unpauses contract (if used with whenNotPaused).
* Only callable by DEFAULT_ADMIN_ROLE.
* @param _paused True to pause, false to unpause.
*/
function setPaused(bool _paused) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (_paused) {
_pause();
} else {
_unpause();
}
}
// ========================= OnlyAdmin =========================
/**
* @dev Updates the price feed contract used for fee calculation.
* Only callable by ADMIN_ROLE.
* @param _priceFeed New price feed address.
*/
function setPriceFeed(
address _priceFeed
) external onlyRole(_ADMIN_ROLE) notZeroAddress(_priceFeed) {
priceFeed = _priceFeed;
emit SetPriceFeed(_priceFeed);
}
/**
* @dev Updates ExecutorFeeLib address used for all fee calculations.
* Only callable by ADMIN_ROLE.
* @param _workerFeeLib Address of new ExecutorFeeLib contract.
*/
function setWorkerFeeLib(
address _workerFeeLib
) external onlyRole(_ADMIN_ROLE) notZeroAddress(_workerFeeLib) {
workerFeeLib = _workerFeeLib;
emit SetWorkerLib(_workerFeeLib);
}
/**
* @dev Updates the default fee multiplier (basis points).
* Only callable by ADMIN_ROLE.
* @param _multiplierBps New default multiplier (bps).
*/
function setDefaultMultiplierBps(uint16 _multiplierBps) external onlyRole(_ADMIN_ROLE) {
defaultMultiplierBps = _multiplierBps;
emit SetDefaultMultiplierBps(_multiplierBps);
}
/**
* @dev Withdraws protocol fees from supported message ULN libraries.
* Only callable by ADMIN_ROLE. Library must have MESSAGE_LIB_ROLE.
* @param _lib Message library address to withdraw from.
* @param _to Recipient address.
* @param _amount Amount to withdraw.
*/
function withdrawFee(
address _lib,
address _to,
uint256 _amount
) external onlyRole(_ADMIN_ROLE) {
if (!hasRole(_MESSAGE_LIB_ROLE, _lib)) revert Worker_OnlyMessageLib();
// Call withdrawFee on the underlying library
ISendLib(_lib).withdrawFee(_to, _amount);
emit Withdraw(_lib, _to, _amount);
}
/**
* @dev Withdraws ERC20 tokens or native currency.
* @param _token Token address (use address(0) for native).
* @param _to Recipient address.
* @param _amount Amount to withdraw.
*/
function withdrawToken(
address _token,
address _to,
uint256 _amount
) external onlyRole(_ADMIN_ROLE) {
// transfers native if _token is address(0x0)
Transfer.nativeOrToken(_token, _to, _amount);
}
/**
* @dev Set supported option type codes for a given remote endpoint.
* Used for feature upgrades, testing, or protocol extension.
* Only callable by ADMIN_ROLE.
* @param _eid Endpoint ID.
* @param _optionTypes List of supported option codes.
*/
function setSupportedOptionTypes(
uint32 _eid,
uint8[] calldata _optionTypes
) external onlyRole(_ADMIN_ROLE) {
_supportedOptionTypes[_eid] = _optionTypes;
}
// ========================= View Functions =========================
/**
* @dev Returns the list of supported option types for a given remote endpoint.
* @param _eid Endpoint ID.
* @return Array of option type codes.
*/
function getSupportedOptionTypes(uint32 _eid) external view returns (uint8[] memory) {
return _supportedOptionTypes[_eid];
}
// ========================= Internal Functions =========================
/**
* @dev Overrides AccessControl's _grantRole to increment allowlistSize as needed.
* @param _role Role to grant.
* @param _account Address to grant role to.
* @return True if role was granted, false otherwise.
*/
function _grantRole(bytes32 _role, address _account) internal override returns (bool) {
if (_role == _ALLOWLIST && !hasRole(_role, _account)) {
++allowlistSize;
}
return super._grantRole(_role, _account);
}
/**
* @dev Overrides AccessControl's _revokeRole to decrement allowlistSize as needed.
* @param _role Role to revoke.
* @param _account Address to revoke role from.
* @return True if role was revoked, false otherwise.
*/
function _revokeRole(bytes32 _role, address _account) internal override returns (bool) {
if (_role == _ALLOWLIST && hasRole(_role, _account)) {
--allowlistSize;
}
return super._revokeRole(_role, _account);
}
/**
* @dev Overrides AccessControl to disable renouncing of roles.
* Disable renouncing roles to prevent accidental loss of permissions.
*/
function renounceRole(bytes32 /*role*/, address /*account*/) public pure override {
revert Worker_RoleRenouncingDisabled();
}
}