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

  • Quote Token Control: Manages which tokens can be used as quote assets

  • Minimum Amounts: Sets minimum order sizes per token

  • 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_ROLE (Emergency Response)

Role Definitions

// Role constants
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

// Role checking function used by all OrderBooks
function hasOrderBookRole(bytes32 role, address account) external view returns (bool) {
    return hasRole(role, account);
}

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

  • Rebates cannot exceed the corresponding taker fee amount

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 virtual override

Validation Steps:

  1. Access Control: Check if caller has permission

  2. Input Validation: Verify token addresses and uniqueness

  3. Quote Token Check: Ensure quote token is whitelisted (if active)

  4. Bytecode Verification: Verify contract matches expected bytecode

  5. Parameter Verification: Validate OrderBook configuration

  6. Storage: Store registration and emit event

Bytecode Verification

bytes32 public override expectedBytecodeHash;

function setExpectedBytecodeHash(bytes32 _newHash) external override onlyAdminRole {
    if (_newHash == bytes32(0)) revert Manager__ZeroBytecodeHash();
    if (_newHash != expectedBytecodeHash) {
        expectedBytecodeHash = _newHash;
        emit ExpectedBytecodeHashSet(_newHash);
    }
}

Security Benefits:

  • Prevents registration of malicious contracts

  • Ensures only approved OrderBook versions are used

  • Maintains system integrity and user trust

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));
}

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) {
    uint256 length = allOrderBooks.length;
    if (startIndex >= length) {
        return new address[](0);
    }
    
    uint256 endIndex = startIndex + count;
    if (endIndex > length) {
        endIndex = length;
    }
    
    // Pause OrderBooks in the specified range
    // Returns addresses of successfully paused OrderBooks
}

function unpauseOrderBooksBatch(uint256 startIndex, uint256 count) 
    external 
    onlyPauserRole 
    returns (address[] memory unpausedAddresses) {
    // Similar implementation for batch unpausing
}

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

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 (can be negative for rebates)
    if (_makerFee < 0) {
        if (uint256(-_makerFee) > MAX_FEE) revert Manager__FeeTooHigh();
    } else {
        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 indexed 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%)

  • 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

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