Contract Name:
SwitchboardRouter
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IEIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IEIP712} from "./IEIP712.sol";
/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer is IEIP712 {
/// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
/// @param maxAmount The maximum amount a spender can request to transfer
error InvalidAmount(uint256 maxAmount);
/// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
/// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
error LengthMismatch();
/// @notice Emits an event when the owner successfully invalidates an unordered nonce.
event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);
/// @notice The token and amount details for a transfer signed in the permit transfer signature
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}
/// @notice The signed permit message for a single token transfer
struct PermitTransferFrom {
TokenPermissions permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice Specifies the recipient address and amount for batched transfers.
/// @dev Recipients and amounts correspond to the index of the signed token permissions array.
/// @dev Reverts if the requested amount is greater than the permitted signed amount.
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}
/// @notice Used to reconstruct the signed permit message for multiple token transfers
/// @dev Do not need to pass in spender address as it is required that it is msg.sender
/// @dev Note that a user still signs over a spender address
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
/// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
/// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
/// @dev It returns a uint256 bitmap
/// @dev The index, or wordPosition is capped at type(uint248).max
function nonceBitmap(address, uint256) external view returns (uint256);
/// @notice Transfers a token using a signed permit message
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param signature The signature to verify
function permitTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers a token using a signed permit message
/// @notice Includes extra data provided by the caller to verify signature over
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param signature The signature to verify
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @notice Includes extra data provided by the caller to verify signature over
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Invalidates the bits specified in mask for the bitmap at the word position
/// @dev The wordPos is maxed at type(uint248).max
/// @param wordPos A number to index the nonceBitmap at
/// @param mask A bitmap masked against msg.sender's current bitmap at the word position
function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
/// @dev used to rescue funds
bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE");
/// @dev used to update configs on protocol contracts
bytes32 constant CONFIG_ROLE = keccak256("CONFIG_ROLE");
/// @dev used to update Socket DL configs
bytes32 constant SOCKET_CONFIG_ROLE = keccak256("SOCKET_CONFIG_ROLE");
/// @dev used to allow cancellation of requests
bytes32 constant CANCEL_ROLE = keccak256("CANCEL_ROLE");
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/*//////////////////////////////////////////////////////////////
BUNGEE ERRORS
//////////////////////////////////////////////////////////////*/
error MofaSignatureInvalid();
error InsufficientNativeAmount();
error UnsupportedRequest();
error RouterNotRegistered();
error CallerNotBungeeGateway();
error CallerNotEntrypoint();
error SwapOutputInsufficient();
error MinOutputNotMet();
error InvalidRequest();
error FulfilmentDeadlineNotMet();
error CallerNotDelegate();
error InvalidMsg();
error RequestProcessed();
error RequestNotProcessed();
error InvalidSwitchboard();
error PromisedAmountNotMet();
error MsgReceiveFailed();
error RouterAlreadyRegistered();
error InvalidFulfil();
error NotImplemented();
error OnlyOwner();
error OnlyNominee();
error InvalidReceiver();
error ImplAlreadyRegistered();
error InvalidAddress();
/*//////////////////////////////////////////////////////////////
SWITCHBOARD ERRORS
//////////////////////////////////////////////////////////////*/
error NotSiblingBungeeGateway();
error NotSocket();
error NotSwitchboardRouter();
error NotSwitchboardPlug();
error SwitchboardPlugZero();
error ConnectionAlreadyInitialised();
error IncorrectSwitchboard();
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol";
import {IEntrypoint} from "../interfaces/IEntrypoint.sol";
import {AccessControl} from "../utils/AccessControl.sol";
import {ISwapExecutor} from "../interfaces/ISwapExecutor.sol";
import {ICalldataExecutor} from "../interfaces/ICalldataExecutor.sol";
import {ISwitchboardRouter} from "../interfaces/ISwitchboardRouter.sol";
import {IFeeCollector} from "../interfaces/IFeeCollector.sol";
/**
* @notice WithdrawnRequest struct
* @dev This struct is used to store info about withdrawn requests on the origin chain
*/
struct WithdrawnRequest {
address token;
uint256 amount;
address receiver;
}
abstract contract BungeeGatewayStorage is AccessControl {
/// @dev address used to identify native token
address public constant NATIVE_TOKEN_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/// @notice address of the permit 2 contract
ISignatureTransfer public immutable PERMIT2;
/// @notice address of the Entrypoint
/// @dev Entrypoint for Bungee, all extraction requests are sent to this contract.
IEntrypoint public ENTRYPOINT;
/// @notice address of the SwitchboardRouter
/// @dev BungeeGateway uses this contract to handle cross-chain messages via Socket
ISwitchboardRouter public SWITCHBOARD_ROUTER;
/// @notice address of the SwapExecutor
/// @dev BungeeGateway delegates swap executions to this contract.
ISwapExecutor public SWAP_EXECUTOR;
/// @notice address of the CalldataExecutor
/// @dev BungeeGateway delegates calldata execution at destination chain to this contract.
ICalldataExecutor public CALLDATA_EXECUTOR;
/// @notice address of the FeeCollector
/// @dev BungeeGateway collects affiliate fees from the users and transfers them to this contract.
IFeeCollector public FEE_COLLECTOR;
/// @notice this mapping holds all implementation contract addresses against their implId
mapping(uint8 implId => address impl) internal _impls;
/// @notice this mapping tracks whether an implId has been used & removed
/// @dev used to prevent reusing an implId
mapping(uint8 implId => bool removed) internal _removedImpls;
/// @notice this mapping holds all the receiver contracts, these contracts will receive funds on the destination chain.
/// @dev bridged funds would reach receiver contracts first and then transmitter uses these funds to fulfil order.
mapping(address router => mapping(uint256 toChainId => address whitelistedReceiver)) internal whitelistedReceivers;
/// @notice this mapping holds all the addresses that are routers.
/// @dev bungee sends funds from the users to these routers.
/// @dev bungee calls these when fulfilment happens on the destination.
mapping(address routers => bool supported) internal bungeeRouters;
/// @notice this mapping stores orders that have been withdrawn on the originChain
/// @dev Requests are deleted from the extractedRequests mapping when withdrawn on the origin chain
/// @dev Can be used by external contracts to track & use withdrawal info about requests
mapping(bytes32 requestHash => WithdrawnRequest request) internal _withdrawnRequests;
/// @notice this mapping stores the settlement amounts collected for the beneficiaries
/// @dev not all routers would have settlement, so these amounts may not be cleared for some routers
mapping(address beneficiary => mapping(address router => mapping(address token => uint256 amount)))
public beneficiarySettlements;
/**
* @notice Constructor.
* @dev Defines all immutable variables & owner
* @param _owner owner of the contract.
* @param _permit2 address of the permit 2 contract.
*/
constructor(address _owner, address _permit2) AccessControl(_owner) {
PERMIT2 = ISignatureTransfer(_permit2);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import {AccessControl} from "../utils/AccessControl.sol";
import {CONFIG_ROLE, SOCKET_CONFIG_ROLE} from "../common/AccessRoles.sol";
import {
CallerNotBungeeGateway,
NotSiblingBungeeGateway,
NotSwitchboardPlug,
SwitchboardPlugZero,
IncorrectSwitchboard,
InvalidMsg
} from "../common/Errors.sol";
import {ISwitchboardPlug} from "../interfaces/ISwitchboardPlug.sol";
import {IBungeeGateway} from "../interfaces/IBungeeGateway.sol";
/// @title SwitchboardRouter
/// @notice Routes messages between Bungee Gateway and different SwitchboardPlugs
/// @dev Enables Bungee Protocol users to use multiple different Switchboards on Socket simultaneously
/// @dev Enables to send messages via different Switchboards without being tied to a single Switchboard & Plug
contract SwitchboardRouter is AccessControl {
/*//////////////////////////////////////////////////////////////////////////
PUBLIC STORAGE
//////////////////////////////////////////////////////////////////////////*/
/// @notice Bungee Gateway contract address
address public BUNGEE_GATEWAY;
/// @notice An ever-increasing switchboardId count
/// @dev prevents switchboardId reuse
uint32 public switchboardIdCount = 1;
/// @notice maps switchboardId to switchboardPlug
mapping(uint32 switchboardId => ISwitchboardPlug switchboardPlug) public switchboardPlugs;
/// @notice Tracks BungeeGateway contracts on sibling chains
mapping(uint32 chainId => address bungeeGateway) public bungeeGateways;
constructor(address _owner, address _bungeeGateway) AccessControl(_owner) {
_grantRole(CONFIG_ROLE, _owner);
_grantRole(SOCKET_CONFIG_ROLE, _owner);
BUNGEE_GATEWAY = _bungeeGateway;
}
/*//////////////////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Send Settlement Message via outbound message to Socket DL
/// @param chainSlug chain slug used in Socket where the requests are to be settled on
/// @param switchboardId identifier for settlement message security
/// @param msgId identifier for the message type. Used as implementation id from Bungee Gateway
/// @param destGasLimit gasLimit required to execute the settlement message on the destination chain
/// @param payload settlement message data
function sendOutboundMsg(
uint32 chainSlug,
uint32 switchboardId,
uint8 msgId,
uint256 destGasLimit,
bytes calldata payload
) external payable {
// checks if the caller is Bungee Gateway
(ISwitchboardPlug switchboardPlug, address siblingBungeeGateway) = _validateOutboundMsg(
chainSlug,
switchboardId
);
// encodes msg.sender (Bungee Gateway) along with the payload
// calls outbound with the payload
switchboardPlug.outbound{value: msg.value}(
chainSlug,
destGasLimit,
abi.encodePacked(msgId, siblingBungeeGateway, msg.sender, payload)
);
}
/// @notice Accept inbound message from another chain via Switchboard
/// @dev Can only be called by the configured SwitchboardPlug
/// @param switchboardId id of the switchboardPlug
/// @param siblingChainId id of the sibling chain where the message is coming from
/// @param payload msg payload sent.
function receiveAndDeliverMsg(uint32 switchboardId, uint32 siblingChainId, bytes calldata payload) external {
// checks if the caller is switchboardPlug
if (msg.sender != address(switchboardPlugs[switchboardId])) revert NotSwitchboardPlug();
// decodes the receiving bungee Gateway address from the incoming payload
uint8 msgId = uint8(bytes1(payload));
address msgReceiver = address(bytes20(payload[1:]));
address msgSender = address(bytes20(payload[21:]));
// @audit msgReceiver need to part of the message actually. receiver can always be the configured bungee gateway
// only problematic case would be when there is message delivered to the wrong bungee gateway,
// when there is an out of date configuration.
// Is it safe to remove it?
if (msgReceiver != BUNGEE_GATEWAY || msgSender != bungeeGateways[siblingChainId]) revert InvalidMsg();
// sends inbound message to Bungee
IBungeeGateway(msgReceiver).inboundMsgFromSwitchboard(
msgId,
switchboardId,
abi.encodePacked(switchboardId, payload[41:])
);
}
/*//////////////////////////////////////////////////////////////////////////
ADMIN FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice adds new sibling bungee gateway contract
/// Can only be called by CONFIG_ROLE.
/// @param _bungeeGateway address of the bungee gateway on sibling chain.
/// @param _chainId id of the sibling chain.
function addBungeeGateway(address _bungeeGateway, uint32 _chainId) external onlyRole(CONFIG_ROLE) {
bungeeGateways[_chainId] = _bungeeGateway;
}
/// @notice sets the bungee gateway contract.
/// Can only be called by CONFIG_ROLE.
/// @param _bungeeGateway address of the new bungee gateway.
function setBungeeGateway(address _bungeeGateway) external onlyRole(CONFIG_ROLE) {
BUNGEE_GATEWAY = _bungeeGateway;
}
/// @notice adds switchboardPlug to switchboardPlugs mapping
/// @dev Can only be called by SOCKET_CONFIG_ROLE
/// @param switchboardPlug address of the switchboardPlug mapped to the id
function addSwitchboardPlug(address switchboardPlug) external onlyRole(SOCKET_CONFIG_ROLE) {
// maps switchboardId to SwitchboardPlug
switchboardPlugs[switchboardIdCount] = ISwitchboardPlug(switchboardPlug);
// checks if the SWITCHBOARD_ID() on the contract is the same as one being assigned on SwitchboardPlug
if (switchboardIdCount != switchboardPlugs[switchboardIdCount].SWITCHBOARD_ID()) revert IncorrectSwitchboard();
// increments switchboard count
switchboardIdCount += 1;
}
/// @notice Disables SwitchboardPlug
/// @dev Can only be called by SOCKET_CONFIG_ROLE
/// @param switchboardId id of the plug to be disabled
function removeSwitchboardPlug(uint32 switchboardId) external onlyRole(SOCKET_CONFIG_ROLE) {
delete switchboardPlugs[switchboardId];
}
/// @notice Connects SwitchboardPlug to sibling chain
/// @dev Can only be called by SOCKET_CONFIG_ROLE
function connectSwitchboardPlug(
uint32 switchboardId,
uint32 siblingChainSlug,
address siblingPlug
) external onlyRole(SOCKET_CONFIG_ROLE) {
switchboardPlugs[switchboardId].connect(siblingChainSlug, siblingPlug);
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice validates the outbound message
/// @dev checks if the caller is Bungee Gateway and if the sibling bungee gateway exists
function _validateOutboundMsg(
uint32 toChainId,
uint32 switchboardId
) internal view returns (ISwitchboardPlug switchboardPlug, address siblingBungeeGateway) {
// revert if caller is not bungee gateway
if (msg.sender != BUNGEE_GATEWAY) revert CallerNotBungeeGateway();
// revert if the bungee gateway address does not exist on the sibling chain
siblingBungeeGateway = bungeeGateways[toChainId];
if (bungeeGateways[toChainId] == address(0)) revert NotSiblingBungeeGateway();
// finds the switchboardPlug to route the message through
switchboardPlug = switchboardPlugs[switchboardId];
// checks if switchboardPlug is not initialised for the switchboardId
if (address(switchboardPlug) == address(0)) revert SwitchboardPlugZero();
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import {WithdrawnRequest} from "../core/BungeeGatewayStorage.sol";
interface IBungeeGateway {
function setWhitelistedReceiver(address receiver, uint256 destinationChainId, address router) external;
function getWhitelistedReceiver(address router, uint256 destinationChainId) external view returns (address);
function inboundMsgFromSwitchboard(uint8 msgId, uint32 switchboardId, bytes calldata payload) external;
function isBungeeRouter(address router) external view returns (bool);
function withdrawnRequests(bytes32 requestHash) external view returns (WithdrawnRequest memory);
function executeImpl(uint8 implId, bytes calldata data) external payable returns (bytes memory);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
interface ICalldataExecutor {
function executeCalldata(address to, bytes memory encodedData, uint256 msgGasLimit) external returns (bool);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
interface IEntrypoint {
function executeSOR(bytes calldata data, bytes calldata mofaSignature) external payable returns (bytes memory);
function executeSR(bytes calldata data, bytes calldata mofaSignature) external payable returns (bytes memory);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
interface IFeeCollector {
function registerFee(address feeTaker, uint256 feeAmount, address feeToken) external;
function registerLockedFee(address feeTaker, uint256 feeAmount, address feeToken, bytes32 requestHash) external;
function settleFee(bytes32 requestHash) external;
function refundFee(bytes32 requestHash, address to) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title IPlug
* @notice Interface for a plug contract that executes the message received from a source chain.
*/
interface IPlug {
/**
* @dev this should be only executable by socket
* @notice executes the message received from source chain
* @notice It is expected to have original sender checks in the destination plugs using payload
* @param srcChainSlug_ chain slug of source
* @param payload_ the data which is needed by plug at inbound call on remote
*/
function inbound(uint32 srcChainSlug_, bytes calldata payload_) external payable;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
interface ISwapExecutor {
function executeSwap(address token, uint256 amount, address swapRouter, bytes memory swapPayload) external payable;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import {IPlug} from "./IPlug.sol";
interface ISwitchboardPlug is IPlug {
function SWITCHBOARD_ID() external view returns (uint32);
function connect(uint32 siblingChainSlug, address siblingPlug) external;
function outbound(uint32 siblingChainSlug, uint256 msgGasLimit, bytes calldata payload) external payable;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
interface ISwitchboardRouter {
function sendOutboundMsg(
uint32 originChainId,
uint32 switchboardId,
uint8 msgId,
uint256 destGasLimit,
bytes calldata payload
) external payable;
function receiveAndDeliverMsg(uint32 switchboardId, uint32 siblingChainId, bytes calldata payload) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import {Ownable} from "./Ownable.sol";
/**
* @title AccessControl
* @dev This abstract contract implements access control mechanism based on roles.
* Each role can have one or more addresses associated with it, which are granted
* permission to execute functions with the onlyRole modifier.
*/
abstract contract AccessControl is Ownable {
/**
* @dev A mapping of roles to a mapping of addresses to boolean values indicating whether or not they have the role.
*/
mapping(bytes32 => mapping(address => bool)) private _permits;
/**
* @dev Emitted when a role is granted to an address.
*/
event RoleGranted(bytes32 indexed role, address indexed grantee);
/**
* @dev Emitted when a role is revoked from an address.
*/
event RoleRevoked(bytes32 indexed role, address indexed revokee);
/**
* @dev Error message thrown when an address does not have permission to execute a function with onlyRole modifier.
*/
error NoPermit(bytes32 role);
/**
* @dev Constructor that sets the owner of the contract.
*/
constructor(address owner_) Ownable(owner_) {}
/**
* @dev Modifier that restricts access to addresses having roles
* Throws an error if the caller do not have permit
*/
modifier onlyRole(bytes32 role) {
if (!_permits[role][msg.sender]) revert NoPermit(role);
_;
}
/**
* @dev Checks and reverts if an address do not have a specific role.
* @param role_ The role to check.
* @param address_ The address to check.
*/
function _checkRole(bytes32 role_, address address_) internal virtual {
if (!_hasRole(role_, address_)) revert NoPermit(role_);
}
/**
* @dev Grants a role to a given address.
* @param role_ The role to grant.
* @param grantee_ The address to grant the role to.
* Emits a RoleGranted event.
* Can only be called by the owner of the contract.
*/
function grantRole(bytes32 role_, address grantee_) external virtual onlyOwner {
_grantRole(role_, grantee_);
}
/**
* @dev Revokes a role from a given address.
* @param role_ The role to revoke.
* @param revokee_ The address to revoke the role from.
* Emits a RoleRevoked event.
* Can only be called by the owner of the contract.
*/
function revokeRole(bytes32 role_, address revokee_) external virtual onlyOwner {
_revokeRole(role_, revokee_);
}
/**
* @dev Internal function to grant a role to a given address.
* @param role_ The role to grant.
* @param grantee_ The address to grant the role to.
* Emits a RoleGranted event.
*/
function _grantRole(bytes32 role_, address grantee_) internal {
_permits[role_][grantee_] = true;
emit RoleGranted(role_, grantee_);
}
/**
* @dev Internal function to revoke a role from a given address.
* @param role_ The role to revoke.
* @param revokee_ The address to revoke the role from.
* Emits a RoleRevoked event.
*/
function _revokeRole(bytes32 role_, address revokee_) internal {
_permits[role_][revokee_] = false;
emit RoleRevoked(role_, revokee_);
}
/**
* @dev Checks whether an address has a specific role.
* @param role_ The role to check.
* @param address_ The address to check.
* @return A boolean value indicating whether or not the address has the role.
*/
function hasRole(bytes32 role_, address address_) public view returns (bool) {
return _hasRole(role_, address_);
}
function _hasRole(bytes32 role_, address address_) internal view returns (bool) {
return _permits[role_][address_];
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.19;
import {OnlyOwner, OnlyNominee} from "../common/Errors.sol";
// @audit Audited before by Zellic: https://github.com/SocketDotTech/audits/blob/main/Socket-DL/07-2023%20-%20Data%20Layer%20-%20Zellic.pdf
abstract contract Ownable {
address private _owner;
address private _nominee;
event OwnerNominated(address indexed nominee);
event OwnerClaimed(address indexed claimer);
constructor(address owner_) {
_claimOwner(owner_);
}
modifier onlyOwner() {
if (msg.sender != _owner) {
revert OnlyOwner();
}
_;
}
function owner() public view returns (address) {
return _owner;
}
function nominee() public view returns (address) {
return _nominee;
}
function nominateOwner(address nominee_) external {
if (msg.sender != _owner) {
revert OnlyOwner();
}
_nominee = nominee_;
emit OwnerNominated(_nominee);
}
function claimOwner() external {
if (msg.sender != _nominee) {
revert OnlyNominee();
}
_claimOwner(msg.sender);
}
function _claimOwner(address claimer_) internal {
_owner = claimer_;
_nominee = address(0);
emit OwnerClaimed(claimer_);
}
}