Robinhood MainnetChain ID: 4663
Contract detail

ExchangeCalendarRouter

ExchangeCalendarRouter is a verified Robinhood Chain smart contract at 0xa8ad9f2b…cbcec2dd with 0 indexed transactions.

Address type
Smart contract
Transactions
0
History coverage
No indexed activity found
Verification
Verified
Compiler
v0.8.33+commit.64118f21
Verification match
full
Creation transaction
0x5ede45d1…749c2bb2
Created at block
#28615776
Language
solidity
EVM version
prague
License
none
Compiler optimization
Enabled · 200 runs
Verified contract

Contract source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {Ownable} from "solady/auth/Ownable.sol";
import {IExchangeCalendar} from "./interfaces/IExchangeCalendar.sol";
import {IExchangeCalendarMetadata} from "./interfaces/IExchangeCalendarMetadata.sol";

/// @notice Stable calendar address with delayed, metadata-verified implementation replacement.
contract ExchangeCalendarRouter is Ownable, IExchangeCalendar {
    IExchangeCalendar public activeCalendar;
    IExchangeCalendar public pendingCalendar;
    bytes32 public pendingCalendarCodeHash;
    bytes32 public pendingCalendarConfigurationHash;
    uint16 public pendingCalendarFirstHolidayYear;
    uint16 public pendingCalendarHolidayCoverage;
    uint64 public pendingCalendarDstCoverageStart;
    uint64 public pendingCalendarDstCoverageEnd;

    uint16 public immutable expectedConfiguredVenueCount;
    uint16 public immutable expectedActiveVenueCount;
    uint16 public immutable expectedDstObservedVenueCount;
    bytes32 public immutable expectedActiveVenueSetHash;
    uint16 public immutable expectedFirstCompleteHolidayYear;
    uint64 public immutable replacementDelay;
    uint64 public activateAfter;
    bool public replacementLocked;

    event CalendarReplacementScheduled(
        address indexed currentCalendar,
        address indexed nextCalendar,
        bytes32 indexed codeHash,
        bytes32 configurationHash,
        uint16 latestCompleteHolidayYear,
        uint64 activateAfter
    );
    event CalendarReplaced(
        address indexed previousCalendar, address indexed nextCalendar, bytes32 indexed configurationHash
    );
    event CalendarReplacementCancelled(address indexed cancelledCalendar);
    event CalendarReplacementLocked(address indexed calendar);

    error ZeroAddress();
    error InvalidCalendar();
    error CalendarConfigurationIncomplete();
    error CalendarVenueCountsChanged();
    error CalendarVenueSetChanged();
    error CalendarHolidayCoverageRegressed();
    error CalendarHolidayCoverageStartChanged();
    error CalendarDstCoverageRegressed();
    error ReplacementNotReady();
    error ReplacementIsLocked();
    error CalendarCodeChanged();
    error CalendarConfigurationChanged();
    error ReplacementMustBeLockedBeforeRenunciation();

    struct MetadataSnapshot {
        uint16 configuredCount;
        uint16 activeCount;
        uint16 dstObservedCount;
        bytes32 activeSetHash;
        uint16 firstHolidayYear;
        uint16 latestHolidayYear;
        uint64 dstStart;
        uint64 dstEnd;
        bytes32 configurationHash;
    }

    constructor(address initialCalendar, address initialOwner, uint64 replacementDelay_) {
        if (initialCalendar == address(0) || initialOwner == address(0)) revert ZeroAddress();
        if (initialCalendar.code.length == 0) revert InvalidCalendar();
        _initializeOwner(initialOwner);

        MetadataSnapshot memory initial = _validatedMetadata(initialCalendar);
        activeCalendar = IExchangeCalendar(initialCalendar);
        expectedConfiguredVenueCount = initial.configuredCount;
        expectedActiveVenueCount = initial.activeCount;
        expectedDstObservedVenueCount = initial.dstObservedCount;
        expectedActiveVenueSetHash = initial.activeSetHash;
        expectedFirstCompleteHolidayYear = initial.firstHolidayYear;
        replacementDelay = replacementDelay_;
    }

    function scheduleReplacement(address nextCalendar) external onlyOwner {
        if (replacementLocked) revert ReplacementIsLocked();
        if (nextCalendar == address(0) || nextCalendar.code.length == 0) revert InvalidCalendar();

        MetadataSnapshot memory next = _validatedMetadata(nextCalendar);
        _requireCompatible(next);
        IExchangeCalendarMetadata current = IExchangeCalendarMetadata(address(activeCalendar));
        if (next.latestHolidayYear < current.latestCompleteHolidayYear()) {
            revert CalendarHolidayCoverageRegressed();
        }
        if (next.dstStart > current.dstCoverageStart()) revert CalendarDstCoverageRegressed();
        if (next.dstEnd < current.dstCoverageEnd()) revert CalendarDstCoverageRegressed();

        pendingCalendar = IExchangeCalendar(nextCalendar);
        pendingCalendarCodeHash = nextCalendar.codehash;
        pendingCalendarConfigurationHash = next.configurationHash;
        pendingCalendarFirstHolidayYear = next.firstHolidayYear;
        pendingCalendarHolidayCoverage = next.latestHolidayYear;
        pendingCalendarDstCoverageStart = next.dstStart;
        pendingCalendarDstCoverageEnd = next.dstEnd;
        activateAfter = uint64(block.timestamp + replacementDelay);
        emit CalendarReplacementScheduled(
            address(activeCalendar),
            nextCalendar,
            pendingCalendarCodeHash,
            next.configurationHash,
            next.latestHolidayYear,
            activateAfter
        );
    }

    function executeReplacement() external {
        if (replacementLocked) revert ReplacementIsLocked();
        IExchangeCalendar next = pendingCalendar;
        address nextAddress = address(next);
        if (nextAddress == address(0) || block.timestamp < activateAfter) revert ReplacementNotReady();
        if (nextAddress.codehash != pendingCalendarCodeHash) revert CalendarCodeChanged();

        MetadataSnapshot memory nextMetadata = _validatedMetadata(nextAddress);
        _requireCompatible(nextMetadata);
        if (
            nextMetadata.firstHolidayYear != pendingCalendarFirstHolidayYear
                || nextMetadata.latestHolidayYear != pendingCalendarHolidayCoverage
                || nextMetadata.dstStart != pendingCalendarDstCoverageStart
                || nextMetadata.dstEnd != pendingCalendarDstCoverageEnd
                || nextMetadata.configurationHash != pendingCalendarConfigurationHash
        ) revert CalendarConfigurationChanged();
        IExchangeCalendarMetadata current = IExchangeCalendarMetadata(address(activeCalendar));
        if (nextMetadata.latestHolidayYear < current.latestCompleteHolidayYear()) {
            revert CalendarHolidayCoverageRegressed();
        }
        if (nextMetadata.dstStart > current.dstCoverageStart()) revert CalendarDstCoverageRegressed();
        if (nextMetadata.dstEnd < current.dstCoverageEnd()) revert CalendarDstCoverageRegressed();

        address previous = address(activeCalendar);
        activeCalendar = next;
        bytes32 acceptedConfigurationHash = pendingCalendarConfigurationHash;
        _clearPending();
        emit CalendarReplaced(previous, nextAddress, acceptedConfigurationHash);
    }

    function cancelReplacement() external onlyOwner {
        address cancelled = address(pendingCalendar);
        _clearPending();
        emit CalendarReplacementCancelled(cancelled);
    }

    function lockReplacement() external onlyOwner {
        if (replacementLocked) revert ReplacementIsLocked();
        replacementLocked = true;
        _clearPending();
        emit CalendarReplacementLocked(address(activeCalendar));
    }

    function renounceOwnership() public payable override onlyOwner {
        if (!replacementLocked) revert ReplacementMustBeLockedBeforeRenunciation();
        super.renounceOwnership();
    }

    function marketState(uint8 exchangeId, uint256 timestamp) external view returns (MarketState memory) {
        return activeCalendar.marketState(exchangeId, timestamp);
    }

    function utcOffsetMinutes(uint8 exchangeId, uint256 timestamp) external view returns (int16, bool) {
        return activeCalendar.utcOffsetMinutes(exchangeId, timestamp);
    }

    function isOpen(uint8 exchangeId, uint256 timestamp) external view returns (bool) {
        return activeCalendar.isOpen(exchangeId, timestamp);
    }

    function isWithinRegularSessionClock(uint8 exchangeId, uint256 timestamp) external view returns (bool) {
        return activeCalendar.isWithinRegularSessionClock(exchangeId, timestamp);
    }

    function isHoliday(uint8 exchangeId, uint256 timestamp) external view returns (bool, bool) {
        return activeCalendar.isHoliday(exchangeId, timestamp);
    }

    function isEmergencyClosure(uint8 exchangeId, uint256 timestamp) external view returns (bool) {
        return activeCalendar.isEmergencyClosure(exchangeId, timestamp);
    }

    function isHolidayYearLocked(uint8 exchangeId, uint16 year) external view returns (bool) {
        return activeCalendar.isHolidayYearLocked(exchangeId, year);
    }

    function activeVenueSetHash() external view returns (bytes32) {
        return expectedActiveVenueSetHash;
    }

    function localDateKey(uint8 exchangeId, uint256 timestamp) external view returns (uint32) {
        return activeCalendar.localDateKey(exchangeId, timestamp);
    }

    function tradingDateKey(uint8 exchangeId, uint256 timestamp) external view returns (uint32) {
        return activeCalendar.tradingDateKey(exchangeId, timestamp);
    }

    function _validatedMetadata(address calendar) internal view returns (MetadataSnapshot memory snapshot) {
        IExchangeCalendarMetadata metadata = IExchangeCalendarMetadata(calendar);
        try metadata.baseHoursLocked() returns (bool locked) {
            if (!locked) revert CalendarConfigurationIncomplete();
        } catch {
            revert InvalidCalendar();
        }
        try metadata.dstScheduleLocked() returns (bool locked) {
            if (!locked) revert CalendarConfigurationIncomplete();
        } catch {
            revert InvalidCalendar();
        }
        try metadata.configuredVenueCount() returns (uint16 value) {
            snapshot.configuredCount = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.activeVenueCount() returns (uint16 value) {
            snapshot.activeCount = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.dstObservedVenueCount() returns (uint16 value) {
            snapshot.dstObservedCount = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.activeVenueSetHash() returns (bytes32 value) {
            snapshot.activeSetHash = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.firstCompleteHolidayYear() returns (uint16 value) {
            snapshot.firstHolidayYear = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.latestCompleteHolidayYear() returns (uint16 value) {
            snapshot.latestHolidayYear = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.dstCoverageStart() returns (uint64 value) {
            snapshot.dstStart = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.dstCoverageEnd() returns (uint64 value) {
            snapshot.dstEnd = value;
        } catch {
            revert InvalidCalendar();
        }
        bytes32 baseHash;
        bytes32 dstHash;
        try metadata.baseDatasetHash() returns (bytes32 value) {
            baseHash = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.dstDatasetHash() returns (bytes32 value) {
            dstHash = value;
        } catch {
            revert InvalidCalendar();
        }
        try metadata.canonicalConfigurationHash() returns (bytes32 value) {
            snapshot.configurationHash = value;
        } catch {
            revert InvalidCalendar();
        }
        if (
            snapshot.configuredCount == 0 || snapshot.activeCount == 0 || snapshot.activeSetHash == bytes32(0)
                || snapshot.firstHolidayYear == 0 || snapshot.latestHolidayYear < snapshot.firstHolidayYear
                || baseHash == bytes32(0) || dstHash == bytes32(0) || snapshot.configurationHash == bytes32(0)
        ) revert CalendarConfigurationIncomplete();
        if (
            snapshot.dstObservedCount != 0
                && (snapshot.dstStart == 0
                    || snapshot.dstEnd <= snapshot.dstStart
                    || block.timestamp < snapshot.dstStart
                    || block.timestamp >= snapshot.dstEnd)
        ) revert CalendarConfigurationIncomplete();
    }

    function _requireCompatible(MetadataSnapshot memory snapshot) internal view {
        if (
            snapshot.configuredCount != expectedConfiguredVenueCount || snapshot.activeCount != expectedActiveVenueCount
                || snapshot.dstObservedCount != expectedDstObservedVenueCount
        ) revert CalendarVenueCountsChanged();
        if (snapshot.activeSetHash != expectedActiveVenueSetHash) revert CalendarVenueSetChanged();
        if (snapshot.firstHolidayYear != expectedFirstCompleteHolidayYear) {
            revert CalendarHolidayCoverageStartChanged();
        }
    }

    function _clearPending() internal {
        delete pendingCalendar;
        pendingCalendarCodeHash = bytes32(0);
        pendingCalendarConfigurationHash = bytes32(0);
        pendingCalendarFirstHolidayYear = 0;
        pendingCalendarHolidayCoverage = 0;
        pendingCalendarDstCoverageStart = 0;
        pendingCalendarDstCoverageEnd = 0;
        activateAfter = 0;
    }
}