Blockchain / Global / Address
Contract
0x785d2bAcbfbe1B40A4E0470a189F126B5CE4441A
- Balance
- 0 SOMI
- Transactions sent
- 1
- Type
- Contract · 9,877 bytes of code
MarketsCore Verified
- Compiler
- solc 0.8.34+commit.80d5c536
- Language
- Solidity
- Optimization
- Yes, 100 runs
- EVM version
- cancun
- License
- —
- Verified
- 2026-10-12
src/MarketsCore.sol
and 15 more files
Solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.34;
import {IMarketsModule} from "./interfaces/IMarketsModule.sol";
import {IVenuePolicy} from "./interfaces/IVenuePolicy.sol";
import {
Ownable2StepUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
/// @title MarketsCore
/// @notice Permissionless registry of operators (brand identities — frontends,
/// aggregators, or any counterparty surfacing the headless protocol)
/// and the typed venues they run. Fee params are type-specific opaque
/// bytes (not hardwired to any one market shape), decoded by the bound
/// module at market creation.
///
/// The model has two levels:
///
/// - **Operator** — the identity. Holds the owner key, the default
/// fee recipient, an operator-wide `IVenuePolicy`, and the enabled
/// flag. `operatorId 0` is reserved ("no operator").
/// - **Venue** — a scoped trading configuration under an operator,
/// bound to exactly ONE market type at creation (immutable
/// thereafter). Holds the type-specific fee params as opaque
/// `bytes` (only the market type's bound module knows the shape,
/// does the decode, and enforces its own protocol fee cap — new
/// market types need ZERO registry changes), an optional EIP-712
/// signer (non-zero ⇒ creation requires a venue signature), an
/// optional policy, and a creation enable flag. `venueId` is a
/// contract-generated opaque `bytes32` (keccak of operatorId + a
/// per-operator sequence); callers never choose it.
///
/// Module binding lives HERE, next to the operator/venue state it
/// gates: governance binds the ONE canonical module per market type via
/// `registerModule` (the `moduleOf` table); `venueForCreate` accepts a
/// market-creation call only from the bound module, so a market's
/// attribution to an operator/venue is a chain fact, not an off-chain
/// convention. Users reach a module's typed creation entry point
/// directly — this registry is consulted BY the module, never a
/// dispatch surface itself. Adding a market type = deploy a module
/// implementing `IMarketsModule` + `registerModule(marketType, module)`,
/// zero changes here beyond that one call.
///
/// Fee semantics for BINARY_V1 venues: origin fees (the venue that
/// creates a market) are decoded by the binary module at market creation
/// and frozen into the BinaryPool's init parameters — the pool has no
/// post-init fee setter, so snapshot-at-creation holds by construction.
/// Routing fees (the venue a trade routes through) ride the pool's
/// per-order builder rail, where the pool enforces the venue-advertised
/// ceiling per order and each user opts in via the pool's `approveBuilder`.
///
/// Disable semantics: `enabled` (operator) / `creationEnabled` (venue)
/// gate only NEW market origination. Existing markets keep their frozen
/// pool params and stay fully operable — users can always exit positions.
contract MarketsCore is UUPSUpgradeable, Ownable2StepUpgradeable {
/*//////////////////////////////////////////////////////////////
ENUMS / STRUCTS
//////////////////////////////////////////////////////////////*/
/// @notice On-chain operator record — the brand identity that owns venues.
/// @param owner The address authorised to update the operator and manage its venues.
/// @param feeRecipient Default recipient for all this operator's venues; a venue
/// can override it via `VenueConfig.feeRecipientOverride`. Must be non-zero
/// while the operator is enabled (a zero recipient would burn fees).
/// @param enabled Brand-wide creation switch: flip off to stop NEW market
/// origination under ALL of this operator's venues in one call
/// (`venueForCreate` reverts `OperatorDisabled`). Coarser than the
/// per-venue `creationEnabled`; existing markets are unaffected.
/// @param policy Optional operator-wide IVenuePolicy consulted in ADDITION to the
/// per-venue policy (AND-composition) — the brand-wide gate for rules
/// (KYC, geo) that apply across every venue; address(0) = no operator-wide gate.
/// @param context Opaque, operator-supplied metadata bytes. The registry attaches
/// NO semantics and never decodes them — purely a place to attach off-chain
/// data (indexed from the events). Size-capped by `_MAX_CONTEXT_BYTES`.
struct Operator {
address owner;
address feeRecipient;
bool enabled;
IVenuePolicy policy;
bytes context;
}
/// @notice Mutable venue configuration. Everything a venue owner can change after
/// creation — deliberately EXCLUDES `marketType`, which is set once in
/// `createVenue` and immutable (mutating it would orphan existing markets
/// whose pool params were decoded under the old type).
/// @param feeParams Type-specific fee parameters, abi-encoded. The registry never
/// decodes these — the market type's bound module does (e.g. the binary
/// module decodes `BinaryVenueParams` at market creation and enforces its
/// own protocol fee cap, in its own units, on the decoded rates). Malformed
/// bytes make market creation revert at decode — no funds at risk, and
/// existing markets are untouched because their params are already frozen
/// in the engine.
/// @param feeRecipientOverride Per-venue fee recipient; address(0) falls back to
/// the operator's `feeRecipient` at resolution time.
/// @param policy Optional per-venue IVenuePolicy. One policy contract serves every
/// decision point the interface exposes (create today; trade/route when a
/// market type gains a registry-aware trade path). address(0) = no
/// per-venue gate; the operator-wide policy still composes (AND).
/// Creation additionally requires SOME create-side policy (operator-wide or
/// per-venue) so openness is an explicit choice — point it at `OpenPolicy`.
/// @param signer Per-venue EIP-712 signer. A non-zero signer IS the signal that
/// market creation under this venue requires a venue-signed authorization
/// (no separate flag — set the signer to require sigs, zero it to open the
/// venue). Venues under one operator can use independent, independently
/// rotated keys.
/// @param creationEnabled Whether this venue may currently back NEW markets
/// (ENFORCED — `venueForCreate` reverts when false). Existing markets are
/// unaffected; this only gates fresh origination.
/// @param context Opaque, operator-supplied metadata bytes. The registry attaches
/// NO semantics and never decodes them — purely a place to attach off-chain
/// data (indexed from the events). Size-capped by `_MAX_CONTEXT_BYTES`.
struct VenueConfig {
bytes feeParams;
address feeRecipientOverride;
IVenuePolicy policy;
address signer;
bool creationEnabled;
bytes context;
}
/// @notice Full venue record: the immutable market type plus the mutable config.
/// @param marketType Market-type id (see `MarketTypeIds`); set once at
/// `createVenue`, immutable, and doubles as the existence sentinel
/// (bytes4(0) = venue does not exist — createVenue rejects a zero type).
/// @param config Mutable configuration; see `VenueConfig`.
struct Venue {
bytes4 marketType;
VenueConfig config;
}
/*//////////////////////////////////////////////////////////////
Contract storage
//////////////////////////////////////////////////////////////*/
/// @custom:storage-location erc7201:prophecy.storage.MarketsCore
/// @dev Storage layout is append-only — see the `// ---- Append-only fields below
/// this line ----` marker inside the struct. Any new field MUST be appended
/// at the end so the ERC-7201 namespaced base location plus existing slot
/// indices remain stable across upgrades.
/// @param nextOperatorId Auto-incrementing id assigned on the next
/// `registerOperator` call. Starts at 1 so 0 stays reserved.
/// @param _operators operatorId → Operator record.
/// @param pendingOwner Staged new owner for a pending two-step operator ownership
/// transfer. Zero means no transfer is in progress.
/// @param _venues operatorId → venueId → Venue record.
/// @param _venueIdsOf operatorId → list of venue ids ever created, for on-chain
/// enumeration. Venue creation is a low-frequency operator action, so the
/// array stays small; events + indexer remain the canonical enumeration.
/// @param moduleOf marketType → the ONE module governance has bound for the
/// type. The gate `venueForCreate` consults: only the bound module
/// may consume a venue of that type.
struct MarketsCoreStorage {
uint32 nextOperatorId;
mapping(uint32 operatorId => Operator) _operators;
mapping(uint32 operatorId => address) pendingOwner;
mapping(uint32 operatorId => mapping(bytes32 venueId => Venue)) _venues;
mapping(uint32 operatorId => bytes32[]) _venueIdsOf;
mapping(bytes4 marketType => address module) moduleOf;
// ---- Append-only fields below this line ----
// Global sequence for contract-generated venue ids (keccak of
// (operatorId, seq)); starts effectively at 1 on first createVenue.
uint256 nextVenueSeq;
}
/// @notice Sanity ceiling on `feeParams` byte length.
/// @dev Not a security boundary (operators pay their own storage) — rejects
/// obvious garbage before it is committed. A version-tagged
/// `BinaryVenueParams` encodes to 192 bytes; 1 KiB leaves generous
/// headroom for richer future types.
uint256 internal constant _MAX_FEE_PARAMS_BYTES = 1_024;
/// @notice Sanity ceiling on the opaque `context` byte length attached to an
/// operator or venue.
/// @dev Not a security boundary (the caller pays their own storage) — just rejects
/// unbounded blobs before they are committed. 4 KiB is generous for the
/// intended small metadata payloads.
uint256 internal constant _MAX_CONTEXT_BYTES = 4_096;
// keccak256(abi.encode(uint256(keccak256("prophecy.storage.MarketsCore")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _MarketsCoreStorageLocation =
0xc527b8bf189e1f392da00951e102e5509fb1fa85f292f86cd2df8a74ea459000;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/// @notice Emitted when a new operator registers.
/// @param operatorId Auto-incremented id assigned to the operator.
/// @param owner Address that registered the operator and can manage it.
/// @param feeRecipient Default fee recipient for the operator's venues.
/// @param enabled True if the operator is live at registration.
/// @param policy Operator-wide IVenuePolicy composed (AND) with each venue's own
/// policy — the operator-level gate is for brand-wide rules (KYC, geo) that
/// apply across every venue; per-venue policies still layer on top.
/// @param context Opaque operator-supplied metadata (registry attaches no semantics).
event OperatorRegistered(
uint32 indexed operatorId,
address indexed owner,
address indexed feeRecipient,
bool enabled,
address policy,
bytes context
);
/// @notice Emitted when the operator owner updates the operator's mutable fields.
/// @param operatorId Target operator.
/// @param feeRecipient New default fee recipient.
/// @param enabled New enabled flag.
/// @param policy New operator-wide policy; zero for none.
/// @param context New opaque operator-supplied metadata (replaces the prior value).
event OperatorUpdated(
uint32 indexed operatorId,
address indexed feeRecipient,
bool indexed enabled,
address policy,
bytes context
);
/// @notice Emitted when the operator owner toggles the `enabled` flag standalone.
/// @param operatorId Target operator.
/// @param enabled New enabled state.
event OperatorEnabledChanged(uint32 indexed operatorId, bool indexed enabled);
/// @notice Emitted when an operator owner stages a two-step ownership transfer.
/// @param operatorId Target operator.
/// @param currentOwner Owner staging the transfer.
/// @param pendingOwner Address staged as the incoming owner; still needs to accept.
event OperatorOwnershipTransferStarted(
uint32 indexed operatorId, address indexed currentOwner, address indexed pendingOwner
);
/// @notice Emitted when a pending owner accepts an operator ownership transfer.
/// @param operatorId Target operator.
/// @param oldOwner Previous owner.
/// @param newOwner Newly installed owner (= caller of `acceptOperatorOwnership`).
event OperatorOwnershipTransferred(
uint32 indexed operatorId, address indexed oldOwner, address indexed newOwner
);
/// @notice Emitted when a pending operator ownership transfer is cancelled,
/// either by the current owner re-staging themself as `newOwner`
/// or by an explicit clear.
/// @param operatorId Target operator.
/// @param cancelledPendingOwner Address that had been staged but never accepted.
event OperatorOwnershipTransferCancelled(
uint32 indexed operatorId, address indexed cancelledPendingOwner
);
/// @notice Emitted when a venue is created under an operator. `marketType` is
/// immutable from this point on.
/// @param operatorId Parent operator.
/// @param venueId Contract-generated opaque `bytes32` venue id (keccak of operatorId + sequence).
/// @param marketType Market-type id the venue is bound to, forever.
/// @param feeParams Type-specific fee parameters (opaque to the registry).
/// @param feeRecipientOverride Per-venue recipient; zero falls back to the operator's.
/// @param policy Per-venue policy; zero for none.
/// @param signer Per-venue EIP-712 signer; non-zero ⇒ creation requires a venue sig.
/// @param creationEnabled Origin-side enable flag.
/// @param context Opaque venue-supplied metadata (registry attaches no semantics).
event VenueCreated(
uint32 indexed operatorId,
bytes32 indexed venueId,
bytes4 indexed marketType,
bytes feeParams,
address feeRecipientOverride,
address policy,
address signer,
bool creationEnabled,
bytes context
);
/// @notice Emitted when the operator owner updates a venue's mutable config.
/// Field semantics match `VenueCreated`; `marketType` cannot change.
/// @param operatorId Parent operator.
/// @param venueId Target venue.
/// @param feeParams New type-specific fee parameters.
/// @param feeRecipientOverride New per-venue recipient override.
/// @param policy New per-venue policy.
/// @param signer New per-venue EIP-712 signer.
/// @param creationEnabled New origin-side enable flag.
/// @param context New opaque venue-supplied metadata (replaces the prior value).
event VenueUpdated(
uint32 indexed operatorId,
bytes32 indexed venueId,
bytes feeParams,
address feeRecipientOverride,
address policy,
address signer,
bool creationEnabled,
bytes context
);
/// @notice Emitted when the operator owner toggles a venue's creation flag standalone.
/// @param operatorId Parent operator.
/// @param venueId Target venue.
/// @param creationEnabled New origin-side enable flag.
event VenueEnabledChanged(uint32 indexed operatorId, bytes32 indexed venueId, bool creationEnabled);
/// @notice Emitted when governance binds (or rebinds) the canonical module
/// for a market type.
/// @param marketType Market-type id.
/// @param module The module now authorised to originate markets of this type.
event ModuleRegistered(bytes4 indexed marketType, address indexed module);
/// @notice Emitted when governance retires a market type: the binding is
/// cleared, so no NEW markets of this type can be originated —
/// while every existing market keeps running untouched (their
/// lifecycle lives on the module, which never consults this table
/// after creation).
/// @param marketType Market-type id.
/// @param module The module that was bound until now.
event ModuleDeregistered(bytes4 indexed marketType, address indexed module);
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @notice Thrown when an enabled operator would end up with a zero fee recipient
/// (fees would be burned — see design finding F10).
error FeeRecipientRequired();
/// @notice Thrown when a non-owner attempts an operator-owner-gated call.
error NotOwner();
/// @notice Thrown when `venueForCreate` is called by anything other than the
/// module governance has bound (`registerModule`) for the venue's
/// market type (or when no module is bound yet).
error NotModule();
/// @notice Thrown when `acceptOperatorOwnership` is called by anyone other than
/// the staged pending owner.
error PendingOwnerOnly();
/// @notice Thrown when the supplied operatorId has never been registered.
error UnknownOperator();
/// @notice Thrown when the supplied (operatorId, venueId) has never been created.
error UnknownVenue();
/// @notice Thrown when a create-path view rejects a disabled operator.
error OperatorDisabled();
/// @notice Thrown when a create-path view rejects a venue whose
/// `creationEnabled` flag is false.
error VenueCreationDisabled();
/// @notice Thrown when a venue's bound market type does not match the market type
/// the caller is operating on (design finding F1 — e.g. a SPOT venue must
/// never originate a BINARY market).
error MarketTypeMismatch(bytes4 expected, bytes4 actual);
/// @notice Thrown when `initialize` is given a zero owner address.
error ZeroOwner();
/// @notice Thrown when callers reference operatorId 0 — the reserved "no operator" sentinel.
error OperatorIdReserved();
/// @notice Thrown when `createVenue` is given `marketType == bytes4(0)` (reserved;
/// zero doubles as the venue-existence sentinel). Also thrown by
/// `registerModule` for the same reserved sentinel.
error MarketTypeReserved();
/// @notice Thrown when a policy contract fails the IVenuePolicy ERC-165 probe.
error InvalidPolicy();
/// @notice Thrown when `feeParams` exceeds the sanity byte-length ceiling.
error FeeParamsTooLong();
/// @notice Thrown when an operator/venue `context` blob exceeds `_MAX_CONTEXT_BYTES`.
error ContextTooLong();
/// @notice Thrown when `registerModule` is given the zero address as module.
error ZeroModule();
/// @notice Thrown when the module being registered reports a different
/// `marketType()` than the id it is being bound under — a
/// governance fat-finger guard.
error ModuleTypeMismatch(bytes4 expected, bytes4 actual);
/// @notice Thrown when no module is registered for the requested market type.
error UnknownMarketType(bytes4 marketType);
/*//////////////////////////////////////////////////////////////
Constructor / Initializer
//////////////////////////////////////////////////////////////*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Initialise the proxy. Called once immediately after the ERC1967Proxy is created.
/// @param owner_ Initial contract owner; receives upgrade authority.
function initialize(address owner_) external initializer {
if (owner_ == address(0)) revert ZeroOwner();
__Ownable_init(owner_);
__UUPSUpgradeable_init();
_getMarketsCoreStorage().nextOperatorId = 1;
}
/*//////////////////////////////////////////////////////////////
Operator management
//////////////////////////////////////////////////////////////*/
/// @notice Register a new operator. Permissionless — anyone can claim an identity;
/// economic weight comes from the venues and markets built under it.
/// Pass `enabled = false` to stage the configuration before going live.
/// @param feeRecipient Default fee recipient for the operator's venues; required
/// non-zero when `enabled` is true.
/// @param enabled Brand-level kill switch: when false the operator can back no
/// NEW markets under any of its venues (`venueForCreate` reverts). Existing
/// markets keep running — users can always exit. Stage config with
/// `enabled = false`, flip on to go live.
/// @param policy Operator-wide IVenuePolicy consulted in addition to per-venue
/// policies (AND-composition); pass address(0) for none.
/// @param context Opaque metadata bytes attached to the operator; empty for none.
/// The registry never decodes it — purely off-chain data, indexed from the
/// event. Size-capped by `_MAX_CONTEXT_BYTES`.
/// @return operatorId Auto-incrementing id assigned to the new operator (always >= 1).
/// @dev Reverts with `FeeRecipientRequired` if `enabled` and `feeRecipient == 0`.
/// @dev Reverts with `InvalidPolicy` if `policy` fails the ERC-165 probe.
/// @dev Reverts with `ContextTooLong` if `context` exceeds `_MAX_CONTEXT_BYTES`.
function registerOperator(address feeRecipient, bool enabled, IVenuePolicy policy, bytes calldata context)
external
returns (uint32 operatorId)
{
_validateOperatorConfig(feeRecipient, enabled);
_validatePolicy(policy);
if (context.length > _MAX_CONTEXT_BYTES) revert ContextTooLong();
MarketsCoreStorage storage $ = _getMarketsCoreStorage();
// Must return the pre-increment value as operatorId, so post-increment is required.
// solhint-disable-next-line gas-increment-by-one
operatorId = $.nextOperatorId++;
$._operators[operatorId] = Operator({
owner: msg.sender, feeRecipient: feeRecipient, enabled: enabled, policy: policy, context: context
});
emit OperatorRegistered(operatorId, msg.sender, feeRecipient, enabled, address(policy), context);
}
/// @notice Update all mutable operator fields in one call. Only the operator
/// owner may call.
/// @param operatorId Target operator; caller must be its owner.
/// @param feeRecipient New default fee recipient; required non-zero when `enabled`.
/// @param enabled New enabled flag.
/// @param policy New operator-wide policy; address(0) removes it.
/// @param context New opaque metadata bytes (replaces the prior value; empty clears it).
/// @dev Reverts with `FeeRecipientRequired` / `InvalidPolicy` / `ContextTooLong` as in `registerOperator`.
function updateOperator(
uint32 operatorId,
address feeRecipient,
bool enabled,
IVenuePolicy policy,
bytes calldata context
) external {
Operator storage op = _onlyOperatorOwner(operatorId);
_validateOperatorConfig(feeRecipient, enabled);
_validatePolicy(policy);
if (context.length > _MAX_CONTEXT_BYTES) revert ContextTooLong();
op.feeRecipient = feeRecipient;
op.enabled = enabled;
op.policy = policy;
op.context = context;
emit OperatorUpdated(operatorId, feeRecipient, enabled, address(policy), context);
}
/// @notice Flip the operator's enabled flag without touching any other field.
/// @param operatorId Target operator; caller must be its owner.
/// @param enabled New enabled state.
/// @dev Reverts with `FeeRecipientRequired` when enabling an operator whose
/// `feeRecipient` is zero (F10 invariant: enabled → recipient != 0).
function setOperatorEnabled(uint32 operatorId, bool enabled) external {
Operator storage op = _onlyOperatorOwner(operatorId);
if (enabled && op.feeRecipient == address(0)) revert FeeRecipientRequired();
op.enabled = enabled;
emit OperatorEnabledChanged(operatorId, enabled);
}
/// @notice Stage a two-step operator ownership transfer. The pending new owner
/// must call `acceptOperatorOwnership` to complete it. Staging the
/// current owner cancels any pending transfer.
/// @param operatorId Target operator; caller must be its current owner.
/// @param newOwner Address of the proposed new owner; must be non-zero.
function transferOperatorOwnership(uint32 operatorId, address newOwner) external {
if (newOwner == address(0)) revert ZeroOwner();
MarketsCoreStorage storage $ = _getMarketsCoreStorage();
Operator storage op = _onlyOperatorOwner(operatorId);
if (newOwner == op.owner) {
address staged = $.pendingOwner[operatorId];
if (staged != address(0)) {
delete $.pendingOwner[operatorId];
emit OperatorOwnershipTransferCancelled(operatorId, staged);
}
return;
}
$.pendingOwner[operatorId] = newOwner;
emit OperatorOwnershipTransferStarted(operatorId, op.owner, newOwner);
}
/// @notice Complete a pending operator ownership transfer. Must be called by the
/// address staged via `transferOperatorOwnership`.
/// @param operatorId Target operator; `msg.sender` must be the pending new owner.
function acceptOperatorOwnership(uint32 operatorId) external {
MarketsCoreStorage storage $ = _getMarketsCoreStorage();
address pending = $.pendingOwner[operatorId];
if (msg.sender != pending) revert PendingOwnerOnly();
Operator storage op = $._operators[operatorId];
address old = op.owner;
op.owner = msg.sender;
delete $.pendingOwner[operatorId];
emit OperatorOwnershipTransferred(operatorId, old, msg.sender);
}
/*//////////////////////////////////////////////////////////////
Venue management
//////////////////////////////////////////////////////////////*/
/// @notice Create a venue under an operator. `marketType` is fixed for this
/// (operatorId, venueId) tuple FOREVER — changing it would orphan
/// markets whose pool params were decoded under the old type (design
/// finding F2). Everything else is mutable via `updateVenue`.
/// @param operatorId Parent operator; caller must be its owner.
/// @param marketType Market-type id (see `MarketTypeIds`); non-zero.
/// @param config Initial mutable configuration; see `VenueConfig`.
/// @return venueId The CONTRACT-GENERATED venue id: an opaque, globally
/// unique `bytes32` = `keccak256(operatorId, seq)`. Callers do NOT
/// supply it (an id is not a caller-chosen label) — this mirrors the
/// auto-assigned `operatorId`, just wider so it stays globally
/// unique and never a human tag. Collision is impossible, so there
/// is no `VenueAlreadyExists`.
/// @dev Reverts with `MarketTypeReserved` on a zero market type.
/// @dev Reverts with `InvalidPolicy` / `FeeParamsTooLong` on invalid config
/// (see `_validateVenueConfig`).
function createVenue(uint32 operatorId, bytes4 marketType, VenueConfig calldata config)
external
returns (bytes32 venueId)
{
if (marketType == bytes4(0)) revert MarketTypeReserved();
_onlyOperatorOwner(operatorId);
_validateVenueConfig(config);
MarketsCoreStorage storage $ = _getMarketsCoreStorage();
// Contract-generated id: keccak over (operatorId, monotonic seq). The
// seq makes it unique even for the same operator; folding operatorId in
// keeps ids from different operators disjoint by construction.
venueId = keccak256(abi.encode(operatorId, ++$.nextVenueSeq));
Venue storage v = $._venues[operatorId][venueId];
v.marketType = marketType;
v.config = config;
$._venueIdsOf[operatorId].push(venueId);
_emitVenueCreated(operatorId, venueId, marketType, config);
}
/// @notice Update a venue's mutable configuration. `marketType` is not part of
/// `VenueConfig` and therefore structurally cannot change here.
/// @param operatorId Parent operator; caller must be its owner.
/// @param venueId Target venue.
/// @param config New configuration; see `VenueConfig`.
/// @dev Reverts with `UnknownVenue` if the venue was never created.
/// @dev Same config validation reverts as `createVenue`.
function updateVenue(uint32 operatorId, bytes32 venueId, VenueConfig calldata config) external {
_onlyOperatorOwner(operatorId);
_validateVenueConfig(config);
Venue storage v = _getMarketsCoreStorage()._venues[operatorId][venueId];
if (v.marketType == bytes4(0)) revert UnknownVenue();
v.config = config;
emit VenueUpdated(
operatorId,
venueId,
config.feeParams,
config.feeRecipientOverride,
address(config.policy),
config.signer,
config.creationEnabled,
config.context
);
}
/// @notice Flip a venue's creation flag without touching any other field.
/// @param operatorId Parent operator; caller must be its owner.
/// @param venueId Target venue.
/// @param creationEnabled New origin-side enable flag.
function setVenueEnabled(uint32 operatorId, bytes32 venueId, bool creationEnabled) external {
_onlyOperatorOwner(operatorId);
Venue storage v = _getMarketsCoreStorage()._venues[operatorId][venueId];
if (v.marketType == bytes4(0)) revert UnknownVenue();
v.config.creationEnabled = creationEnabled;
emit VenueEnabledChanged(operatorId, venueId, creationEnabled);
}
/*//////////////////////////////////////////////////////////////
Module binding (governance)
//////////////////////////////////////////////////////////////*/
/// @notice Bind the canonical module contract for a market type. Governance-
/// only. Only the bound module may consume a venue of that type
/// (the `venueForCreate` gate reads this table), making market
/// attribution a chain fact.
/// @param marketType Market-type id (see `MarketTypeIds`); non-zero.
/// @param module Module authorised to originate markets of this type;
/// non-zero, must report `marketType() == marketType`.
function registerModule(bytes4 marketType, address module) external onlyOwner {
if (marketType == bytes4(0)) revert MarketTypeReserved();
if (module == address(0)) revert ZeroModule();
bytes4 reported = IMarketsModule(module).marketType();
if (reported != marketType) revert ModuleTypeMismatch(marketType, reported);
_getMarketsCoreStorage().moduleOf[marketType] = module;
emit ModuleRegistered(marketType, module);
}
/// @notice Retire a market type SAFELY: clears the binding so no NEW markets
/// of this type can be originated (the module's typed create path
/// calls `venueForCreate`, which no longer admits it). Every
/// EXISTING market keeps running untouched: mint/merge/redeem,
/// trading, and oracle resolution all live on the module and never
/// consult this table after creation. Re-`registerModule` to resume.
/// @param marketType Market-type id to retire; must currently be bound.
function deregisterModule(bytes4 marketType) external onlyOwner {
MarketsCoreStorage storage $ = _getMarketsCoreStorage();
address module = $.moduleOf[marketType];
if (module == address(0)) revert UnknownMarketType(marketType);
delete $.moduleOf[marketType];
emit ModuleDeregistered(marketType, module);
}
/// @notice The canonical module bound for a market type, or address(0) if unset.
/// @param marketType Market-type id to query.
/// @return The bound module address.
function moduleOf(bytes4 marketType) external view returns (address) {
return _getMarketsCoreStorage().moduleOf[marketType];
}
/*//////////////////////////////////////////////////////////////
View Functions
//////////////////////////////////////////////////////////////*/
/// @notice Auto-incrementing. Returns the id that will be assigned on the next
/// `registerOperator` call. Starts at 1 so 0 stays reserved.
function nextOperatorId() external view returns (uint32) {
return _getMarketsCoreStorage().nextOperatorId;
}
/// @notice Staged new owner for a pending two-step operator transfer. Zero means
/// no transfer is in progress for that operatorId.
/// @param operatorId Target operator.
/// @return The staged pending owner, or address(0) if none.
function pendingOperatorOwner(uint32 operatorId) external view returns (address) {
return _getMarketsCoreStorage().pendingOwner[operatorId];
}
/// @notice Return the full operator record. Reverts for the reserved id 0.
/// @param operatorId Target operator.
/// @return Full Operator struct; `owner` is zero for unregistered ids.
function getOperator(uint32 operatorId) external view returns (Operator memory) {
if (operatorId == 0) revert OperatorIdReserved();
return _getMarketsCoreStorage()._operators[operatorId];
}
/// @notice Return the full venue record (marketType + config).
/// @param operatorId Parent operator.
/// @param venueId Target venue.
/// @return Full Venue struct; `marketType` is zero for venues never created.
function getVenue(uint32 operatorId, bytes32 venueId) external view returns (Venue memory) {
if (operatorId == 0) revert OperatorIdReserved();
return _getMarketsCoreStorage()._venues[operatorId][venueId];
}
/// @notice Enumerate all venue ids ever created under `operatorId`.
/// @param operatorId Parent operator.
/// @return List of venue ids in creation order (soft-disabled venues included —
/// venues are never hard-deleted, so ids never dangle).
function venueIdsOf(uint32 operatorId) external view returns (bytes32[] memory) {
return _getMarketsCoreStorage()._venueIdsOf[operatorId];
}
/// @notice Resolve the effective fee recipient for a venue: the per-venue
/// override if set, otherwise the operator's default recipient.
/// @param operatorId Parent operator.
/// @param venueId Target venue.
/// @return Effective recipient; may be zero only while the operator is disabled
/// (the F10 invariant guarantees enabled operators have a recipient).
function resolvedFeeRecipient(uint32 operatorId, bytes32 venueId) public view returns (address) {
MarketsCoreStorage storage $ = _getMarketsCoreStorage();
address override_ = $._venues[operatorId][venueId].config.feeRecipientOverride;
return override_ != address(0) ? override_ : $._operators[operatorId].feeRecipient;
}
/// @notice Everything a market-type contract needs to admit a market-creation
/// under (operatorId, venueId), gathered in one call with all liveness
/// checks applied. This is the create-path hot view BinaryMarketsModule uses.
/// @param operatorId Origin operator.
/// @param venueId Origin venue.
/// @param marketType Market type the CALLER operates on; must match the venue's
/// bound type (design finding F1 — cross-type attribution is rejected here,
/// not left to the caller).
/// @return signer Per-venue EIP-712 signer; non-zero means the caller MUST verify a
/// venue signature (the signer's existence IS the requirement — no flag).
/// @return operatorPolicy Operator-wide policy (AND'd with the venue policy); zero for none.
/// @return venuePolicy Per-venue policy; zero for none.
/// @return feeParams Type-specific fee parameters for the caller to decode + cap.
/// @return feeRecipient Effective fee recipient (venue override or operator default).
/// @dev Caller MUST be the module governance bound for `marketType` — reverts
/// `NotModule` otherwise, so only the canonical module can originate a
/// market under a venue and attribution can never be spoofed.
/// @dev Reverts with `UnknownOperator` for unregistered operators.
/// @dev Reverts with `OperatorDisabled` when the operator is soft-disabled.
/// @dev Reverts with `UnknownVenue` for venues never created.
/// @dev Reverts with `MarketTypeMismatch` when the venue serves a different type.
/// @dev Reverts with `VenueCreationDisabled` when the venue's origin side is off.
function venueForCreate(uint32 operatorId, bytes32 venueId, bytes4 marketType)
external
view
returns (
address signer,
IVenuePolicy operatorPolicy,
IVenuePolicy venuePolicy,
bytes memory feeParams,
address feeRecipient
)
{
MarketsCoreStorage storage $ = _getMarketsCoreStorage();
if (msg.sender != $.moduleOf[marketType]) revert NotModule();
Operator storage op = $._operators[operatorId];
if (op.owner == address(0)) revert UnknownOperator();
if (!op.enabled) revert OperatorDisabled();
Venue storage v = $._venues[operatorId][venueId];
if (v.marketType == bytes4(0)) revert UnknownVenue();
if (v.marketType != marketType) revert MarketTypeMismatch(marketType, v.marketType);
VenueConfig storage cfg = v.config;
if (!cfg.creationEnabled) revert VenueCreationDisabled();
address recipient =
cfg.feeRecipientOverride != address(0) ? cfg.feeRecipientOverride : op.feeRecipient;
return (cfg.signer, op.policy, cfg.policy, cfg.feeParams, recipient);
}
/*//////////////////////////////////////////////////////////////
Internal helpers
//////////////////////////////////////////////////////////////*/
/// @dev UUPS upgrade authorization hook — restricted to the contract owner.
function _authorizeUpgrade(address) internal override onlyOwner {}
/// @dev F10 invariant: an enabled operator must have a fee recipient, otherwise
/// every venue falling back to the operator default would burn fees.
function _validateOperatorConfig(address feeRecipient, bool enabled) internal pure {
if (enabled && feeRecipient == address(0)) revert FeeRecipientRequired();
}
/// @dev Shared config validation for `createVenue` / `updateVenue` — factored so
/// the two paths can never drift.
function _validateVenueConfig(VenueConfig calldata config) internal view {
_validatePolicy(config.policy);
// Common-sense bound: rejects obvious garbage before it is committed to
// storage; not a security boundary (see `_MAX_FEE_PARAMS_BYTES`). The fee
// rates themselves are opaque here — the bound module decodes and caps
// them in its own units at market creation.
if (config.feeParams.length > _MAX_FEE_PARAMS_BYTES) revert FeeParamsTooLong();
if (config.context.length > _MAX_CONTEXT_BYTES) revert ContextTooLong();
}
/// @dev Revert if `policy` is non-zero but does not declare ERC-165 support for
/// IVenuePolicy. Catches both revert and false-return cases.
function _validatePolicy(IVenuePolicy policy) internal view {
if (address(policy) == address(0)) return;
try policy.supportsInterface(type(IVenuePolicy).interfaceId) returns (bool ok) {
if (!ok) revert InvalidPolicy();
} catch {
revert InvalidPolicy();
}
}
/// @dev Load the operator and assert the caller is its owner. Returns the storage
/// pointer so callers can mutate fields without a second SLOAD.
function _onlyOperatorOwner(uint32 operatorId) internal view returns (Operator storage op) {
op = _getMarketsCoreStorage()._operators[operatorId];
if (op.owner == address(0)) revert UnknownOperator();
if (msg.sender != op.owner) revert NotOwner();
}
/// @dev Emit `VenueCreated` from its own frame to keep `createVenue` clear of the
/// event's argument fan-out.
function _emitVenueCreated(
uint32 operatorId,
bytes32 venueId,
bytes4 marketType,
VenueConfig calldata config
) private {
emit VenueCreated(
operatorId,
venueId,
marketType,
config.feeParams,
config.feeRecipientOverride,
address(config.policy),
config.signer,
config.creationEnabled,
config.context
);
}
/// @dev ERC-7201 namespaced storage accessor.
function _getMarketsCoreStorage() private pure returns (MarketsCoreStorage storage $) {
assembly {
$.slot := _MarketsCoreStorageLocation
}
}
}
Other files
lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol) pragma solidity ^0.8.20; import {OwnableUpgradeable} from "./OwnableUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This extension of the {Ownable} contract includes a two-step mechanism to transfer * ownership, where the new owner must call {acceptOwnership} in order to replace the * old one. This can help prevent common mistakes, such as transfers of ownership to * incorrect accounts, or to contracts that are unable to interact with the * permission system. * * The initial owner is specified at deployment time in the constructor for `Ownable`. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step struct Ownable2StepStorage { address _pendingOwner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00; function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) { assembly { $.slot := Ownable2StepStorageLocation } } event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); function __Ownable2Step_init() internal onlyInitializing { } function __Ownable2Step_init_unchained() internal onlyInitializing { } /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { Ownable2StepStorage storage $ = _getOwnable2StepStorage(); return $._pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. * * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer. */ function transferOwnership(address newOwner) public virtual override onlyOwner { Ownable2StepStorage storage $ = _getOwnable2StepStorage(); $._pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { Ownable2StepStorage storage $ = _getOwnable2StepStorage(); delete $._pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() public virtual { address sender = _msgSender(); if (pendingOwner() != sender) { revert OwnableUnauthorizedAccount(sender); } _transferOwnership(sender); } }lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC-1967 implementation slot: * ```solidity * contract ERC1967 { * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot. * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * TIP: Consider using this library along with {SlotDerivation}. */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct Int256Slot { int256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Int256Slot` with member `value` located at `slot`. */ function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } /** * @dev Returns a `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } }lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable struct OwnableStorage { address _owner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly { $.slot := OwnableStorageLocation } } /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ function __Ownable_init(address initialOwner) internal onlyInitializing { __Ownable_init_unchained(initialOwner); } function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { OwnableStorage storage $ = _getOwnableStorage(); return $._owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { OwnableStorage storage $ = _getOwnableStorage(); address oldOwner = $._owner; $._owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol) pragma solidity ^0.8.20; import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol"; import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import {Initializable} from "./Initializable.sol"; /** * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. * * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing * `UUPSUpgradeable` with a custom implementation of upgrades. * * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. */ abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable { /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address private immutable __self = address(this); /** * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)` * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called, * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string. * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function * during an upgrade. */ string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; /** * @dev The call is from an unauthorized context. */ error UUPSUnauthorizedCallContext(); /** * @dev The storage `slot` is unsupported as a UUID. */ error UUPSUnsupportedProxiableUUID(bytes32 slot); /** * @dev Check that the execution is being performed through a delegatecall call and that the execution context is * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to * fail. */ modifier onlyProxy() { _checkProxy(); _; } /** * @dev Check that the execution is not being performed through a delegate call. This allows a function to be * callable on the implementing contract but not through proxies. */ modifier notDelegated() { _checkNotDelegated(); _; } function __UUPSUpgradeable_init() internal onlyInitializing { } function __UUPSUpgradeable_init_unchained() internal onlyInitializing { } /** * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the * implementation. It is used to validate the implementation's compatibility when performing an upgrade. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier. */ function proxiableUUID() external view virtual notDelegated returns (bytes32) { return ERC1967Utils.IMPLEMENTATION_SLOT; } /** * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call * encoded in `data`. * * Calls {_authorizeUpgrade}. * * Emits an {Upgraded} event. * * @custom:oz-upgrades-unsafe-allow-reachable delegatecall */ function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy { _authorizeUpgrade(newImplementation); _upgradeToAndCallUUPS(newImplementation, data); } /** * @dev Reverts if the execution is not performed via delegatecall or the execution * context is not of a proxy with an ERC-1967 compliant implementation pointing to self. * See {_onlyProxy}. */ function _checkProxy() internal view virtual { if ( address(this) == __self || // Must be called through delegatecall ERC1967Utils.getImplementation() != __self // Must be called through an active proxy ) { revert UUPSUnauthorizedCallContext(); } } /** * @dev Reverts if the execution is performed via delegatecall. * See {notDelegated}. */ function _checkNotDelegated() internal view virtual { if (address(this) != __self) { // Must not be called through delegatecall revert UUPSUnauthorizedCallContext(); } } /** * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by * {upgradeToAndCall}. * * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. * * ```solidity * function _authorizeUpgrade(address) internal onlyOwner {} * ``` */ function _authorizeUpgrade(address newImplementation) internal virtual; /** * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call. * * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value * is expected to be the implementation slot in ERC-1967. * * Emits an {IERC1967-Upgraded} event. */ function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) { revert UUPSUnsupportedProxiableUUID(slot); } ERC1967Utils.upgradeToAndCall(newImplementation, data); } catch { // The implementation is not UUPS revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation); } } }lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }lib/openzeppelin-contracts/contracts/interfaces/IERC1967.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol) pragma solidity ^0.8.20; /** * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC. */ interface IERC1967 { /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Emitted when the beacon is changed. */ event BeaconUpgraded(address indexed beacon); }lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.20; /** * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified * proxy whose upgrades are fully controlled by the current implementation. */ interface IERC1822Proxiable { /** * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation * address. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. */ function proxiableUUID() external view returns (bytes32); }lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Utils.sol) pragma solidity ^0.8.21; import {IBeacon} from "../beacon/IBeacon.sol"; import {IERC1967} from "../../interfaces/IERC1967.sol"; import {Address} from "../../utils/Address.sol"; import {StorageSlot} from "../../utils/StorageSlot.sol"; /** * @dev This library provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots. */ library ERC1967Utils { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev The `implementation` of the proxy is invalid. */ error ERC1967InvalidImplementation(address implementation); /** * @dev The `admin` of the proxy is invalid. */ error ERC1967InvalidAdmin(address admin); /** * @dev The `beacon` of the proxy is invalid. */ error ERC1967InvalidBeacon(address beacon); /** * @dev An upgrade function sees `msg.value > 0` that may be lost. */ error ERC1967NonPayable(); /** * @dev Returns the current implementation address. */ function getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the ERC-1967 implementation slot. */ function _setImplementation(address newImplementation) private { if (newImplementation.code.length == 0) { revert ERC1967InvalidImplementation(newImplementation); } StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Performs implementation upgrade with additional setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-Upgraded} event. */ function upgradeToAndCall(address newImplementation, bytes memory data) internal { _setImplementation(newImplementation); emit IERC1967.Upgraded(newImplementation); if (data.length > 0) { Address.functionDelegateCall(newImplementation, data); } else { _checkNonPayable(); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(ADMIN_SLOT).value; } /** * @dev Stores a new address in the ERC-1967 admin slot. */ function _setAdmin(address newAdmin) private { if (newAdmin == address(0)) { revert ERC1967InvalidAdmin(address(0)); } StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {IERC1967-AdminChanged} event. */ function changeAdmin(address newAdmin) internal { emit IERC1967.AdminChanged(getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Returns the current beacon. */ function getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(BEACON_SLOT).value; } /** * @dev Stores a new beacon in the ERC-1967 beacon slot. */ function _setBeacon(address newBeacon) private { if (newBeacon.code.length == 0) { revert ERC1967InvalidBeacon(newBeacon); } StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon; address beaconImplementation = IBeacon(newBeacon).implementation(); if (beaconImplementation.code.length == 0) { revert ERC1967InvalidImplementation(beaconImplementation); } } /** * @dev Change the beacon and trigger a setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-BeaconUpgraded} event. * * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for * efficiency. */ function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal { _setBeacon(newBeacon); emit IERC1967.BeaconUpgraded(newBeacon); if (data.length > 0) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } else { _checkNonPayable(); } } /** * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract * if an upgrade doesn't perform an initialization call. */ function _checkNonPayable() private { if (msg.value > 0) { revert ERC1967NonPayable(); } } }lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.20; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {UpgradeableBeacon} will check that this address is a contract. */ function implementation() external view returns (address); }lib/openzeppelin-contracts/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert Errors.FailedCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {Errors.FailedCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case * of an unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {Errors.FailedCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly ("memory-safe") { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert Errors.FailedCall(); } } }lib/openzeppelin-contracts/contracts/utils/Errors.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol) pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. * * _Available since v5.1._ */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); /** * @dev A necessary precompile is missing. */ error MissingPrecompile(address); }lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }src/interfaces/IMarketsModule.sol
// SPDX-License-Identifier: MIT pragma solidity 0.8.34; /// @title IMarketsModule /// @notice The MINIMAL surface every market-type module implements so /// `MarketsCore.registerModule` can sanity-check a governance /// binding before committing it: the module must self-report the /// SAME market-type id it is being bound under. This is the only /// requirement the registry places on a module — everything else /// (creation, mint/merge/redeem, trading, resolution) is type-specific /// and lives on the module's own typed surface, which users call /// directly. /// /// This is the extension seam: adding a market type = deploy a module /// implementing this interface + `MarketsCore.registerModule /// (marketType, module)`, with ZERO changes to the registry or /// sibling modules. interface IMarketsModule { /// @notice The market-type id this module is the canonical module for /// (matches its `MarketsCore.moduleOf` binding; checked at /// registration). function marketType() external view returns (bytes4); }src/interfaces/IVenuePolicy.sol
// SPDX-License-Identifier: MIT pragma solidity 0.8.34; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /// @title IVenuePolicy /// @notice Pluggable access-control hook a venue can attach to its `Venue` /// record in the registry. The market-type module consults a venue's /// policy at three decision points; each method returns `false` to deny. /// /// A venue's policy contract serves all three callbacks. Implementations /// pick which dimensions they actually gate; the others can return `true` /// unconditionally. NOTE: market creation requires SOME create-side /// policy (operator-wide or per-venue) — a venue with no policy at all /// reverts `VenuePolicyUnset`. Openness is an explicit choice: point the /// policy at `OpenPolicy` to open the venue. /// /// Implements ERC-165. The registry validates `supportsInterface` before /// accepting a policy address, so passing a non-conforming address reverts. interface IVenuePolicy is IERC165 { /// @notice Called when `user` attempts to create a market through this /// venue (i.e. tagging the create-market call with this venue's id). /// Use this for KYC / allowlist gates on market creators. /// @param user Account attempting to create the market. /// @return allowed True if `user` is permitted to create markets under this venue. function canCreateMarket(address user) external view returns (bool allowed); /// @notice Called when `user` attempts to buy/sell through this venue. /// `conditionId` lets the policy gate on a per-market basis (e.g. /// "this venue only allows verified accounts to trade on real-money /// markets; demo markets are open"). /// @param user Account attempting to trade. /// @param conditionId Identifier of the market being traded. /// @return allowed True if `user` is permitted to trade `conditionId` under this venue. function canTrade(address user, bytes32 conditionId) external view returns (bool allowed); /// @notice Called when a *different* venue (`routingVenueId`) attempts to /// route a trade against a market that *this* venue originated. /// Lets a market-creating venue gate which counterparties may /// facilitate trades on its markets. Returning `false` blocks the /// trade. /// /// Skipped entirely when the routing venue and the origin venue /// are the same (a venue never gates itself). Skipped when the /// origin venue is 0 (the market was created without a venue — /// there is no origin policy to consult). /// /// A `routingVenueId` of 0 represents a venueless ("direct") trade /// path; an origin venue can still refuse it by returning `false`, /// which is the lever for "all trades on my markets must go through /// an approved routing venue". /// @param routingVenueId Venue id attempting to route the trade (0 for a direct path). /// @param conditionId Identifier of the market being routed against. /// @return allowed True if `routingVenueId` is permitted to route trades on `conditionId`. function canRoute(uint32 routingVenueId, bytes32 conditionId) external view returns (bool allowed); }
ABI · 71 entries
json
[
{
"type": "constructor",
"inputs": [],
"stateMutability": "nonpayable"
},
{
"name": "AddressEmptyCode",
"type": "error",
"inputs": [
{
"name": "target",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "ContextTooLong",
"type": "error",
"inputs": []
},
{
"name": "ERC1967InvalidImplementation",
"type": "error",
"inputs": [
{
"name": "implementation",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "ERC1967NonPayable",
"type": "error",
"inputs": []
},
{
"name": "FailedCall",
"type": "error",
"inputs": []
},
{
"name": "FeeParamsTooLong",
"type": "error",
"inputs": []
},
{
"name": "FeeRecipientRequired",
"type": "error",
"inputs": []
},
{
"name": "InvalidInitialization",
"type": "error",
"inputs": []
},
{
"name": "InvalidPolicy",
"type": "error",
"inputs": []
},
{
"name": "MarketTypeMismatch",
"type": "error",
"inputs": [
{
"name": "expected",
"type": "bytes4",
"internalType": "bytes4"
},
{
"name": "actual",
"type": "bytes4",
"internalType": "bytes4"
}
]
},
{
"name": "MarketTypeReserved",
"type": "error",
"inputs": []
},
{
"name": "ModuleTypeMismatch",
"type": "error",
"inputs": [
{
"name": "expected",
"type": "bytes4",
"internalType": "bytes4"
},
{
"name": "actual",
"type": "bytes4",
"internalType": "bytes4"
}
]
},
{
"name": "NotInitializing",
"type": "error",
"inputs": []
},
{
"name": "NotModule",
"type": "error",
"inputs": []
},
{
"name": "NotOwner",
"type": "error",
"inputs": []
},
{
"name": "OperatorDisabled",
"type": "error",
"inputs": []
},
{
"name": "OperatorIdReserved",
"type": "error",
"inputs": []
},
{
"name": "OwnableInvalidOwner",
"type": "error",
"inputs": [
{
"name": "owner",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "OwnableUnauthorizedAccount",
"type": "error",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "PendingOwnerOnly",
"type": "error",
"inputs": []
},
{
"name": "UUPSUnauthorizedCallContext",
"type": "error",
"inputs": []
},
{
"name": "UUPSUnsupportedProxiableUUID",
"type": "error",
"inputs": [
{
"name": "slot",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"name": "UnknownMarketType",
"type": "error",
"inputs": [
{
"name": "marketType",
"type": "bytes4",
"internalType": "bytes4"
}
]
},
{
"name": "UnknownOperator",
"type": "error",
"inputs": []
},
{
"name": "UnknownVenue",
"type": "error",
"inputs": []
},
{
"name": "VenueCreationDisabled",
"type": "error",
"inputs": []
},
{
"name": "ZeroModule",
"type": "error",
"inputs": []
},
{
"name": "ZeroOwner",
"type": "error",
"inputs": []
},
{
"name": "Initialized",
"type": "event",
"inputs": [
{
"name": "version",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
}
],
"anonymous": false
},
{
"name": "ModuleDeregistered",
"type": "event",
"inputs": [
{
"name": "marketType",
"type": "bytes4",
"indexed": true,
"internalType": "bytes4"
},
{
"name": "module",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "ModuleRegistered",
"type": "event",
"inputs": [
{
"name": "marketType",
"type": "bytes4",
"indexed": true,
"internalType": "bytes4"
},
{
"name": "module",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OperatorEnabledChanged",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "enabled",
"type": "bool",
"indexed": true,
"internalType": "bool"
}
],
"anonymous": false
},
{
"name": "OperatorOwnershipTransferCancelled",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "cancelledPendingOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OperatorOwnershipTransferStarted",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "currentOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "pendingOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OperatorOwnershipTransferred",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "oldOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OperatorRegistered",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "feeRecipient",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "enabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "policy",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "context",
"type": "bytes",
"indexed": false,
"internalType": "bytes"
}
],
"anonymous": false
},
{
"name": "OperatorUpdated",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "feeRecipient",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "enabled",
"type": "bool",
"indexed": true,
"internalType": "bool"
},
{
"name": "policy",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "context",
"type": "bytes",
"indexed": false,
"internalType": "bytes"
}
],
"anonymous": false
},
{
"name": "OwnershipTransferStarted",
"type": "event",
"inputs": [
{
"name": "previousOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OwnershipTransferred",
"type": "event",
"inputs": [
{
"name": "previousOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "Upgraded",
"type": "event",
"inputs": [
{
"name": "implementation",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "VenueCreated",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "venueId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "marketType",
"type": "bytes4",
"indexed": true,
"internalType": "bytes4"
},
{
"name": "feeParams",
"type": "bytes",
"indexed": false,
"internalType": "bytes"
},
{
"name": "feeRecipientOverride",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "policy",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "signer",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "creationEnabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "context",
"type": "bytes",
"indexed": false,
"internalType": "bytes"
}
],
"anonymous": false
},
{
"name": "VenueEnabledChanged",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "venueId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "creationEnabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
}
],
"anonymous": false
},
{
"name": "VenueUpdated",
"type": "event",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"indexed": true,
"internalType": "uint32"
},
{
"name": "venueId",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "feeParams",
"type": "bytes",
"indexed": false,
"internalType": "bytes"
},
{
"name": "feeRecipientOverride",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "policy",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "signer",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "creationEnabled",
"type": "bool",
"indexed": false,
"internalType": "bool"
},
{
"name": "context",
"type": "bytes",
"indexed": false,
"internalType": "bytes"
}
],
"anonymous": false
},
{
"name": "UPGRADE_INTERFACE_VERSION",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "string",
"internalType": "string"
}
],
"stateMutability": "view"
},
{
"name": "acceptOperatorOwnership",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "acceptOwnership",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "createVenue",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "marketType",
"type": "bytes4",
"internalType": "bytes4"
},
{
"name": "config",
"type": "tuple",
"components": [
{
"name": "feeParams",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "feeRecipientOverride",
"type": "address",
"internalType": "address"
},
{
"name": "policy",
"type": "address",
"internalType": "contract IVenuePolicy"
},
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "creationEnabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "context",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct MarketsCore.VenueConfig"
}
],
"outputs": [
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "nonpayable"
},
{
"name": "deregisterModule",
"type": "function",
"inputs": [
{
"name": "marketType",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "getOperator",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"components": [
{
"name": "owner",
"type": "address",
"internalType": "address"
},
{
"name": "feeRecipient",
"type": "address",
"internalType": "address"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "policy",
"type": "address",
"internalType": "contract IVenuePolicy"
},
{
"name": "context",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct MarketsCore.Operator"
}
],
"stateMutability": "view"
},
{
"name": "getVenue",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"components": [
{
"name": "marketType",
"type": "bytes4",
"internalType": "bytes4"
},
{
"name": "config",
"type": "tuple",
"components": [
{
"name": "feeParams",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "feeRecipientOverride",
"type": "address",
"internalType": "address"
},
{
"name": "policy",
"type": "address",
"internalType": "contract IVenuePolicy"
},
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "creationEnabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "context",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct MarketsCore.VenueConfig"
}
],
"internalType": "struct MarketsCore.Venue"
}
],
"stateMutability": "view"
},
{
"name": "initialize",
"type": "function",
"inputs": [
{
"name": "owner_",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "moduleOf",
"type": "function",
"inputs": [
{
"name": "marketType",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "nextOperatorId",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"name": "owner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "pendingOperatorOwner",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
}
],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "pendingOwner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "proxiableUUID",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "registerModule",
"type": "function",
"inputs": [
{
"name": "marketType",
"type": "bytes4",
"internalType": "bytes4"
},
{
"name": "module",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "registerOperator",
"type": "function",
"inputs": [
{
"name": "feeRecipient",
"type": "address",
"internalType": "address"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "policy",
"type": "address",
"internalType": "contract IVenuePolicy"
},
{
"name": "context",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "nonpayable"
},
{
"name": "renounceOwnership",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "resolvedFeeRecipient",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "setOperatorEnabled",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setVenueEnabled",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "creationEnabled",
"type": "bool",
"internalType": "bool"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "transferOperatorOwnership",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "newOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "transferOwnership",
"type": "function",
"inputs": [
{
"name": "newOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "updateOperator",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "feeRecipient",
"type": "address",
"internalType": "address"
},
{
"name": "enabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "policy",
"type": "address",
"internalType": "contract IVenuePolicy"
},
{
"name": "context",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "updateVenue",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "config",
"type": "tuple",
"components": [
{
"name": "feeParams",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "feeRecipientOverride",
"type": "address",
"internalType": "address"
},
{
"name": "policy",
"type": "address",
"internalType": "contract IVenuePolicy"
},
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "creationEnabled",
"type": "bool",
"internalType": "bool"
},
{
"name": "context",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct MarketsCore.VenueConfig"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "upgradeToAndCall",
"type": "function",
"inputs": [
{
"name": "newImplementation",
"type": "address",
"internalType": "address"
},
{
"name": "data",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "venueForCreate",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "venueId",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "marketType",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [
{
"name": "signer",
"type": "address",
"internalType": "address"
},
{
"name": "operatorPolicy",
"type": "address",
"internalType": "contract IVenuePolicy"
},
{
"name": "venuePolicy",
"type": "address",
"internalType": "contract IVenuePolicy"
},
{
"name": "feeParams",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "feeRecipient",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "venueIdsOf",
"type": "function",
"inputs": [
{
"name": "operatorId",
"type": "uint32",
"internalType": "uint32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
],
"stateMutability": "view"
}
]