Market administration

Everything on this page is called by a market owner (or someone stepping into the role). A market is created by createMarket — whoever calls it becomes the owner of the resulting pair and can later transfer that role, update the LP whitelist, or permanently stop new swaps.

Source: SwapCore.sol

Market lifecycle

createMarket ──► (market is live) ──► transferMarketOwnership ──► acceptMarketOwnership
                       │                                               │
                       │                                               ▼
                       │                                       (new owner active)

                       └──► setMarketLpWhitelist (if lpWhitelistEnabled)

                       └──► terminateMarket ──► (no new swaps; existing swaps still settle)

Note that every call to createMarket produces two market IDs — one BUY_FIXED and one BUY_FLOATING — that share the same oracles, collateral token, term, and config. The two sides are independent markets from an ownership and accounting standpoint, but transferMarketOwnership / terminateMarket / setMarketLpWhitelist each operate on a single marketId, so if you want to change both sides you call each function twice.


createMarket

function createMarket(
    address referenceRateOracle,
    address baseSwapRateOracle,
    address swapToken,
    uint64  leverageMultiplier,
    uint32  swapTerm,
    uint256 utilFeeSlopeWad,
    uint256 kinkUtilization,
    uint256 maxKinkFeeWad,
    bool    earlyExitAllowed,
    uint256 earlyExitFee,
    uint256 liquidationIncentive,
    uint32  numBuckets,
    uint32  bucketInterval,
    address fixedRiskPremiumOracle,
    address floatingRiskPremiumOracle,
    bool    lpWhitelistEnabled,
    uint88  minCollateral,
    Types.RateConvention rateConvention
) external payable nonReentrant returns (bytes32 fixedMarketId, bytes32 floatingMarketId);

Who calls: anyone. No prior permission required — but if the protocol has a market creation fee configured in Admin, msg.sender has to pay it (ETH or ERC20, depending on CREATE_MARKET_FEE_TOKEN).

What it does: atomically creates a BUY_FIXED / BUY_FLOATING market pair. For each side, it validates oracles, checks parameter bounds, generates a unique market ID (hashing the params with creator, timestamp, and a nonce), initializes the pool and rate index, and emits MarketCreated + MarketConfigured. The caller is recorded as marketOwner on both sides.

Parameters:

Parameter
Meaning

referenceRateOracle

Oracle that provides the floating rate used by the swap's floating leg. Shared between the two sides of the pair.

baseSwapRateOracle

Oracle that provides the tenor-dependent base rate used in swap pricing and collateral sizing. Shared between sides.

swapToken

ERC20 used for collateral and settlement. Must be a standard ERC20 (no rebasing, no fee-on-transfer).

leverageMultiplier

WAD-scaled leverage multiplier. 1e18 = 1x, 2e18 = 2x. Capped at 18e18.

swapTerm

Duration of each swap in seconds. Must be > 0.

utilFeeSlopeWad

Slope (WAD) of the linear region of the utilization fee curve. Capped at 10e18.

kinkUtilization

Utilization at which the quadratic region kicks in (WAD, must be < 1e18).

maxKinkFeeWad

Maximum additional fee from the quadratic region (WAD, capped at 10e18).

earlyExitAllowed

Whether buyers can close a swap before expiry via exitSwapEarly.

earlyExitFee

WAD early-exit fee. Capped at 1e18 (100%).

liquidationIncentive

WAD fraction paid to a liquidator from the liquidated party's collateral. Must be < 1e18.

numBuckets

Number of time buckets. Must be in [1, 365].

bucketInterval

Seconds per bucket. Must be ≥ 3600 and ≤ swapTerm. numBuckets × bucketInterval must ≥ swapTerm.

fixedRiskPremiumOracle

Optional risk premium oracle for the BUY_FIXED side. Pass address(0) to disable.

floatingRiskPremiumOracle

Optional risk premium oracle for the BUY_FLOATING side.

lpWhitelistEnabled

If true, only addresses on marketLpWhitelist[marketId] can supplyCollateral. The caller is auto-whitelisted.

minCollateral

Minimum per-swap collateral (both buyer and LP). Denominated in swapToken decimals. Must be > 0.

rateConvention

How the reference rate oracle provides data. See Types.RateConvention (Cumulative, SpotRate, or SpotCompoundRate).

Returns: (fixedMarketId, floatingMarketId). These are also published in MarketCreated events.

Payable: if Admin.CREATE_MARKET_FEE_AMOUNT() > 0 and CREATE_MARKET_FEE_TOKEN() == address(0), send the fee as msg.value. Excess ETH is refunded to msg.sender at the end of the call. If the fee token is an ERC20, approve SwapCore for the fee amount before calling — no msg.value is required.

State changes / events:

  • markets[fixedMarketId] and markets[floatingMarketId] populated, both marked exists = true, marketOwner = msg.sender.

  • Both IDs appended to allMarketIds.

  • Global rateIndex initialized for the reference rate oracle if it wasn't already.

  • If lpWhitelistEnabled, marketLpWhitelist[<each>][msg.sender] = true (creator is auto-whitelisted so they can seed liquidity).

  • Emits MarketCreated (×2) and MarketConfigured (×2), plus MarketCreationFeeCollected if a fee was charged.

Reverts:

Code
Reason

E150

ETH fee required but msg.value < feeAmount.

E151

ETH send to multisig or refund to caller failed.

E301

Market ID collision (should not happen in practice — nonces are unique).

E600 / E603

Base rate oracle call reverted or returned isValid = false.

E602 / E605

Risk premium oracle (if set) call reverted or returned isValid = false.

E606

Reference rate oracle is unhealthy during rateIndex.initialize.

E710

referenceRateOracle == address(0).

E711

baseSwapRateOracle == address(0).

E712

swapToken == address(0).

E713

leverageMultiplier == 0 or > 18e18.

E714

liquidationIncentive >= 1e18.

E715

swapTerm == 0.

E716

kinkUtilization >= 1e18.

E717

earlyExitFee > 1e18.

E718

utilFeeSlopeWad > 10e18.

E719

maxKinkFeeWad > 10e18.

E703

numBuckets == 0 or > 365.

E704

bucketInterval < 3600 or > swapTerm.

E705

numBuckets × bucketInterval < swapTerm.

E720

Invalid rateType (unreachable from the enum but guarded).

E721

minCollateral == 0.

See also: getAllMarketIds, transferMarketOwnership, terminateMarket.


transferMarketOwnership

Who calls: the current marketOwner of marketId.

What it does: step one of a two-step ownership handoff. Sets pendingMarketOwner[marketId] = newOwner. No state on the market itself changes yet — the actual owner only flips when newOwner accepts.

Pass newOwner = address(0) to cancel an in-flight transfer. There is no renounceOwnership — a market always has an owner once created.

Modifiers: marketOwner(marketId) (reverts with E202 if not the current owner, E300 if the market does not exist).

State changes / events:

  • pendingMarketOwner[marketId] = newOwner.

  • Emits MarketOwnershipTransferStarted(marketId, currentOwner, newOwner).

Reverts:

  • E300 — market doesn't exist.

  • E202 — caller is not the current market owner.

See also: acceptMarketOwnership.


acceptMarketOwnership

Who calls: the address previously set as pendingMarketOwner[marketId].

What it does: step two of the handoff. Flips markets[marketId].marketOwner to msg.sender and clears the pending slot. After this call, the new owner has full control over terminateMarket and setMarketLpWhitelist, and will receive the creator fee split on new swaps (if configured in Admin).

State changes / events:

  • markets[marketId].marketOwner = msg.sender.

  • pendingMarketOwner[marketId] = address(0).

  • Emits MarketOwnerChanged(marketId, msg.sender).

Reverts:

  • E300 — market doesn't exist.

  • E202 — caller is not the pending owner.


terminateMarket

Who calls: the marketOwner of marketId.

What it does: permanently flips markets[marketId].terminated = true. From that block forward, buySwap reverts on this market (see E304). Existing swaps are unaffected — LPs can still withdraw, keepers can still makePayment, liquidators can still liquidateSwap, and buyers can still exitSwapEarly if it was enabled at market creation. This is a one-way switch; there is no unterminate.

Terminate when you want to wind a market down cleanly — new positions can't be opened, but open ones still settle.

Modifiers: marketOwner(marketId).

State changes / events:

  • markets[marketId].terminated = true.

  • Emits MarketAdminChange(marketId, "terminateMarket", true).

Reverts:

  • E300 — market doesn't exist.

  • E202 — not market owner.

  • E304 — market is already terminated.


setMarketLpWhitelist

Who calls: the marketOwner of marketId.

What it does: toggles an address's presence in marketLpWhitelist[marketId]. Only meaningful if the market was created with lpWhitelistEnabled = true — otherwise the call reverts. supplyCollateral checks this mapping inline and reverts with E201 if the target LP isn't listed. withdrawCollateral is not gated on the whitelist — an LP who is removed can still withdraw their existing shares.

The market creator is auto-whitelisted at createMarket time, so there's always at least one LP who can seed the pool.

Modifiers: marketOwner(marketId).

State changes / events:

  • marketLpWhitelist[marketId][lp] = status.

  • Emits MarketLpWhitelistUpdated(marketId, lp, status).

Reverts:

  • E300 — market doesn't exist.

  • E202 — not market owner.

  • E205lpWhitelistEnabled == false on this market (you can't toggle entries on a market that doesn't use a whitelist).

See also: supplyCollateral.


getAllMarketIds

Who calls: anyone — frontends, indexers, scripts.

What it does: returns every market ID that has ever been created, in creation order. BUY_FIXED and BUY_FLOATING sides appear consecutively for each createMarket call. Use the returned IDs as keys into the public markets(bytes32) getter to read full configs.

There is no pagination — if you expect thousands of markets, read this via staticCall off-chain rather than from on-chain code that would run out of gas.

See also: markets mapping getter.

Last updated