Manager Contract
The Manager.sol
contract serves as the central coordination layer for the entire Worldbook ecosystem. It implements sophisticated access control, fee management, orderbook registration, and emergency response capabilities.
Contract Overview
File:
Manager.sol
Size: ~900 lines of code
Role: Central coordination and control layer
Security: Role-based access control with inheritance hierarchy
Key Responsibilities
1. Access Control System
Role Management: Hierarchical role system with inheritance
Permission Delegation: Centralized permission checking for all OrderBooks
Emergency Controls: Pause/unpause capabilities across the entire system
2. OrderBook Registry
Registration: Validates and registers new OrderBook contracts
Bytecode Verification: Ensures only legitimate contracts are registered
Pair Management: Manages unique trading pairs and prevents duplicates
3. Fee Management
4-Tier Fee System: Sophisticated fee structure with customization options
Maker Rebates: Support for negative maker fees (rebates)
Fee Collection: Centralized fee collection and distribution
User Discounts: Percentage and absolute discount mechanisms
4. Token Whitelist and Standard Token Registry
Quote Token Control: Manages which tokens can be used as quote assets
Minimum Amounts: Sets minimum order sizes per token
Standard Tokens: Registry for exact-transfer ERC-20s and optional enforcement for BASE on registration
Dynamic Management: Add/remove tokens from whitelist
5. Self-Trade Prevention (STP)
User STP Modes: Configurable self-trade prevention behavior
Three Modes: NONE, EXPIRE_MAKER, and SKIP
Default Mode: EXPIRE_MAKER for new users
Access Control Architecture
Role Hierarchy
DEFAULT_ADMIN_ROLE (Role Management Only)
│
▼
ADMIN_ROLE (Full Operational Control + PAUSER privileges)
│
▼
PAUSER_ROLE (Emergency Response)
Important: ADMIN_ROLE automatically has PAUSER_ROLE privileges through the hasOrderBookRole
function implementation. The Manager
contract itself also holds PAUSER_ROLE
to perform batch pause/unpause.
Role Definitions
// Role constants
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant FACTORY_ROLE = keccak256("FACTORY_ROLE");
// Role checking function used by all OrderBooks with inheritance
function hasOrderBookRole(bytes32 role, address account) external view returns (bool) {
// Direct role check
if (hasRole(role, account)) {
return true;
}
// Implement role inheritance: ADMIN_ROLE includes PAUSER_ROLE privileges
if (role == PAUSER_ROLE && hasRole(ADMIN_ROLE, account)) {
return true;
}
return false;
}
Role Capabilities
DEFAULT_ADMIN_ROLE:
Grant and revoke ADMIN_ROLE
Grant and revoke PAUSER_ROLE
Role management only (no operational privileges)
ADMIN_ROLE:
All operational functions
Fee management
OrderBook registration control
Token whitelist management
System configuration
Inherits PAUSER_ROLE privileges
PAUSER_ROLE:
Emergency pause/unpause operations
Community managers and emergency responders
Limited to emergency response only
Fee Management System
4-Tier Fee Structure
Worldbook implements a sophisticated fee system with four layers:
Layer 1: Default Fees
int256 public override makerFee; // Can be negative for rebates
uint256 public override takerFee; // Always non-negative
Maker Rebates:
Maker fees can be negative to incentivize liquidity provision
Rebates are funded from taker fees
Maximum rebate: 0.05% (500 basis points, different from max fee of 0.5%)
Layer 2: OrderBook-Specific Overrides
mapping(address => int256) public orderbookMakerFeeOverrides;
mapping(address => uint256) public orderbookTakerFeeOverrides;
Layer 3: User Percentage Discounts
mapping(address => uint256) public userMakerFeeDiscountFactor;
mapping(address => uint256) public userTakerFeeDiscountFactor;
Layer 4: User Absolute Discounts
mapping(address => uint256) public userMakerFeeDiscountAbs;
mapping(address => uint256) public userTakerFeeDiscountAbs;
Fee Calculation Logic
// Fee calculation with rebate support
function _calculateMakerFeeRate(address orderbook, address user) internal view returns (int256) {
// Step 1: Get base rate (orderbook override OR default)
int256 baseRate = orderbookMakerFeeOverrides[orderbook] != 0 ?
orderbookMakerFeeOverrides[orderbook] :
makerFee;
// Step 2: Apply percentage discount ONLY if base rate is positive
int256 afterPercentageDiscount = baseRate;
if (baseRate > 0) {
uint256 discountFactor = userMakerFeeDiscountFactor[user];
if (discountFactor > 0 && discountFactor < 10000000) {
afterPercentageDiscount = int256(uint256(baseRate) * discountFactor / FEE_DENOM);
}
}
// Step 3: Apply absolute discount (can turn positive fees into rebates)
uint256 absoluteDiscount = userMakerFeeDiscountAbs[user];
return afterPercentageDiscount - int256(absoluteDiscount);
}
Fee Collection
address public override feeCollector;
function setFeeCollector(address _feeCollector) external override onlyAdminRole {
if (_feeCollector == address(0)) revert Manager__InvalidFeeCollector();
if (_feeCollector != feeCollector) {
address oldFeeCollector = feeCollector;
feeCollector = _feeCollector;
emit FeeCollectorUpdated(oldFeeCollector, _feeCollector);
}
}
OrderBook Registration System
Registration Process
function registerOrderBook(
address baseToken,
address quoteToken,
address orderBookAddress
) external payable virtual override
Validation Steps:
Access Control: If
allowAnyoneRegisterOrderBook
is false, only ADMIN_ROLE may register. Callers withFACTORY_ROLE
bypass bytecode checks.Registration Fee: Require payment of
registrationFee
(admins exempt)Input Validation: Verify token addresses and uniqueness
Quote Token Check: Ensure quote token is whitelisted (if active)
Bytecode Verification: Verify contract matches expected bytecode (exact or normalized hash)
Parameter Verification: Validate OrderBook configuration
Storage: Store registration and emit event
Fee Transfer: Send fee to collector, refund excess
Registration Fee System
The Manager supports optional registration fees for OrderBook creation:
uint256 public registrationFee = 0.001 ether; // Default: 0.001 ETH
function setRegistrationFee(uint256 _fee) external onlyAdminRole
Key Features:
Admin role is exempt from registration fees
Fees are sent directly to the fee collector
Excess payment is automatically refunded
Can be set to 0 to disable fees
Prevents spam OrderBook creation
Bytecode Verification
bytes32 public override expectedBytecodeHash;
function setExpectedBytecodeHash(bytes32 _newHash) external override onlyAdminRole {
if (_newHash == bytes32(0)) revert Manager__ZeroBytecodeHash();
if (_newHash != expectedBytecodeHash) {
bytes32 oldHash = expectedBytecodeHash;
expectedBytecodeHash = _newHash;
emit ExpectedBytecodeHashSet(oldHash, _newHash);
}
}
Security Benefits:
Prevents registration of malicious contracts
Ensures only approved OrderBook versions are used
Maintains system integrity and user trust
Normalized Bytecode Verification
To support OrderBooks with different immutable constructor parameters (manager/base/quote addresses, decimals, scales), the Manager can verify a canonical normalized runtime hash:
bytes32 public override expectedNormalizedBytecodeHash;
function setExpectedNormalizedBytecodeHash(bytes32 _newHash) external onlyAdminRole {
if (_newHash == bytes32(0)) revert Manager__ZeroBytecodeHash();
if (_newHash != expectedNormalizedBytecodeHash) {
bytes32 oldHash = expectedNormalizedBytecodeHash;
expectedNormalizedBytecodeHash = _newHash;
emit ExpectedNormalizedBytecodeHashSet(oldHash, _newHash);
}
}
// Optional fast-path: zero-out configured [offset,length] windows before hashing
function setNormalizationMask(uint32[] calldata offsets, uint32[] calldata lengths) external onlyAdminRole;
// Tooling helper
function computeNormalizedRuntimeHash(address orderBook) external view returns (bytes32);
If the caller has FACTORY_ROLE
, bytecode checks are skipped to allow trusted factories to register books.
Normalized Bytecode Verification
To support OrderBooks with different immutable constructor parameters (manager/base/quote addresses, decimals, scales), the Manager can verify a canonical normalized runtime hash:
bytes32 public override expectedNormalizedBytecodeHash;
function setExpectedNormalizedBytecodeHash(bytes32 _newHash) external onlyAdminRole {
if (_newHash == bytes32(0)) revert Manager__ZeroBytecodeHash();
if (_newHash != expectedNormalizedBytecodeHash) {
bytes32 oldHash = expectedNormalizedBytecodeHash;
expectedNormalizedBytecodeHash = _newHash;
emit ExpectedNormalizedBytecodeHashSet(oldHash, _newHash);
}
}
// Optional fast-path: zero-out configured [offset,length] windows before hashing
function setNormalizationMask(uint32[] calldata offsets, uint32[] calldata lengths) external onlyAdminRole;
// Tooling helper
function computeNormalizedRuntimeHash(address orderBook) external view returns (bytes32);
If the caller has FACTORY_ROLE
, bytecode checks are skipped to allow trusted factories to register books.
Pair Management
mapping(bytes32 => address) public override orderBooks;
address[] public allOrderBooks;
function getPairHash(address baseToken, address quoteToken) public pure returns (bytes32) {
// Deterministic hash regardless of token order
(address token0, address token1) = baseToken < quoteToken ?
(baseToken, quoteToken) : (quoteToken, baseToken);
return keccak256(abi.encodePacked(token0, token1));
}
// Append-only per-pair index (latest registered remains the canonical pointer above)
function getPairOrderBooks(address tokenA, address tokenB) external view returns (address[] memory);
// Append-only per-pair index (latest registered remains the canonical pointer above)
function getPairOrderBooks(address tokenA, address tokenB) external view returns (address[] memory);
Quote Token Whitelist System
Whitelist Control
bool public override isQuoteWhitelistActive;
mapping(address => bool) public override isQuoteTokenWhitelisted;
mapping(address => uint256) public override quoteTokenMinAmounts;
Whitelist Management
function addOrUpdateQuoteToken(address _token, uint256 _minQuoteAmount) external override onlyAdminRole {
if (_token == address(0)) revert Manager__ZeroAddressToken();
bool wasWhitelisted = isQuoteTokenWhitelisted[_token];
uint256 oldMinAmount = quoteTokenMinAmounts[_token];
if (!wasWhitelisted || oldMinAmount != _minQuoteAmount) {
isQuoteTokenWhitelisted[_token] = true;
quoteTokenMinAmounts[_token] = _minQuoteAmount;
if (!wasWhitelisted) {
allWhitelistedQuoteTokens.push(_token);
quoteTokenToIndex[_token] = allWhitelistedQuoteTokens.length - 1;
emit QuoteTokenWhitelisted(_token, _minQuoteAmount);
} else {
emit QuoteTokenMinAmountUpdated(_token, oldMinAmount, _minQuoteAmount);
}
}
}
Emergency Controls
System-Wide Pause
function pauseAllOrderBooks() external onlyPauserRole returns (uint256[] memory pausedIndices) {
uint256 length = allOrderBooks.length;
uint256[] memory tempIndices = new uint256[](length);
uint256 pausedCount = 0;
for (uint256 i = 0; i < length; i++) {
try IOrderBook(allOrderBooks[i]).pause() {
tempIndices[pausedCount] = i;
pausedCount++;
} catch {
// Continue even if individual pause fails
}
}
// Return successfully paused indices
pausedIndices = new uint256[](pausedCount);
for (uint256 i = 0; i < pausedCount; i++) {
pausedIndices[i] = tempIndices[i];
}
emit AllOrderBooksPaused(msg.sender, pausedIndices);
return pausedIndices;
}
System-Wide Unpause
function unpauseAllOrderBooks() external onlyPauserRole returns (uint256[] memory unpausedIndices) {
// Similar implementation for unpausing all OrderBooks
}
Batch Pause/Unpause Operations
For more granular control and gas efficiency when dealing with many OrderBooks:
function pauseOrderBooksBatch(uint256 startIndex, uint256 count)
external
onlyPauserRole
returns (address[] memory pausedAddresses)
function unpauseOrderBooksBatch(uint256 startIndex, uint256 count)
external
onlyPauserRole
returns (address[] memory unpausedAddresses)
Batch Operation Benefits:
Avoids gas limits when pausing/unpausing many OrderBooks
Provides pagination for large-scale operations
Returns specific addresses that were successfully affected
Enables targeted emergency responses
Continues operation even if individual OrderBooks fail to pause/unpause
Usage Example:
// Pause OrderBooks 0-99
pauseOrderBooksBatch(0, 100);
// Pause OrderBooks 100-199
pauseOrderBooksBatch(100, 100);
// Returns array of successfully paused addresses
Pauser Management
Admins can manage who has emergency pause privileges:
function addPauser(address account) external {
// Only ADMIN_ROLE can add pausers
if (!this.hasOrderBookRole(ADMIN_ROLE, msg.sender)) {
revert AccessControlUnauthorizedAccount(msg.sender, ADMIN_ROLE);
}
_grantRole(PAUSER_ROLE, account);
}
function removePauser(address account) external {
// Only ADMIN_ROLE can remove pausers
if (!this.hasOrderBookRole(ADMIN_ROLE, msg.sender)) {
revert AccessControlUnauthorizedAccount(msg.sender, ADMIN_ROLE);
}
_revokeRole(PAUSER_ROLE, account);
}
Emergency Use Cases:
Critical security vulnerabilities discovered
Market manipulation attempts
Extreme market conditions
Regulatory compliance requirements
Configuration Functions
Fee Configuration
function setFees(int256 _makerFee, uint256 _takerFee) external override onlyAdminRole {
// Validate taker fee
if (_takerFee > MAX_FEE) revert Manager__FeeTooHigh();
// Validate maker fee (different limits for fees vs rebates)
if (_makerFee < 0) {
// For rebates: limit to MAX_REBATE (0.05%)
if (uint256(-_makerFee) > MAX_REBATE) revert Manager__FeeTooHigh();
} else {
// For positive fees: limit to MAX_FEE (0.5%)
if (uint256(_makerFee) > MAX_FEE) revert Manager__FeeTooHigh();
}
if (makerFee != _makerFee || takerFee != _takerFee) {
int256 oldMakerFee = makerFee;
uint256 oldTakerFee = takerFee;
makerFee = _makerFee;
takerFee = _takerFee;
emit FeesUpdated(oldMakerFee, _makerFee, oldTakerFee, _takerFee);
}
}
Registration Control
function setAllowAnyoneRegisterOrderBook(bool _allowed) external override onlyAdminRole {
if (_allowed != allowAnyoneRegisterOrderBook) {
allowAnyoneRegisterOrderBook = _allowed;
emit AllowAnyoneRegisterOrderBookSet(_allowed);
}
}
Events
Registration Events
event OrderBookRegistered(
bytes32 indexed pairHash,
address indexed baseToken,
address indexed quoteToken,
address orderBookAddress
);
event ExpectedBytecodeHashSet(bytes32 oldHash, bytes32 newHash);
event ExpectedNormalizedBytecodeHashSet(bytes32 oldHash, bytes32 newHash);
event AllowAnyoneRegisterOrderBookSet(bool allowed);
Fee Events
event FeesUpdated(
int256 oldMakerFee,
int256 newMakerFee,
uint256 oldTakerFee,
uint256 newTakerFee
);
event FeeCollectorUpdated(
address indexed oldCollector,
address indexed newCollector
);
Emergency Events
event AllOrderBooksPaused(
address indexed pauser,
uint256[] pausedIndices
);
event AllOrderBooksUnpaused(
address indexed unpauser,
uint256[] unpausedIndices
);
event OrderBooksPausedBatch(
address indexed pauser,
uint256 startIndex,
uint256 endIndex,
address[] pausedAddresses
);
event OrderBooksUnpausedBatch(
address indexed unpauser,
uint256 startIndex,
uint256 endIndex,
address[] unpausedAddresses
);
Security Features
Input Validation
Zero address checks for all critical parameters
Fee rate bounds checking (MAX_FEE = 0.5%, MAX_REBATE = 0.05%)
Bytecode hash validation
Token pair uniqueness enforcement
Access Control
Role-based permissions with inheritance
Centralized role checking for all OrderBooks
Emergency response capabilities
Emergency Response
System-wide pause/unpause functionality
Graceful error handling (continues if individual OrderBook fails)
Event emission for transparency
Standard Token Registry and Token Denylist
Standard ERC-20 Registry
function setStandardToken(address token, bool isStandard) external onlyAdminRole;
function isStandardToken(address token) external view returns (bool);
function setEnforceStandardBaseOnRegister(bool enforce) external onlyAdminRole;
When enforcement is enabled,
registerOrderBook
requires the BASE token to be marked standard.“Standard” means exact-transfer, non-rebasing ERC-20 for gas-fast paths.
Emergency Token Denylist
function setTokenDenied(address token, bool denied) external onlyAdminRole;
function isTokenDenied(address token) external view returns (bool);
Denied tokens cannot be used in new OrderBooks (applies to both base and quote), regardless of whitelist status.
Self-Trade Prevention (STP)
STP Modes
The Manager contract supports three STP modes for preventing self-trading:
enum STPMode {
NONE, // No self-trade prevention
EXPIRE_MAKER, // Cancel maker order when self-trade detected (default)
SKIP // Skip maker order without matching
}
STP Configuration
// Set user's STP mode
function setUserSTPMode(STPMode mode) external override {
userSTPMode[msg.sender] = mode;
_isStpModeExplicitlySet[msg.sender] = true;
}
// Get user's STP mode (defaults to EXPIRE_MAKER)
function getUserSTPMode(address user) external view override returns (STPMode) {
if (!_isStpModeExplicitlySet[user]) {
return STPMode.EXPIRE_MAKER;
}
return userSTPMode[user];
}
STP Behavior
NONE: Self-trades are allowed and executed normally
EXPIRE_MAKER: When self-trade is detected:
Maker order is automatically cancelled
Collateral is refunded to the maker
Taker order continues to match with other orders
SKIP: When self-trade is detected:
Maker order is skipped (remains active)
Taker order continues to match with other orders
Integration Points
OrderBook Integration
// OrderBooks call this for role checking
function hasOrderBookRole(bytes32 role, address account) external view returns (bool);
// OrderBooks call this for fee calculation
function getMakerFeeRate(address orderbook, address user) external view returns (int256);
function getTakerFeeRate(address orderbook, address user) external view returns (uint256);
// OrderBooks call this for STP mode checking
function getUserSTPMode(address user) external view returns (STPMode);
Frontend Integration
Event monitoring for system changes
Role-based UI elements
Real-time fee calculation
Emergency status indicators
The Manager contract provides the foundational infrastructure for secure, scalable, and maintainable decentralized exchange operations while maintaining flexibility for future enhancements and community governance.
Last updated