Source Code
Overview
HYPE Balance
HYPE Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ControlFacet
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 20000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
import "@loans/storages/BaseStorage.sol";
import "@loans/storages/LoanStorage.sol";
import "@loans/libraries/LibValidator.sol";
import "@loans/libraries/LibDiamond.sol";
import "./IControlFacet.sol";
import "../AccessControl.sol";
/**
* @dev Implementation of control operations for the multi-source loan protocol.
*
* This contract provides administrative functions for managing protocol parameters,
* role-based access control, contract addresses, and fee structures. It follows
* the Diamond Standard (EIP-2535) pattern as a facet.
*
* The contract implements a two-tier access control system:
* - Contract Owner: Has ultimate control and can perform any operation
* - Admin Role: Can perform most administrative operations except ownership transfer
*
* Protocol fee updates follow a notice period pattern to ensure transparency
* and give users time to react to fee changes.
*
* NOTE: This contract is designed to be used as a Diamond facet and should not
* be deployed standalone.
*/
contract ControlFacet is IControlFacet, AccessControl {
/**
* @dev Minimum duration for liquidation auctions.
*
* Set to 3 days to ensure sufficient time for competitive bidding
* while preventing indefinite auction periods.
*/
uint48 public constant MIN_AUCTION_DURATION = 3 days;
/**
* @dev Maximum duration for liquidation auctions.
*
* Set to 7 days to balance auction competitiveness with timely
* resolution of defaulted loans.
*/
uint48 public constant MAX_AUCTION_DURATION = 7 days;
/**
* @dev Thrown when attempting to set protocol fee before notice period expires.
* @param _pendingProtocolFeeSetTime The timestamp when the fee can be activated
*/
error TooEarlyError(uint256 _pendingProtocolFeeSetTime);
/**
* @dev Thrown when attempting to whitelist a contract that's already whitelisted.
*/
error AlreadyWhitelistedContract();
/**
* @dev Thrown when attempting to remove a contract that's not whitelisted.
*/
error NotWhitelistedContract();
/**
* @dev Thrown when attempting to set protocol fee before notice period expires.
*/
error TooSoonError();
/**
* @dev Thrown when auction duration is outside acceptable bounds.
*/
error InvalidDurationError();
/**
* @dev Modifier that allows access to either contract owner or admin role holders.
*
* This provides flexibility in access control while maintaining security.
* Used for critical operations that should be restricted to trusted entities.
*/
modifier onlyContractOwnerOrAdminRole() {
_checkOnlyContractOwnerOrAdmin();
_;
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner_`).
*
* This is a critical operation that should only be performed by the current owner.
* The new owner will have ultimate control over the protocol.
*
* Requirements:
* - Can only be called by the current contract owner
* - `newOwner_` must not be the zero address
*
* @param newOwner_ The address of the new owner
*/
function transferOwnership(address newOwner_) external virtual override {
LibDiamond.enforceIsContractOwner();
LibValidator.checkNotZero(newOwner_);
LibDiamond.setContractOwner(newOwner_);
}
/**
* @dev Grants `role_` to `account_`.
*
* Internal function without access restriction. Access control is handled
* by the modifier which allows both contract owner and admin role holders
* to grant roles.
*
* Requirements:
* - The caller must have contract ownership or admin role
* - Emits a {RoleGranted} event
*
* @param role_ The role identifier to grant
* @param account_ The address to receive the role
*/
function grantRole(bytes32 role_, address account_) external virtual override onlyContractOwnerOrAdminRole {
AccessControlStorage.layout().hasRole[account_][role_] = true;
emit RoleGranted(role_, account_, msg.sender);
}
/**
* @dev Revokes `role_` from `account_`.
*
* Internal function without access restriction. Access control is handled
* by the modifier which allows both contract owner and admin role holders
* to revoke roles.
*
* Requirements:
* - The caller must have contract ownership or admin role
* - Emits a {RoleRevoked} event
*
* @param role_ The role identifier to revoke
* @param account_ The address to lose the role
*/
function revokeRole(bytes32 role_, address account_) external virtual override onlyContractOwnerOrAdminRole {
AccessControlStorage.layout().hasRole[account_][role_] = false;
emit RoleRevoked(role_, account_, msg.sender);
}
/**
* @dev Updates the liquidation contract address.
*
* The liquidation contract handles the auction and settlement process
* for defaulted loans. This is a critical component that must implement
* the required liquidation interface.
*
* Requirements:
* - The caller must have the DEFAULT_ADMIN_ROLE
* - `loanLiquidator_` must not be the zero address
*
* Emits:
* - {LiquidationContractUpdated} event
*
* @param loanLiquidator_ The new liquidation contract address
*/
function updateLiquidationContract(address loanLiquidator_)
external
virtual
override
onlyRole(LibAccessControl.DEFAULT_ADMIN_ROLE)
{
LibValidator.checkNotZero(loanLiquidator_);
BaseStorage.layout().loanLiquidator = loanLiquidator_;
emit LiquidationContractUpdated(loanLiquidator_);
}
/**
* @dev Sets the flash action contract address.
*
* The flash action contract enables atomic operations across multiple
* lending protocols within a single transaction. This can be set to
* zero address to disable flash loan functionality.
*
* Requirements:
* - The caller must have the DEFAULT_ADMIN_ROLE
*
* Emits:
* - {FlashActionContractUpdated} event
*
* @param newFlashActionContract_ The new flash action contract address (can be zero)
*/
function setFlashActionContract(address newFlashActionContract_)
external
virtual
override
onlyRole(LibAccessControl.DEFAULT_ADMIN_ROLE)
{
BaseStorage.layout().flashActionContract = newFlashActionContract_;
emit FlashActionContractUpdated(newFlashActionContract_);
}
/**
* @dev Updates the liquidation auction duration.
*
* This sets how long liquidation auctions will run before automatically
* settling. The duration must be within predefined bounds to ensure
* proper auction dynamics.
*
* Requirements:
* - The caller must have the DEFAULT_ADMIN_ROLE
* - Duration must be between MIN_AUCTION_DURATION and MAX_AUCTION_DURATION
*
* Emits:
* - {LiquidationAuctionDurationUpdated} event
*
* @param liquidationAuctionDuration_ The new auction duration in seconds
*/
function updateLiquidationAuctionDuration(uint48 liquidationAuctionDuration_)
external
virtual
override
onlyRole(LibAccessControl.DEFAULT_ADMIN_ROLE)
{
require(
liquidationAuctionDuration_ >= MIN_AUCTION_DURATION && liquidationAuctionDuration_ <= MAX_AUCTION_DURATION,
InvalidDurationError()
);
BaseStorage.layout().liquidationAuctionDuration = liquidationAuctionDuration_;
emit LiquidationAuctionDurationUpdated(liquidationAuctionDuration_);
}
/**
* @dev Sets the minimum lock period for new loans.
*
* During the lock period, loans cannot be refinanced or modified,
* providing stability and predictability for lenders. This applies
* to all new loans created after the change.
*
* Requirements:
* - The caller must have the DEFAULT_ADMIN_ROLE
*
* Emits:
* - {MinLockPeriodUpdated} event
*
* @param minLockPeriod_ The new minimum lock period in BPS
*/
function setMinLockPeriod(uint256 minLockPeriod_)
external
virtual
override
onlyRole(LibAccessControl.DEFAULT_ADMIN_ROLE)
{
LoanStorage.layout().minLockPeriod = minLockPeriod_;
emit MinLockPeriodUpdated(minLockPeriod_);
}
/**
* @dev Adds a contract to the callback whitelist.
*
* Whitelisted contracts can receive callbacks during loan operations,
* enabling integration with external protocols. Only trusted contracts
* should be whitelisted to prevent malicious behavior.
*
* Requirements:
* - The caller must have the DEFAULT_ADMIN_ROLE
* - `contract_` must not be the zero address
* - `contract_` must not already be whitelisted
*
* Emits:
* - {WhitelistedCallbackContractAdded} event
*
* @param contract_ The contract address to add to the whitelist
*/
function addWhitelistedCallbackContract(address contract_)
external
virtual
override
onlyRole(LibAccessControl.DEFAULT_ADMIN_ROLE)
{
LibValidator.checkNotZero(contract_);
require(!BaseStorage.layout().isWhitelistedCallbackContract[contract_], AlreadyWhitelistedContract());
BaseStorage.layout().isWhitelistedCallbackContract[contract_] = true;
emit WhitelistedCallbackContractAdded(contract_);
}
/**
* @dev Removes a contract from the callback whitelist.
*
* This revokes the contract's ability to receive callbacks during
* loan operations. Useful for removing compromised or deprecated
* integration contracts.
*
* Requirements:
* - The caller must have the DEFAULT_ADMIN_ROLE
* - `contract_` must currently be whitelisted
*
* Emits:
* - {WhitelistedCallbackContractRemoved} event
*
* @param contract_ The contract address to remove from the whitelist
*/
function removeWhitelistedCallbackContract(address contract_)
external
virtual
override
onlyRole(LibAccessControl.DEFAULT_ADMIN_ROLE)
{
require(BaseStorage.layout().isWhitelistedCallbackContract[contract_], NotWhitelistedContract());
BaseStorage.layout().isWhitelistedCallbackContract[contract_] = false;
emit WhitelistedCallbackContractRemoved(contract_);
}
/**
* @dev Updates the minimum APR improvement required for loan refinancing.
*
* This prevents frivolous refinancing attempts by requiring a meaningful
* improvement in loan terms. The value is typically expressed in basis points.
*
* Requirements:
* - The caller must have the DEFAULT_ADMIN_ROLE
*
* Emits:
* - {MinAprImprovementUpdated} event
*
* @param minImprovementApr_ The new minimum APR improvement in basis points
*/
function updateMinImprovementApr(uint256 minImprovementApr_)
external
virtual
override
onlyRole(LibAccessControl.DEFAULT_ADMIN_ROLE)
{
LoanStorage.layout().minImprovementApr = minImprovementApr_;
emit MinAprImprovementUpdated(minImprovementApr_);
}
/**
* @dev Stages a new protocol fee configuration for future activation.
*
* The new fee structure enters a notice period before becoming active,
* allowing users time to understand and react to fee changes. This
* implements a transparent fee update mechanism.
*
* Requirements:
* - The caller must have the DEFAULT_ADMIN_ROLE
* - Fee recipient must not be the zero address
*
* Emits:
* - {ProtocolFeePendingUpdate} event
*
* NOTE: The fee becomes active only after calling `setProtocolFee`
* once the notice period has elapsed.
*
* @param protocolFee_ The new protocol fee configuration to stage
*/
function updateProtocolFee(ProtocolFee calldata protocolFee_)
external
virtual
override
onlyRole(LibAccessControl.DEFAULT_ADMIN_ROLE)
{
LibValidator.checkNotZero(protocolFee_.recipient);
BaseStorage.Layout storage $ = BaseStorage.layout();
$.pendingProtocolFee = protocolFee_;
$.pendingProtocolFeeSetTime = block.timestamp;
emit ProtocolFeePendingUpdate(protocolFee_);
}
/**
* @dev Activates a previously staged protocol fee.
*
* This function can only be called after the required notice period
* has elapsed since the fee was staged via `updateProtocolFee`.
* Once activated, the new fee structure applies to all future operations.
*
* Requirements:
* - A fee update must be pending
* - Can be called by anyone once conditions are met
*
* Emits:
* - {ProtocolFeeUpdated} event with the newly active fee structure
*
* NOTE: This function clears the pending fee state after activation.
*/
function setProtocolFee() external virtual override {
require(block.timestamp >= BaseStorage.layout().pendingProtocolFeeSetTime, TooSoonError());
BaseStorage.Layout storage $ = BaseStorage.layout();
ProtocolFee memory newProtocolFee = $.pendingProtocolFee;
$.protocolFee = newProtocolFee;
delete $.pendingProtocolFee;
$.pendingProtocolFeeSetTime = type(uint256).max;
emit ProtocolFeeUpdated(newProtocolFee);
}
/**
* @dev Internal function to check if caller has contract ownership or admin role.
*
* This function implements the dual access control logic where either
* the contract owner or holders of the DEFAULT_ADMIN_ROLE can perform
* certain operations.
*
* The function first checks for admin role (cheaper check) and falls back
* to owner check if the role check fails.
*/
function _checkOnlyContractOwnerOrAdmin() internal view {
if (!LibAccessControl.hasRole(LibAccessControl.DEFAULT_ADMIN_ROLE, msg.sender)) {
LibDiamond.enforceIsContractOwner();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
/**
* @dev Protocol fee configuration structure.
*
* Defines the fee structure for protocol operations, including the recipient
* address and the fee fraction. Used for both active and pending fee configurations.
*/
struct ProtocolFee {
/**
* @dev Address that will receive the protocol fees.
*
* Must not be the zero address when setting active fees.
*/
address recipient;
/**
* @dev Fee fraction expressed in basis points.
*
* For example, 100 = 1%, 10000 = 100%. The maximum value
* should be validated by the implementing contract.
*/
uint256 fraction;
}
/**
* @dev Storage layout for base protocol configuration and contracts.
*
* This library implements the Diamond Storage pattern to store core protocol
* configuration in a specific storage slot, avoiding storage collisions
* when used in a Diamond proxy architecture.
*
* The storage contains addresses of key protocol contracts, fee configurations,
* and operational parameters that are shared across different facets.
*
* Storage Location: Deterministically calculated using keccak256 hash to ensure
* consistency across deployments and avoid collisions with other storage layouts.
*/
library BaseStorage {
/**
* @dev Storage slot for the base storage layout.
*
* Calculated as: keccak256(abi.encode(uint256(keccak256("bae.lending.storage.Base")) - 1)) & ~bytes32(uint256(0xff))
* This ensures a pseudo-random storage slot that avoids collisions with
* standard storage layouts and other Diamond storage patterns.
*/
bytes32 private constant BaseLoanStorageLocation =
0x014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2000;
/**
* @dev Base storage layout containing core protocol configuration.
*
* This struct contains all the essential addresses and parameters needed
* for protocol operation across different facets.
*/
struct Layout {
/**
* @dev Address of the currency manager contract.
*
* Manages whitelist and validation of ERC20 tokens that can be used
* as principal currencies in loans.
*/
address currencyManager;
/**
* @dev Address of the collection manager contract.
*
* Manages whitelist and validation of NFT collections that can be used
* as collateral in loans.
*/
address collectionManager;
/**
* @dev Address of the loan liquidator contract.
*
* Handles the liquidation process for defaulted loans, including
* auction management and collateral disposal.
*/
address loanLiquidator;
/**
* @dev Address of the loan manager registry contract.
*
* Registry of approved loan manager contracts that can interact
* with the protocol on behalf of users.
*/
address loanManagerRegistry;
/**
* @dev Address of the fee discount manager contract.
*
* Manages fee discounts for qualifying users or specific conditions.
* May be zero address if fee discounts are not implemented.
*/
address feeDiscountManager;
/**
* @dev Address of the delegate registry contract.
*
* Manages delegation relationships for governance and operational
* permissions within the protocol.
*/
address delegateRegistry;
/**
* @dev Address of the flash action contract.
*
* Enables atomic operations and flash loans across multiple protocols.
* May be zero address if flash functionality is disabled.
*/
address flashActionContract;
/**
* @dev Current active protocol fee configuration.
*
* Defines the fee structure currently in effect for protocol operations.
*/
ProtocolFee protocolFee;
/**
* @dev Pending protocol fee configuration awaiting activation.
*
* New fee structure that has been proposed but is still in the notice period.
*/
ProtocolFee pendingProtocolFee;
/**
* @dev Timestamp when the pending protocol fee was set.
*
* Used to enforce the notice period before fee changes can take effect.
* Set to type(uint256).max when no fee change is pending.
*/
uint256 pendingProtocolFeeSetTime;
/**
* @dev Duration for liquidation auctions in seconds.
*
* Determines how long liquidation auctions remain open for bidding
* before automatic settlement occurs.
*/
uint48 liquidationAuctionDuration;
/**
* @dev Mapping of contracts whitelisted to receive callbacks.
*
* Whitelisted contracts can receive notifications during loan operations,
* enabling complex integrations and automated responses.
*/
mapping(address => bool) isWhitelistedCallbackContract;
}
/**
* @dev Returns the storage pointer for the base storage layout.
*
* Uses inline assembly to return a storage pointer to the predetermined
* storage slot, following the Diamond Storage pattern.
*
* @return $ Storage pointer to the base storage layout
*/
function layout() internal pure returns (Layout storage $) {
assembly {
$.slot := BaseLoanStorageLocation
}
}
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
/**
* @dev Storage layout for loan-specific data and operational parameters.
*
* This library implements the Diamond Storage pattern to store loan-related
* data in a specific storage slot, separate from base protocol configuration.
*
* The storage contains loan tracking data, offer management, capacity tracking,
* and operational parameters specific to loan functionality.
*
* Storage Location: Uses a different deterministically calculated slot from
* BaseStorage to ensure proper separation of concerns.
*/
library LoanStorage {
/**
* @dev Storage slot for the loan storage layout.
*
* Calculated as: keccak256(abi.encode(uint256(keccak256("bae.lending.storage.Loan")) - 1)) & ~bytes32(uint256(0xff))
* This ensures a pseudo-random storage slot that avoids collisions with
* other storage layouts in the Diamond architecture.
*/
bytes32 private constant BaseLoanStorageLocation =
0x847582c39cc3b2c67c7a5fe5a43704b4d7ff45ccf56777815a28a282cadb5800;
/**
* @dev Loan storage layout containing all loan-related data.
*
* This struct organizes loan data, offer tracking, capacity management,
* and cancellation status in a coherent storage structure.
*/
struct Layout {
/**
* @dev Total number of loans issued by the protocol.
*
* Monotonically increasing counter used for generating unique loan IDs
* and tracking protocol usage statistics.
*/
uint256 totalLoansIssued;
/**
* @dev Maximum number of tranches allowed per loan.
*
* Tranches enable complex loan structures with multiple lenders having
* different terms, priorities, and risk profiles within a single loan.
*/
uint256 maxTranches;
/**
* @dev Minimum lock period for new loans in seconds.
*
* During the lock period, loans cannot be refinanced or modified,
* providing stability and predictability for lenders.
*/
uint256 minLockPeriod;
/**
* @dev Minimum APR improvement required for loan refinancing.
*
* Expressed in basis points to prevent frivolous refinancing attempts
* by requiring meaningful improvement in loan terms.
*/
uint256 minImprovementApr;
/**
* @dev Mapping from loan ID to loan data hash.
*
* Stores the hash of loan parameters for verification and integrity checks.
* The hash serves as a commitment to the loan terms and enables detection
* of any unauthorized modifications.
*/
mapping(uint256 loanId => bytes32 loanHash) loans;
/**
* @dev Mapping of used capacity for lender offers.
*
* Tracks how much of each lender's offer has been utilized across all loans.
* Used to enforce offer capacity limits and calculate remaining availability.
*
* Structure: lender address => offer ID => used amount
*/
mapping(address user => mapping(uint256 offerId => uint256 used)) used;
/**
* @dev Mapping of used capacity per borrower for lender offers.
*
* Enables per-borrower capacity limits on offers, allowing lenders to
* diversify risk by limiting exposure to individual borrowers.
*
* Structure: lender address => borrower address => offer ID => used amount
*/
mapping(address user => mapping(address borrower => mapping(uint256 offerId => uint256 used))) usedBorrower;
/**
* @dev Mapping of minimum offer ID for each user's lending offers.
*
* Used for gas-efficient iteration over active offers and cleanup of
* expired offers. Offers with IDs below this value may be inactive.
*/
mapping(address user => uint256 minOfferId) minOfferId;
/**
* @dev Mapping of minimum borrow offer ID for each user.
*
* Used for efficient management of borrow requests and cleanup of
* expired or fulfilled borrow offers.
*/
mapping(address user => uint256 minOfferId) minBorrowOfferId;
/**
* @dev Mapping of cancelled renegotiation offers.
*
* Tracks which renegotiation offers have been cancelled by their creators.
* Renegotiation offers allow modification of existing loan terms.
*
* Structure: user address => renegotiation ID => is cancelled
*/
mapping(address user => mapping(uint256 renegotiationIf => bool notActive)) isRenegotiationOfferCancelled;
/**
* @dev Mapping of cancelled borrow offers.
*
* Tracks which borrow offers have been cancelled by borrowers before
* being matched with lenders.
*
* Structure: borrower address => offer ID => is cancelled
*/
mapping(address user => mapping(uint256 offerId => bool notActive)) isBorrowOfferCancelled;
/**
* @dev Mapping of cancelled lending offers.
*
* Tracks which lending offers have been cancelled by lenders before
* being accepted by borrowers.
*
* Structure: lender address => offer ID => is cancelled
*/
mapping(address user => mapping(uint256 offerId => bool notActive)) isOfferCancelled;
/**
* @dev Mapping of lenders attached to borrower offers.
*
* When a borrower creates an offer that gets matched with a specific lender,
* this mapping tracks that relationship for future reference and validation.
*
* Structure: borrower address => offer ID => attached lender address
*/
mapping(address borrower => mapping(uint256 offerId => address)) borrowerOfferAttachedLender;
}
/**
* @dev Returns the storage pointer for the loan storage layout.
*
* Uses inline assembly to return a storage pointer to the predetermined
* storage slot, following the Diamond Storage pattern.
*
* @return $ Storage pointer to the loan storage layout
*/
function layout() internal pure returns (Layout storage $) {
assembly {
$.slot := BaseLoanStorageLocation
}
}
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
/**
* @dev Library for common input validation operations.
*
* Provides reusable validation functions to ensure data integrity
* and prevent common errors like zero address assignments.
*/
library LibValidator {
/**
* @dev Thrown when an address parameter is the zero address.
*/
error AddressZero();
/**
* @dev Validates that an address is not the zero address.
*
* @param target_ The address to validate
*/
function checkNotZero(address target_) internal pure {
require(target_ != address(0), AddressZero());
}
}// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.18; /** * \ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 * /***************************************************************************** */ import { IDiamondCut } from "@loans/facets/DiamondCut/IDiamondCut.sol"; import { IDiamondLoupe } from "@loans/facets/DiamondLoup/IDiamondLoupe.sol"; library LibDiamond { bytes32 public constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); struct FacetAddressAndSelectorPosition { address facetAddress; uint16 selectorPosition; } struct DiamondStorage { // function selector => facet address and selector position in selectors array mapping(bytes4 => FacetAddressAndSelectorPosition) facetAddressAndSelectorPosition; bytes4[] selectors; mapping(bytes4 => bool) supportedInterfaces; // owner of the contract address contractOwner; } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } } event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; ds.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); } function contractOwner() internal view returns (address contractOwner_) { contractOwner_ = diamondStorage().contractOwner; } function enforceIsOwnerOrContract() internal view { require( msg.sender == diamondStorage().contractOwner || msg.sender == address(this), "LibDiamond: Must be contract or owner" ); } function enforceIsContractOwner() internal view { require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner"); } event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata); // Internal function version of diamondCut function diamondCut(IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) internal { for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action; if (action == IDiamondCut.FacetCutAction.Add) { addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Replace) { replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Remove) { removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else { revert("LibDiamondCut: Incorrect FacetCutAction"); } } emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); uint16 selectorCount = uint16(ds.selectors.length); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); enforceHasContractCode(_facetAddress, "LibDiamondCut: Add facet has no code"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.facetAddressAndSelectorPosition[selector].facetAddress; require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists"); ds.facetAddressAndSelectorPosition[selector] = FacetAddressAndSelectorPosition(_facetAddress, selectorCount); ds.selectors.push(selector); selectorCount++; } } function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Replace facet can't be address(0)"); enforceHasContractCode(_facetAddress, "LibDiamondCut: Replace facet has no code"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.facetAddressAndSelectorPosition[selector].facetAddress; // can't replace immutable functions -- functions defined directly in the diamond require(oldFacetAddress != address(this), "LibDiamondCut: Can't replace immutable function"); require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function"); require(oldFacetAddress != address(0), "LibDiamondCut: Can't replace function that doesn't exist"); // replace old facet address ds.facetAddressAndSelectorPosition[selector].facetAddress = _facetAddress; } } function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); uint256 selectorCount = ds.selectors.length; require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; FacetAddressAndSelectorPosition memory oldFacetAddressAndSelectorPosition = ds.facetAddressAndSelectorPosition[selector]; require( oldFacetAddressAndSelectorPosition.facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist" ); // can't remove immutable functions -- functions defined directly in the diamond require( oldFacetAddressAndSelectorPosition.facetAddress != address(this), "LibDiamondCut: Can't remove immutable function." ); // replace selector with last selector selectorCount--; if (oldFacetAddressAndSelectorPosition.selectorPosition != selectorCount) { bytes4 lastSelector = ds.selectors[selectorCount]; ds.selectors[oldFacetAddressAndSelectorPosition.selectorPosition] = lastSelector; ds.facetAddressAndSelectorPosition[lastSelector].selectorPosition = oldFacetAddressAndSelectorPosition.selectorPosition; } // delete last selector ds.selectors.pop(); delete ds.facetAddressAndSelectorPosition[selector]; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { require(_calldata.length == 0, "LibDiamondCut: _init is address(0) but_calldata is not empty"); } else { require(_calldata.length > 0, "LibDiamondCut: _calldata is empty but _init is not address(0)"); if (_init != address(this)) { enforceHasContractCode(_init, "LibDiamondCut: _init address has no code"); } (bool success, bytes memory error) = _init.delegatecall(_calldata); if (!success) { if (error.length > 0) { // bubble up the error revert(string(error)); } else { revert("LibDiamondCut: _init function reverted"); } } } } function enforceHasContractCode(address _contract, string memory _errorMessage) internal view { uint256 contractSize; assembly { contractSize := extcodesize(_contract) } require(contractSize > 0, _errorMessage); } }
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
import "./IControlEvents.sol";
/**
* @dev Interface for control operations in the multi-source loan protocol.
*
* This interface provides administrative functions for managing protocol
* parameters, role-based access control, contract addresses, and fee structures.
*
* All functions in this interface require appropriate administrative privileges
* and may have additional validation requirements.
*
* The interface extends IControlEvents to ensure all control operations
* can emit the appropriate events for transparency and monitoring.
*/
interface IControlFacet is IControlEvents {
/**
* @dev Transfers ownership of the contract to a new account (`newOwner_`).
*
* Requirements:
* - Can only be called by the current owner
* - `newOwner_` must not be the zero address
*
* @param newOwner_ The address of the new owner
*/
function transferOwnership(address newOwner_) external;
/**
* @dev Grants `role_` to `account_`.
*
* If `account_` had not been already granted `role_`, emits a {RoleGranted} event.
*
* Requirements:
* - The caller must have the admin role for the role being granted
* - `account_` must not be the zero address
*
* @param role_ The role identifier to grant
* @param account_ The address to receive the 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 the admin role for the role being revoked
* - `account_` must currently have the role
*
* @param role_ The role identifier to revoke
* @param account_ The address to lose the role
*/
function revokeRole(bytes32 role_, address account_) external;
/**
* @dev Updates the liquidation contract address.
*
* Requirements:
* - The caller must have the appropriate admin role
* - `loanLiquidator_` must not be the zero address
* - `loanLiquidator_` must implement the required liquidation interface
*
* @param loanLiquidator_ The new liquidation contract address
*/
function updateLiquidationContract(address loanLiquidator_) external;
/**
* @dev Sets the flash action contract address.
*
* The flash action contract enables atomic operations across multiple
* lending protocols within a single transaction.
*
* Requirements:
* - The caller must have the appropriate admin role
* - `newFlashActionContract_` can be the zero address to disable flash actions
*
* @param newFlashActionContract_ The new flash action contract address
*/
function setFlashActionContract(address newFlashActionContract_) external;
/**
* @dev Updates the liquidation auction duration.
*
* This sets how long liquidation auctions will run before automatically
* settling at the current highest bid.
*
* Requirements:
* - The caller must have the appropriate admin role
* - `liquidationAuctionDuration_` must be within reasonable bounds
*
* @param liquidationAuctionDuration_ The new auction duration in seconds
*/
function updateLiquidationAuctionDuration(uint48 liquidationAuctionDuration_) external;
/**
* @dev Sets the minimum lock period for new loans.
*
* During the lock period, loans cannot be refinanced or modified,
* providing stability for lenders.
*
* Requirements:
* - The caller must have the appropriate admin role
* - `minLockPeriod_` must be greater than zero
*
* @param minLockPeriod_ The new minimum lock period in BPS
*/
function setMinLockPeriod(uint256 minLockPeriod_) external;
/**
* @dev Updates the minimum APR improvement required for loan refinancing.
*
* This prevents frivolous refinancing attempts and ensures meaningful
* improvements for borrowers.
*
* Requirements:
* - The caller must have the appropriate admin role
* - `minImprovementApr_` must be expressed in basis points
*
* @param minImprovementApr_ The new minimum APR improvement in basis points
*/
function updateMinImprovementApr(uint256 minImprovementApr_) external;
/**
* @dev Adds a contract to the callback whitelist.
*
* Whitelisted contracts can receive callbacks during loan operations,
* enabling integration with external protocols.
*
* Requirements:
* - The caller must have the appropriate admin role
* - `contract_` must not be the zero address
* - `contract_` must not already be whitelisted
*
* @param contract_ The contract address to add to the whitelist
*/
function addWhitelistedCallbackContract(address contract_) external;
/**
* @dev Removes a contract from the callback whitelist.
*
* Requirements:
* - The caller must have the appropriate admin role
* - `contract_` must currently be whitelisted
*
* @param contract_ The contract address to remove from the whitelist
*/
function removeWhitelistedCallbackContract(address contract_) external;
/**
* @dev Stages a new protocol fee configuration for future activation.
*
* The new fee structure enters a notice period before becoming active,
* allowing users to react to fee changes.
*
* Requirements:
* - The caller must have the appropriate admin role
* - Fee parameters must be within acceptable ranges
* - Notice period must be respected between fee updates
*
* @param protocolFee_ The new protocol fee configuration to stage
*/
function updateProtocolFee(ProtocolFee calldata protocolFee_) external;
/**
* @dev Activates a previously staged protocol fee.
*
* This function can only be called after the required notice period
* has elapsed since the fee was staged via `updateProtocolFee`.
*
* Requirements:
* - The caller must have the appropriate admin role
* - A fee update must be pending
* - The notice period must have elapsed
*
* Emits:
* - {ProtocolFeeUpdated} event with the newly active fee structure
*/
function setProtocolFee() external;
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
import "@loans/libraries/LibAccessControl.sol";
/**
* @title AccessControl
* @author BAE Lending Protocol
* @notice Abstract contract providing role-based access control functionality
* @dev Implements OpenZeppelin-style access control patterns with Diamond proxy compatibility.
* Provides modifiers and internal functions for role-based authorization.
*
* This contract is designed to be inherited by Diamond facets that need role-based
* access control. It uses the LibAccessControl library for actual role checking
* to maintain compatibility with Diamond storage patterns.
*/
abstract contract AccessControl {
/**
* @notice Thrown when an account lacks the required role for an operation
* @dev This error should be caught and handled appropriately by calling contracts
* @param account The address that attempted the unauthorized action
* @param neededRole The role identifier that was required for the operation
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @notice Modifier that restricts function access to accounts with a specific role
* @dev Reverts with AccessControlUnauthorizedAccount if the caller lacks the required role
* @param role_ The role identifier required to execute the function
*
* Usage:
* ```solidity
* function adminFunction() external onlyRole(DEFAULT_ADMIN_ROLE) {
* // Function implementation
* }
* ```
*/
modifier onlyRole(bytes32 role_) {
_checkRole(role_);
_;
}
/**
* @notice Internal function to check if msg.sender has the required role
* @dev This is a convenience function that checks the role for the current message sender.
* Can be overridden in derived contracts to customize authorization logic.
* @param role_ The role identifier to check against msg.sender
*
* @custom:throws AccessControlUnauthorizedAccount if msg.sender lacks the required role
*/
function _checkRole(bytes32 role_) internal view virtual {
_checkRole(role_, msg.sender);
}
/**
* @notice Internal function to check if a specific account has the required role
* @dev Core authorization function that performs the actual role verification.
* Uses LibAccessControl.hasRole() to query the Diamond storage.
* Can be overridden in derived contracts for custom authorization logic.
*
* @param role_ The role identifier to check
* @param account The address to verify role membership for
*
* @custom:throws AccessControlUnauthorizedAccount if the account lacks the required role
*/
function _checkRole(bytes32 role_, address account) internal view virtual {
require(LibAccessControl.hasRole(role_, account), AccessControlUnauthorizedAccount(account, role_));
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity =0.8.28;
interface IDiamondCut {
// Add=0, Replace=1, Remove=2
enum FacetCutAction {
Add,
Replace,
Remove
}
struct FacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}
/// @notice Add/replace/remove any number of functions and optionally execute
/// a function with delegatecall
/// @param _diamondCut Contains the facet addresses and function selectors
/// @param _init The address of the contract or facet to execute _calldata
/// @param _calldata A function call, including function selector and arguments
/// _calldata is executed with delegatecall on _init
function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;
event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity =0.8.28;
interface IDiamondLoupe {
struct Facet {
address facetAddress;
bytes4[] functionSelectors;
}
/// @notice Gets all facet addresses and their four byte function selectors.
/// @return facets_ Facet
function facets() external view returns (Facet[] memory facets_);
/// @notice Gets all the function selectors supported by a specific facet.
/// @param _facet The facet address.
/// @return facetFunctionSelectors_
function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_);
/// @notice Get all the facet addresses used by a diamond.
/// @return facetAddresses_
function facetAddresses() external view returns (address[] memory facetAddresses_);
/// @notice Gets the facet that supports the given selector.
/// @dev If facet is not found return address(0).
/// @param _functionSelector The function selector.
/// @return facetAddress_ The facet address.
function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
import { ProtocolFee } from "@loans/storages/BaseStorage.sol";
/**
* @dev Interface for control-related events in the multi-source loan protocol.
*
* This interface defines all events that are emitted by control operations
* such as role management, contract updates, and protocol parameter changes.
*
* Events follow the standard pattern of including indexed parameters for
* efficient filtering and non-indexed parameters for additional context.
*/
interface IControlEvents {
/**
* @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}.
*
* @param role The role identifier that was granted
* @param account The address that received the role
* @param sender The address that granted the role (must have admin privileges)
*/
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`)
*
* @param role The role identifier that was revoked
* @param account The address that lost the role
* @param sender The address that revoked the role
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when the minimum bid for liquidations is updated.
*
* @param newMinBid The new minimum bid amount required for liquidation auctions
*/
event MinBidLiquidationUpdated(uint256 newMinBid);
/**
* @dev Emitted when the liquidation contract address is updated.
*
* @param liquidator The new liquidation contract address
*/
event LiquidationContractUpdated(address liquidator);
/**
* @dev Emitted when the minimum APR improvement requirement is updated.
*
* @param _minimum The new minimum APR improvement required for loan refinancing
*/
event MinAprImprovementUpdated(uint256 _minimum);
/**
* @dev Emitted when a contract is added to the callback whitelist.
*
* @param contractAdded The address of the contract added to the whitelist
*/
event WhitelistedCallbackContractAdded(address contractAdded);
/**
* @dev Emitted when a contract is removed from the callback whitelist.
*
* @param contractRemoved The address of the contract removed from the whitelist
*/
event WhitelistedCallbackContractRemoved(address contractRemoved);
/**
* @dev Emitted when `protocolFee` is updated and becomes effective.
*
* This event is fired when the new protocol fee structure takes effect
* after the required notice period has elapsed.
*
* @param fee The newly activated protocol fee configuration
*/
event ProtocolFeeUpdated(ProtocolFee fee);
/**
* @dev Emitted when a new fee is staged and enters the notice period.
*
* The staged fee will become active after call Set Protocol Fee
* expires, providing users time to react to fee changes.
*
* @param fee The fee configuration that will become active after the notice period
*/
event ProtocolFeePendingUpdate(ProtocolFee fee);
/**
* @dev Emitted when the liquidation auction duration is updated.
*
* @param newDuration The new duration for liquidation auctions in seconds
*/
event LiquidationAuctionDurationUpdated(uint256 newDuration);
/**
* @dev Emitted when the flash action contract address is updated.
*
* @param newFlashActionContract The new flash action contract address
*/
event FlashActionContractUpdated(address newFlashActionContract);
/**
* @dev Emitted when the minimum lock period for loans is updated.
*
* @param minLockPeriod The new minimum lock period in BPS
*/
event MinLockPeriodUpdated(uint256 minLockPeriod);
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
import "@loans/storages/AccessControlStorage.sol";
/**
* @title LibAccessControl
* @author BAE Lending Protocol
* @notice Library providing core access control functionality for role-based permissions
* @dev Implements role-based access control patterns compatible with OpenZeppelin's AccessControl
* but optimized for Diamond proxy storage patterns. Provides internal functions for role checking.
*/
library LibAccessControl {
/// @notice The default admin role identifier, represented as bytes32(0)
/// @dev This role has special privileges and can grant/revoke other roles
bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @notice Checks if an account has been granted a specific role
* @dev Queries the Diamond storage to determine role membership
* @param role_ The role identifier to check (keccak256 hash)
* @param account_ The address to check for role membership
* @return hasRole True if the account has the specified role, false otherwise
*/
function hasRole(bytes32 role_, address account_) internal view returns (bool) {
AccessControlStorage.Layout storage $ = AccessControlStorage.layout();
return $.hasRole[account_][role_];
}
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;
/**
* @title AccessControlStorage
* @author BAE Lending Protocol
* @notice Provides storage layout for role-based access control in a Diamond proxy pattern
* @dev This library implements ERC-2535 Diamond storage pattern to avoid storage collisions
* in a multi-facet proxy system. Uses a deterministic storage slot based on a unique namespace.
*/
library AccessControlStorage {
/// @dev Storage slot location calculated as:
/// keccak256(abi.encode(uint256(keccak256("bae.lending.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
/// This ensures the storage slot is deterministic and collision-resistant in Diamond proxy
bytes32 private constant BaseLoanStorageLocation =
0xfbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed200;
/**
* @notice Storage layout for access control data
* @dev Contains mappings for role assignments using nested mapping pattern
*/
struct Layout {
/// @notice Mapping from account address to role hash to boolean indicating role possession
/// @dev Structure: hasRole[account][roleHash] = true/false
mapping(address => mapping(bytes32 => bool)) hasRole;
}
/**
* @notice Returns a storage pointer to the AccessControl storage layout
* @dev Uses inline assembly to return a storage reference at the predetermined slot
* @return $ Storage pointer to the Layout struct
*/
function layout() internal pure returns (Layout storage $) {
assembly {
$.slot := BaseLoanStorageLocation
}
}
}{
"remappings": [
"@/=src/",
"@delegate-registry/=dependencies/delegate-registry-2.0/src/",
"@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.3.0/",
"@openzeppelin/contracts-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.3.0/",
"@forge-std/=dependencies/forge-std-1.9.7/src/",
"@loans/=src/loans/",
"@test/=test/",
"@openzeppelin-contracts-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/",
"@openzeppelin-contracts-upgradeable-5.3.0/=dependencies/@openzeppelin-contracts-upgradeable-5.3.0/",
"delegate-registry-2.0/=dependencies/delegate-registry-2.0/src/",
"ds-test/=dependencies/delegate-registry-2.0/lib/forge-std/lib/ds-test/src/",
"forge-std-1.9.7/=dependencies/forge-std-1.9.7/src/",
"forge-std/=dependencies/delegate-registry-2.0/lib/forge-std/src/",
"murky/=dependencies/delegate-registry-2.0/lib/murky/src/",
"openzeppelin/=dependencies/delegate-registry-2.0/lib/openzeppelin-contracts/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 20000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"AlreadyWhitelistedContract","type":"error"},{"inputs":[],"name":"InvalidDurationError","type":"error"},{"inputs":[],"name":"NotWhitelistedContract","type":"error"},{"inputs":[{"internalType":"uint256","name":"_pendingProtocolFeeSetTime","type":"uint256"}],"name":"TooEarlyError","type":"error"},{"inputs":[],"name":"TooSoonError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newFlashActionContract","type":"address"}],"name":"FlashActionContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"LiquidationAuctionDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"}],"name":"LiquidationContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_minimum","type":"uint256"}],"name":"MinAprImprovementUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMinBid","type":"uint256"}],"name":"MinBidLiquidationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minLockPeriod","type":"uint256"}],"name":"MinLockPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"indexed":false,"internalType":"struct ProtocolFee","name":"fee","type":"tuple"}],"name":"ProtocolFeePendingUpdate","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"indexed":false,"internalType":"struct ProtocolFee","name":"fee","type":"tuple"}],"name":"ProtocolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractAdded","type":"address"}],"name":"WhitelistedCallbackContractAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractRemoved","type":"address"}],"name":"WhitelistedCallbackContractRemoved","type":"event"},{"inputs":[],"name":"MAX_AUCTION_DURATION","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_AUCTION_DURATION","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contract_","type":"address"}],"name":"addWhitelistedCallbackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"account_","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contract_","type":"address"}],"name":"removeWhitelistedCallbackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"account_","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFlashActionContract_","type":"address"}],"name":"setFlashActionContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minLockPeriod_","type":"uint256"}],"name":"setMinLockPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner_","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"liquidationAuctionDuration_","type":"uint48"}],"name":"updateLiquidationAuctionDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"loanLiquidator_","type":"address"}],"name":"updateLiquidationContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minImprovementApr_","type":"uint256"}],"name":"updateMinImprovementApr","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct ProtocolFee","name":"protocolFee_","type":"tuple"}],"name":"updateProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60808060405234601557610f64908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80632f2ff15d14610c6b57806336cb351b14610c005780633fb3517814610be35780634fd930ba14610a9a5780635122afb1146109555780636ccc9dde146108815780637320ca261461081657806373b99f10146105ac578063b97e527a146104a4578063c2f50a7a14610487578063d547741f146103ca578063da8d76b514610276578063db540a8a146101ab5763f2fde38b146100b3575f80fd5b346101a75760206003193601126101a7576100cc610d4e565b6100d4610e93565b6100dd81610e4e565b73ffffffffffffffffffffffffffffffffffffffff807fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f5416911690817fffffffffffffffffffffffff00000000000000000000000000000000000000007fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f5416177fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b5f80fd5b346101a75760206003193601126101a7577faed358a2bd4ca37fb72362cad5ae66b0b49a2a2031f9474a656e47b1f4afc419602073ffffffffffffffffffffffffffffffffffffffff6101fc610d4e565b610204610dde565b16807fffffffffffffffffffffffff00000000000000000000000000000000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d20065416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200655604051908152a1005b346101a75760206003193601126101a75761028f610d4e565b610297610dde565b6102a081610e4e565b60ff6102e98273ffffffffffffffffffffffffffffffffffffffff165f527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200d60205260405f2090565b54166103a25760208161035a7f6a6b10ec403319c89f7cc46cb63debaa4dd4b26822f357ca8f43e7a05059a69b9373ffffffffffffffffffffffffffffffffffffffff165f527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200d60205260405f2090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff60405191168152a1005b7f9ad3410b000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101a75760406003193601126101a75760043573ffffffffffffffffffffffffffffffffffffffff6103fb610d2b565b610403610d94565b16805f527ffbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed20060205260405f20825f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4005b346101a7575f6003193601126101a75760206040516203f4808152f35b346101a75760206003193601126101a75760043565ffffffffffff81168091036101a7576104d0610dde565b6203f4808110158061059f575b15610577576020817f9b0306e96c09148e30f9acd9a1ebc2c7cb1bc0348adbf320530ead875f78292c927fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200c5416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200c55604051908152a1005b7fa922f92c000000000000000000000000000000000000000000000000000000005f5260045ffd5b5062093a808111156104dd565b346101a7575f6003193601126101a7577f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200b5442106107ee576040516040810181811067ffffffffffffffff8211176107c1577fb3c1d38dbdc9199d0ce01f386d70e29014ed4af7af1d321ca6641a91f4b4dc0c91604091825273ffffffffffffffffffffffffffffffffffffffff7f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200954168082527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200a5460208301918183527fffffffffffffffffffffffff00000000000000000000000000000000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d20075416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2007557f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2008555f7f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2009555f7f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200a557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200b5573ffffffffffffffffffffffffffffffffffffffff83519251168252516020820152a1005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fa8a3a9dc000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101a75760206003193601126101a7577fe818ff6972bf8970ca14a893539e57452588b197b76e6afe29c40a31b3363c616020600435610855610dde565b807f847582c39cc3b2c67c7a5fe5a43704b4d7ff45ccf56777815a28a282cadb580255604051908152a1005b346101a75760206003193601126101a7577f0656dc0fa3e59bca53331c14b87a4778ffac8c184915152fdd0f103838a4f378602073ffffffffffffffffffffffffffffffffffffffff6108d2610d4e565b6108da610dde565b6108e381610e4e565b16807fffffffffffffffffffffffff00000000000000000000000000000000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d20025416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200255604051908152a1005b346101a75760406003193601126101a75761096e610dde565b61097e610979610d71565b610e4e565b73ffffffffffffffffffffffffffffffffffffffff61099b610d71565b167fffffffffffffffffffffffff00000000000000000000000000000000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d20095416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2009557f160fef22fef07b45037a807beef2c89408a81168d1055cf34024c85396b882af6040602435807f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200a55427f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200b5581519073ffffffffffffffffffffffffffffffffffffffff610a8f610d4e565b1682526020820152a1005b346101a75760206003193601126101a757610ab3610d4e565b610abb610dde565b60ff610b048273ffffffffffffffffffffffffffffffffffffffff165f527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200d60205260405f2090565b541615610bbb57602081610b767f924773930b2679ab8bf328330b211bedfb8f917551fd856f536bec008d17f9a69373ffffffffffffffffffffffffffffffffffffffff165f527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200d60205260405f2090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff60405191168152a1005b7f178eb9d9000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101a7575f6003193601126101a757602060405162093a808152f35b346101a75760206003193601126101a7577f257d95d6b6cd5a1a1e56bddff8487147cadd06fce4618b2197b174ccc96342016020600435610c3f610dde565b807f847582c39cc3b2c67c7a5fe5a43704b4d7ff45ccf56777815a28a282cadb580355604051908152a1005b346101a75760406003193601126101a75760043573ffffffffffffffffffffffffffffffffffffffff610c9c610d2b565b610ca4610d94565b16805f527ffbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed20060205260405f20825f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4005b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101a757565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101a757565b60043573ffffffffffffffffffffffffffffffffffffffff811681036101a75790565b335f527ffbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed20060205260405f205f805260205260ff60405f20541615610dd457565b610ddc610e93565b565b335f527ffbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed20060205260405f205f805260205260ff60405f20541615610e1e57565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004525f60245260445ffd5b73ffffffffffffffffffffffffffffffffffffffff1615610e6b57565b7f9fabe1c1000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f54163303610ed357565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f4c69624469616d6f6e643a204d75737420626520636f6e7472616374206f776e60448201527f65720000000000000000000000000000000000000000000000000000000000006064820152fdfea164736f6c634300081c000a
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c80632f2ff15d14610c6b57806336cb351b14610c005780633fb3517814610be35780634fd930ba14610a9a5780635122afb1146109555780636ccc9dde146108815780637320ca261461081657806373b99f10146105ac578063b97e527a146104a4578063c2f50a7a14610487578063d547741f146103ca578063da8d76b514610276578063db540a8a146101ab5763f2fde38b146100b3575f80fd5b346101a75760206003193601126101a7576100cc610d4e565b6100d4610e93565b6100dd81610e4e565b73ffffffffffffffffffffffffffffffffffffffff807fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f5416911690817fffffffffffffffffffffffff00000000000000000000000000000000000000007fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f5416177fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b5f80fd5b346101a75760206003193601126101a7577faed358a2bd4ca37fb72362cad5ae66b0b49a2a2031f9474a656e47b1f4afc419602073ffffffffffffffffffffffffffffffffffffffff6101fc610d4e565b610204610dde565b16807fffffffffffffffffffffffff00000000000000000000000000000000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d20065416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200655604051908152a1005b346101a75760206003193601126101a75761028f610d4e565b610297610dde565b6102a081610e4e565b60ff6102e98273ffffffffffffffffffffffffffffffffffffffff165f527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200d60205260405f2090565b54166103a25760208161035a7f6a6b10ec403319c89f7cc46cb63debaa4dd4b26822f357ca8f43e7a05059a69b9373ffffffffffffffffffffffffffffffffffffffff165f527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200d60205260405f2090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff60405191168152a1005b7f9ad3410b000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101a75760406003193601126101a75760043573ffffffffffffffffffffffffffffffffffffffff6103fb610d2b565b610403610d94565b16805f527ffbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed20060205260405f20825f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4005b346101a7575f6003193601126101a75760206040516203f4808152f35b346101a75760206003193601126101a75760043565ffffffffffff81168091036101a7576104d0610dde565b6203f4808110158061059f575b15610577576020817f9b0306e96c09148e30f9acd9a1ebc2c7cb1bc0348adbf320530ead875f78292c927fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200c5416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200c55604051908152a1005b7fa922f92c000000000000000000000000000000000000000000000000000000005f5260045ffd5b5062093a808111156104dd565b346101a7575f6003193601126101a7577f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200b5442106107ee576040516040810181811067ffffffffffffffff8211176107c1577fb3c1d38dbdc9199d0ce01f386d70e29014ed4af7af1d321ca6641a91f4b4dc0c91604091825273ffffffffffffffffffffffffffffffffffffffff7f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200954168082527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200a5460208301918183527fffffffffffffffffffffffff00000000000000000000000000000000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d20075416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2007557f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2008555f7f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2009555f7f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200a557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200b5573ffffffffffffffffffffffffffffffffffffffff83519251168252516020820152a1005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fa8a3a9dc000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101a75760206003193601126101a7577fe818ff6972bf8970ca14a893539e57452588b197b76e6afe29c40a31b3363c616020600435610855610dde565b807f847582c39cc3b2c67c7a5fe5a43704b4d7ff45ccf56777815a28a282cadb580255604051908152a1005b346101a75760206003193601126101a7577f0656dc0fa3e59bca53331c14b87a4778ffac8c184915152fdd0f103838a4f378602073ffffffffffffffffffffffffffffffffffffffff6108d2610d4e565b6108da610dde565b6108e381610e4e565b16807fffffffffffffffffffffffff00000000000000000000000000000000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d20025416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200255604051908152a1005b346101a75760406003193601126101a75761096e610dde565b61097e610979610d71565b610e4e565b73ffffffffffffffffffffffffffffffffffffffff61099b610d71565b167fffffffffffffffffffffffff00000000000000000000000000000000000000007f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d20095416177f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d2009557f160fef22fef07b45037a807beef2c89408a81168d1055cf34024c85396b882af6040602435807f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200a55427f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200b5581519073ffffffffffffffffffffffffffffffffffffffff610a8f610d4e565b1682526020820152a1005b346101a75760206003193601126101a757610ab3610d4e565b610abb610dde565b60ff610b048273ffffffffffffffffffffffffffffffffffffffff165f527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200d60205260405f2090565b541615610bbb57602081610b767f924773930b2679ab8bf328330b211bedfb8f917551fd856f536bec008d17f9a69373ffffffffffffffffffffffffffffffffffffffff165f527f014188aa47349e14521c6215ea0fc8aaca8692b6e279bfc01a7282e69c9d200d60205260405f2090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff60405191168152a1005b7f178eb9d9000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101a7575f6003193601126101a757602060405162093a808152f35b346101a75760206003193601126101a7577f257d95d6b6cd5a1a1e56bddff8487147cadd06fce4618b2197b174ccc96342016020600435610c3f610dde565b807f847582c39cc3b2c67c7a5fe5a43704b4d7ff45ccf56777815a28a282cadb580355604051908152a1005b346101a75760406003193601126101a75760043573ffffffffffffffffffffffffffffffffffffffff610c9c610d2b565b610ca4610d94565b16805f527ffbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed20060205260405f20825f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4005b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101a757565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101a757565b60043573ffffffffffffffffffffffffffffffffffffffff811681036101a75790565b335f527ffbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed20060205260405f205f805260205260ff60405f20541615610dd457565b610ddc610e93565b565b335f527ffbd26770be56a82eb749ee8a16f2d01dbfcdba8e3efdf2a04de27a0b60aed20060205260405f205f805260205260ff60405f20541615610e1e57565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004525f60245260445ffd5b73ffffffffffffffffffffffffffffffffffffffff1615610e6b57565b7f9fabe1c1000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131f54163303610ed357565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f4c69624469616d6f6e643a204d75737420626520636f6e7472616374206f776e60448201527f65720000000000000000000000000000000000000000000000000000000000006064820152fdfea164736f6c634300081c000a
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in HYPE
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.