Contract Name:
ConduitCaptain
Contract Source Code:
File 1 of 1 : ConduitCaptain
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/**
* @title ConduitCaptainInterface
* @author 0age
* @notice ConduitCaptainInterface contains function endpoints, events, and error
* declarations for the ConduitCaptain contract.
*/
interface ConduitCaptainInterface {
/**
* @dev Emit an event whenever a revoker role is updated by the owner.
*
* @param revoker The address of the new revoker role.
*/
event RevokerUpdated(address revoker);
/**
* @dev Revert with an error when attempting to set a conduit controller
* that does not contain contract code.
*/
error InvalidConduitController(address conduitController);
/**
* @dev Revert with an error when attempting to call closeChannel from an
* account that does not currently hold the revoker role.
*/
error InvalidRevoker();
/**
* @dev Revert with an error when attempting to register a revoker and
* supplying the null address.
*/
error RevokerIsNullAddress();
/**
* @notice Initiate conduit ownership transfer by assigning a new potential
* owner for the given conduit. Only callable by the owner.
*
* @param conduit The conduit for which to initiate ownership
* transfer.
* @param newPotentialOwner The new potential owner to set.
*/
function transferConduitOwnership(
address conduit,
address newPotentialOwner
) external;
/**
* @notice Clear the currently set potential owner, if any, from a conduit.
* Only callable by the owner.
*
* @param conduit The conduit for which to cancel ownership transfer.
*/
function cancelConduitOwnershipTransfer(address conduit) external;
/**
* @notice Accept ownership of a given conduit once this contract has been
* set as the current potential owner. Only callable by the owner.
*
* @param conduit The conduit for which to accept ownership transfer.
*/
function acceptConduitOwnership(address conduit) external;
/**
* @notice Open or close a channel on a given conduit, thereby allowing the
* specified account to execute transfers against that conduit.
* Extreme care must be taken when updating channels, as malicious
* or vulnerable channels can transfer any ERC20, ERC721 and ERC1155
* tokens where the token holder has granted the conduit approval.
* Only the owner may call this function.
*
* @param conduit The conduit for which to open or close the channel.
* @param channel The channel to open or close on the conduit.
* @param isOpen A boolean indicating whether to open or close the channel.
*/
function updateChannel(
address conduit,
address channel,
bool isOpen
) external;
/**
* @notice Close a channel on a given conduit, thereby preventing the
* specified account from executing transfers against that conduit.
* Only the designated revoker may call this function.
*
* @param conduit The conduit for which to close the channel.
* @param channel The channel to close on the conduit.
*/
function closeChannel(address conduit, address channel) external;
/**
* @notice Set a revoker role that can close channels. Only the owner may
* call this function.
*
* @param revoker The account to set as the revoker.
*/
function updateRevoker(address revoker) external;
/**
* @notice External view function to retrieve the address of the revoker
* role that can close channels.
*
* @return revoker The account set as the revoker.
*/
function getRevoker() external view returns (address revoker);
/**
* @notice External view function to retrieve the address of the
* ConduitController referenced by the contract
*
* @return conduitController The address of the ConduitController.
*/
function getConduitController()
external
view
returns (address conduitController);
}
/**
* @title ConduitControllerInterface
* @author 0age
* @notice ConduitControllerInterface contains relevant external function
* interfaces for a conduit controller contract.
*/
interface ConduitControllerInterface {
/**
* @notice Initiate conduit ownership transfer by assigning a new potential
* owner for the given conduit. Once set, the new potential owner
* may call `acceptOwnership` to claim ownership of the conduit.
* Only the owner of the conduit in question may call this function.
*
* @param conduit The conduit for which to initiate ownership transfer.
*/
function transferOwnership(address conduit, address newPotentialOwner)
external;
/**
* @notice Clear the currently set potential owner, if any, from a conduit.
* Only the owner of the conduit in question may call this function.
*
* @param conduit The conduit for which to cancel ownership transfer.
*/
function cancelOwnershipTransfer(address conduit) external;
/**
* @notice Accept ownership of a supplied conduit. Only accounts that the
* current owner has set as the new potential owner may call this
* function.
*
* @param conduit The conduit for which to accept ownership.
*/
function acceptOwnership(address conduit) external;
/**
* @notice Open or close a channel on a given conduit, thereby allowing the
* specified account to execute transfers against that conduit.
* Extreme care must be taken when updating channels, as malicious
* or vulnerable channels can transfer any ERC20, ERC721 and ERC1155
* tokens where the token holder has granted the conduit approval.
* Only the owner of the conduit in question may call this function.
*
* @param conduit The conduit for which to open or close the channel.
* @param channel The channel to open or close on the conduit.
* @param isOpen A boolean indicating whether to open or close the channel.
*/
function updateChannel(
address conduit,
address channel,
bool isOpen
) external;
}
/**
* @title TwoStepOwnableInterface
* @author OpenSea Protocol
* @notice TwoStepOwnableInterface contains all external function interfaces,
* events and errors for the TwoStepOwnable contract.
*/
interface TwoStepOwnableInterface {
/**
* @dev Emit an event whenever the contract owner registers a new potential
* owner.
*
* @param newPotentialOwner The new potential owner of the contract.
*/
event PotentialOwnerUpdated(address newPotentialOwner);
/**
* @dev Emit an event whenever contract ownership is transferred.
*
* @param previousOwner The previous owner of the contract.
* @param newOwner The new owner of the contract.
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Revert with an error when attempting to set an initial owner when
* one has already been set.
*/
error OwnerAlreadySet(address currentOwner);
/**
* @dev Revert with an error when attempting to call a function with the
* onlyOwner modifier from an account other than that of the owner.
*/
error CallerIsNotOwner();
/**
* @dev Revert with an error when attempting to register an initial owner
* and supplying the null address.
*/
error InitialOwnerIsNullAddress();
/**
* @dev Revert with an error when attempting to register a new potential
* owner and supplying the null address.
*/
error NewPotentialOwnerIsNullAddress();
/**
* @dev Revert with an error when attempting to claim ownership of the
* contract with a caller that is not the current potential owner.
*/
error CallerIsNotNewPotentialOwner();
/**
* @notice Initiate ownership transfer by assigning a new potential owner
* to this contract. Once set, the new potential owner may call
* `acceptOwnership` to claim ownership. Only the owner may call
* this function.
*
* @param newPotentialOwner The address for which to initiate ownership
* transfer to.
*/
function transferOwnership(address newPotentialOwner) external;
/**
* @notice Clear the currently set potential owner, if any. Only the owner
* of this contract may call this function.
*/
function cancelOwnershipTransfer() external;
/**
* @notice Accept ownership of this contract. Only the account that the
* current owner has set as the new potential owner may call this
* function.
*/
function acceptOwnership() external;
/**
* @notice An external view function that returns the potential owner.
*
* @return The address of the potential owner.
*/
function potentialOwner() external view returns (address);
/**
* @notice An external view function that returns the owner.
*
* @return The address of the owner.
*/
function owner() external view returns (address);
}
/**
* @title TwoStepOwnable
* @author OpenSea Protocol Team
* @notice TwoStepOwnable provides access control for inheriting contracts,
* where the ownership of the contract can be exchanged via a two step
* process. A potential owner is set by the current owner by calling
* `transferOwnership`, then accepted by the new potential owner by
* calling `acceptOwnership`.
*/
abstract contract TwoStepOwnable is TwoStepOwnableInterface {
// The address of the owner.
address private _owner;
// The address of the new potential owner.
address private _potentialOwner;
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
// Ensure that the caller is the owner.
if (msg.sender != _owner) {
revert CallerIsNotOwner();
}
// Continue with function execution.
_;
}
/**
* @notice Initiate ownership transfer by assigning a new potential owner
* to this contract. Once set, the new potential owner may call
* `acceptOwnership` to claim ownership. Only the owner may call
* this function.
*
* @param newPotentialOwner The address for which to initiate ownership
* transfer to.
*/
function transferOwnership(address newPotentialOwner)
external
override
onlyOwner
{
// Ensure the new potential owner is not an invalid address.
if (newPotentialOwner == address(0)) {
revert NewPotentialOwnerIsNullAddress();
}
// Emit an event indicating that the potential owner has been updated.
emit PotentialOwnerUpdated(newPotentialOwner);
// Set the new potential owner as the potential owner.
_potentialOwner = newPotentialOwner;
}
/**
* @notice Clear the currently set potential owner, if any. Only the owner
* of this contract may call this function.
*/
function cancelOwnershipTransfer() external override onlyOwner {
// Emit an event indicating that the potential owner has been cleared.
emit PotentialOwnerUpdated(address(0));
// Clear the current new potential owner.
delete _potentialOwner;
}
/**
* @notice Accept ownership of this contract. Only the account that the
* current owner has set as the new potential owner may call this
* function.
*/
function acceptOwnership() external override {
// Ensure the caller is the potential owner.
if (msg.sender != _potentialOwner) {
// Revert, indicating that caller is not current potential owner.
revert CallerIsNotNewPotentialOwner();
}
// Emit an event indicating that the potential owner has been cleared.
emit PotentialOwnerUpdated(address(0));
// Clear the current new potential owner.
delete _potentialOwner;
// Set the caller as the owner of this contract.
_setOwner(msg.sender);
}
/**
* @notice An external view function that returns the potential owner.
*
* @return The address of the potential owner.
*/
function potentialOwner() external view override returns (address) {
return _potentialOwner;
}
/**
* @notice A public view function that returns the owner.
*
* @return The address of the owner.
*/
function owner() public view override returns (address) {
return _owner;
}
/**
* @notice Internal function that sets the inital owner of the base
* contract. The initial owner must not already be set.
* To be called in the constructor or when initializing a proxy.
*
* @param initialOwner The address to set for initial ownership.
*/
function _setInitialOwner(address initialOwner) internal {
// Ensure that an initial owner has been supplied.
if (initialOwner == address(0)) {
revert InitialOwnerIsNullAddress();
}
// Ensure that the owner has not already been set.
if (_owner != address(0)) {
revert OwnerAlreadySet(_owner);
}
// Set the initial owner.
_setOwner(initialOwner);
}
/**
* @notice Private function that sets a new owner and emits a corresponding
* event.
*
* @param newOwner The address to assign as the new owner.
*/
function _setOwner(address newOwner) private {
// Emit an event indicating that the new owner has been set.
emit OwnershipTransferred(_owner, newOwner);
// Set the new owner.
_owner = newOwner;
}
}
/**
* @title ConduitCaptain
* @author 0age
* @notice ConduitCaptain is an owned contract where the owner can in turn update
* conduits that are owned by the contract. It allows for designating an
* account that may revoke channels from conduits.
*/
contract ConduitCaptain is TwoStepOwnable, ConduitCaptainInterface {
// Set the conduit controller as an immutable argument.
ConduitControllerInterface private immutable _CONDUIT_CONTROLLER;
// Designate a storage variable for the revoker role.
address private _revoker;
/**
* @dev Initialize contract by setting the conduit controller, the initial
* owner, and the initial revoker role.
*/
constructor(
address conduitController,
address initialOwner,
address initialRevoker
) {
// Ensure that a contract is deployed to the given conduit controller.
if (conduitController.code.length == 0) {
revert InvalidConduitController(conduitController);
}
// Set the conduit controller as an immutable argument.
_CONDUIT_CONTROLLER = ConduitControllerInterface(conduitController);
// Set the initial owner.
_setInitialOwner(initialOwner);
// Set the initial revoker.
_setRevoker(initialRevoker);
}
/**
* @notice Initiate conduit ownership transfer by assigning a new potential
* owner for the given conduit. Only callable by the owner.
*
* @param conduit The conduit for which to initiate ownership
* transfer.
* @param newPotentialOwner The new potential owner to set.
*/
function transferConduitOwnership(
address conduit,
address newPotentialOwner
) external override onlyOwner {
// Call the conduit controller to transfer conduit ownership.
_CONDUIT_CONTROLLER.transferOwnership(conduit, newPotentialOwner);
}
/**
* @notice Clear the currently set potential owner, if any, from a conduit.
* Only callable by the owner.
*
* @param conduit The conduit for which to cancel ownership transfer.
*/
function cancelConduitOwnershipTransfer(address conduit)
external
override
onlyOwner
{
// Call the conduit controller to cancel conduit ownership transfer.
_CONDUIT_CONTROLLER.cancelOwnershipTransfer(conduit);
}
/**
* @notice Accept ownership of a given conduit once this contract has been
* set as the current potential owner. Only callable by the owner.
*
* @param conduit The conduit for which to accept ownership transfer.
*/
function acceptConduitOwnership(address conduit)
external
override
onlyOwner
{
// Call the conduit controller to accept conduit ownership.
_CONDUIT_CONTROLLER.acceptOwnership(conduit);
}
/**
* @notice Open or close a channel on a given conduit, thereby allowing the
* specified account to execute transfers against that conduit.
* Extreme care must be taken when updating channels, as malicious
* or vulnerable channels can transfer any ERC20, ERC721 and ERC1155
* tokens where the token holder has granted the conduit approval.
* Only the owner may call this function.
*
* @param conduit The conduit for which to open or close the channel.
* @param channel The channel to open or close on the conduit.
* @param isOpen A boolean indicating whether to open or close the channel.
*/
function updateChannel(
address conduit,
address channel,
bool isOpen
) external override onlyOwner {
// Call the conduit controller to update channel status on the conduit.
_CONDUIT_CONTROLLER.updateChannel(conduit, channel, isOpen);
}
/**
* @notice Close a channel on a given conduit, thereby preventing the
* specified account from executing transfers against that conduit.
* Only the designated revoker may call this function.
*
* @param conduit The conduit for which to close the channel.
* @param channel The channel to close on the conduit.
*/
function closeChannel(address conduit, address channel) external override {
// Revert if the caller is not the revoker.
if (msg.sender != _revoker) {
revert InvalidRevoker();
}
// Call the conduit controller to close the channel on the conduit.
_CONDUIT_CONTROLLER.updateChannel(conduit, channel, false);
}
/**
* @notice Set a revoker role that can close channels. Only the owner may
* call this function.
*
* @param revoker The account to set as the revoker.
*/
function updateRevoker(address revoker) external override onlyOwner {
// Assign the new revoker role.
_setRevoker(revoker);
}
/**
* @notice External view function to retrieve the address of the revoker
* role that can close channels.
*
* @return revoker The account set as the revoker.
*/
function getRevoker() external view override returns (address revoker) {
return _revoker;
}
/**
* @notice External view function to retrieve the address of the
* ConduitController referenced by the contract
*
* @return conduitController The address of the ConduitController.
*/
function getConduitController()
external
view
override
returns (address conduitController)
{
return address(_CONDUIT_CONTROLLER);
}
/**
* @notice Internal function to set a revoker role that can close channels.
*
* @param revoker The account to set as the revoker.
*/
function _setRevoker(address revoker) internal {
// Revert if no address is supplied for the revoker role.
if (revoker == address(0)) {
revert RevokerIsNullAddress();
}
// Assign the new revoker role.
_revoker = revoker;
emit RevokerUpdated(revoker);
}
}