Creating a New Market

Currently Perennial has permissioned market creation. However, in the future this will be opened up to anyone. Please join our discord to get the latest updates.

Perennial allows for permissionless market creation โ€” anyone can come in and launch any market they desire. We anticipate there will be markets with long / short <asset> payoffs for many types of assets, duplicate markets for assets that tweak LP/risk parameters, and many exotic defi-native markets.

Each market created in Perennial has a market operator controlled by an on-chain address (typically a multisig). The market operator serves a few functions:

  • Design & deploy the market โ€” pick market, choose price feed & utilization curve, launch market

  • Set and tune parameters โ€” set initial parameters and adjust them in response to market Manage

  • Market evolution โ€” upgrade the market, help with distribution to users, and more

In return, the market operator receives a portion of the fees collected by the market, creating an incentive for market creation.

Because market operators have the ability to change important system parameters, we recommend that operator keys are managed by a multisig with a timelock (such that there is a delay before changes are implemented).

Most markets will be operated by ecosystem participants โ€” For the Long-SQTH pool, the market operator is Opyn team, and the key is controlled by Opyn protocol core multisig w/ timelock.

Some markets will be protocol-owned โ€” For the Long-ETH & Short-ETH markets, the Perennial protocol will be the market operator, and the key is initially controlled by Perennial's core multisig w/ timelock.

Initializing a Market

In order to create a market there are a number of parameters you need to consider. These will impact the operation & safety of the market. See the values explained below:

Market Definition

Tokens

token is the underlying asset that the market settles in. It must match the quote currency of the oracle.

reward is the token that rewards are paid out in. It must not match the token. It may initially be set to the zero address, but once non-zero it may not be changed.

ParametersInvariants
  • token

  • reward

Oracle

oracle is the underlying oracle provider that feeds in the marketโ€™s underlying price.

payoff is the payoff provider that transforms the oracleโ€™s underlying price to create the marketโ€™s price. The payoff may be set to the zero address to signify an identity function.

ParametersInvariants
  • oracle

  • payoff

Roles coordinator is allowed to update the risk parameters and claim any accrued risk fee.

beneficiary is allowed to claim and accrued donation.

ParametersInvariants
  • coordinator

  • beneficiary

Risk Parameters

Parameters per market, set by the risk coordinator.

Position Fees

Skew is a measure of when a markets Takers, long & short, aren't equal:

skew=longโˆ’shortmax(long,short)skew = \frac {long - short} {max(long, short)}

To derive the TakerFee we use the Skew we determine what the appropriate fee is when opening position fees:

takerfee=notionalโˆ—(takerFee+โˆฃskewโ€™โˆ’skewโˆฃโˆ—takerSkewFee+(โˆฃskewโ€™โˆฃโˆ’โˆฃskewโˆฃ)โˆ—takerImpactFee)taker fee = notional * ( takerFee + |skewโ€™ - skew| * takerSkewFee + (|skewโ€™| - |skew|) * takerImpactFee )

Similarly, for the MakersFee in the market we calculate their fee based on the utilization of the present collateral:

makerfee=notionalโˆ—(makerFee+(utilizationโ€™โˆ’utilization)โˆ—makerImpactFee)maker fee = notional * ( makerFee + (utilizationโ€™ - utilization) * makerImpactFee )

ParametersInvariants
  • takerFee

  • takerSkewFee

  • takerImpactFee

  • makerFee

  • makerImpactFee

  • takerFee <= protocolParameter.maxFee

  • takerSkewFee <= protocolParameter.maxFee

  • takerImpactFee <= protocolParameter.maxFee

  • makerFee <= protocolParameter.maxFee

  • makerImpactFee <= protocolParameter.maxFee

Collateralization

An account is eligible for liquidation when:

collateral<max(notionalโˆ—maintenance,minMaintenance)collateral < max( notional * maintenance, minMaintenance )

During a liquidation, the liquidator is eligible to take at most

liquidationfee=min(max(notionalโˆ—maintenanceโˆ—liquidationFee,minLiquidationFee),maxLiquidationFee)liquidation fee = min( max( notional * maintenance * liquidationFee, minLiquidationFee ), maxLiquidationFee )

Since oracle prices are only pushed upon request, account updates (position / collateral) are blocked when the market is stale:

oracle.currentTimestampโˆ’oracle.latestTimetamp>staleAfteroracle.currentTimestamp - oracle.latestTimetamp > staleAfter

If the market is stale a new price must be posted with the update in order to โ€œfreshenโ€ it.

ParametersInvariants
  • maintenance

  • minMaintenance

  • liquidationFee

  • minLiquidationFee

  • maxLiquidationFee

  • staleAfter

  • maintenance >= protocolParameter.minMaintenance

  • minMaintenance <= protocolParameter.maxAbsolutefee

  • liquidationFee <= protocolParameter.maxCut

  • minLiquidationFee <= protocolParameter.maxAbsolutefee

  • maxLiquidationFee <= protocolParameter.maxAbsolutefee

  • staleAfter <= 32 bits

Limits

We define efficiency (the ratio of maximum taker notional over back maker notional) as:

efficiency=maker/max(long,short)efficiency = maker / max(long, short)

The global position of the market must satisfy*:

maker<makerLimitEfficiency>efficiencyLimitmaker < makerLimit Efficiency > efficiencyLimit

* only applies when the position is increased

ParametersInvariants
  • makerLimit

  • efficiencyLimit

  • makerLimit <= 48 bits

  • efficiencyLimit >= protocolParameter.minEfficiency

Funding

Funding is determined via a P-controller over long vs short skew.

fundingโ€™=min(funding+(((skewโ€™+skew)/2)โˆ—1/pController.kโˆ—(tโ€™โˆ’t)),pController.max)fundingโ€™ = min( funding + ( ((skewโ€™ + skew) / 2) * 1 / pController.k * (tโ€™ - t) ), pController.max )
ParametersInvariants
  • pController.k

  • pController.max

  • pController.k <= 48 bits

  • pController.max <= protocolParameter.maxRate

Interest

Incentivizes the delta-neutral capital costs of makers by deducting an interest rate from both long and short over a jump rate utilization curve.

U = JumpRate utilization curve

u=maxโก(long,short)maker+minโก(long,short)Interest=minโก(maker,long+short)โˆ—U(u)\begin{align*} u &= \frac {\max(long, short)} {maker + \min(long, short)} \\ Interest &= \min(maker, long + short) * U(u) \end{align*}
ParametersInvariants
  • utilizationCurve.minRate

  • utilizationCurve.targetRate

  • utilizationCurve.maxRate

  • utilizationCurve.targetUtilization

  • utilizationCurve.minRate <= protocolParameter.maxRate

  • utilizationCurve.targetRate <= protocolParameter.maxRate

  • utilizationCurve.maxRate <= protocolParameter.maxRate

  • utilizationCurve.targetUtilization <= 1

Miscellaneous

When makerReceiveOnly is active, the sign of the funding will instantaneously flip when the sign of the skew flips instead of gradually rebalancing through the P-controller. This is useful if we want to ensure that the makers always receive non-negative funding.

An amount of virtualTaker is added to both the long and short sides of the market for the purpose of funding and taker fee skew and impact calculations. This helps dampen fee volatility in new markets without a critical mass of activity.

ParametersInvariants
  • makerReceiveOnly

  • virtualTaker

  • makerReceiveOnly

  • virtualTaker <= 64 bits

Market Parameters

Parameters per market, set by the market factory owner.

Fees

fundingFee, interestFee, and positionFee are the percentages of the funding, interest, and maker / taker fees respectively that are collected as fees by the market.

The settlementFee is a fixed dollar-term amount that is collected to incentivize the oracle keeper any time a new oracle price is requested.

The pooled fees are split between the market and the protocol and market as follows:

protocolfee=totalfeesโˆ—protocolFeemarketfee=totalfeesโˆ’protocolfee\begin{align*} protocol fee &= total fees * protocolFee \\ market fee &= total fees - protocol fee \end{align*}

The market fee is then further split as follows:

oraclefee=marketfeeโˆ—oracleFee+settlementFeeriskfee=marketfeeโˆ—riskFee\begin{align*} oracle fee &= market fee * oracleFee + settlementFee \\ risk fee &= market fee * riskFee \end{align*}

With the oracle fee going to the oracle factory of the market, and the risk fee being claimable by the risk coordinator.

Any leftover market fees are recorded as a donation and given to the beneficiary to support any miscellaneous additional fees.

ParametersInvariants
  • fundingFee

  • interestFee

  • positionFee

  • oracleFee

  • riskFee

  • settlementFee

  • fundingFee <= protocolParameter.maxCut

  • interestFee <= protocolParameter.maxCut

  • positionFee <= protocolParameter.maxCut

  • oracleFee + riskFee <= 1

  • settlementFee <= protocolParameter.maxFeeAbsolute

Pending Limits

Because of Perennialโ€™s delayed-settlement system, there may be multiple pending positions that need to be settled during a market flywheel touch.

Since this is a loop, we want to limit the worst case gas consumption of this process by capping the number of pending positions in the market.

maxPendingGlobal limits the quantity of global pending positions, while maxPendingLocal limits the quantity of local pending positions per account.

Once hit, this limit will soft-pause the market until the keepers are able to catch up.

ParametersInvariants
  • maxPendingGlobal

  • maxPendingLocal

  • maxPendingGlobal <= 16 bits

  • maxPendingLocal <= 16 bits

Incentives

Incentive rewards may be dripped to all three sides of the market at their individually specified rates per second:

  • makerRewardRate

  • longRewardRate

  • shortRewardRate

The reward tokens are optimistically accrued meaning the market owner is responsible for ensuring the market contract has a suitable amount of reward tokens to satisfy outstanding accruals. There are no direct invariants to account for this.

ParametersInvariants
  • makerRewardRate

  • longRewardRate

  • shortRewardRate

  • makerRewardRate <= 40 bits

  • longRewardRate <= 40 bits

  • shortRewardRate <= 40 bits

Miscellaneous

When takerCloseAlways is active, long and short positions may always close even when their closure will bring the market into socialization. Likewise when makerCloseAlways is active, maker positions may always close even when their closure will bring the market into socialization. If inactive, for both cases, the closure will instead revert.

When closed is active, the market is in a closed-only state. No funding, interest, or profit & loss, or rewards are accrued, and positions may only be reduced. Liquidations and position fees are still active during this time.

ParametersInvariants
  • takerCloseAlways

  • makerCloseAlways

  • closed

Protocol Parameters

Protocol-wise parameters, set by the market factory owner.

Fee

protocolFee is the cut of the market fee that is taken by the protocol.

ParametersInvariants
  • protocolFee

protocolFee <= protocolParameter.maxCut

Parameter Limits

Limits apply to parameters in all instance markets as specified above.

ParametersInvariants
  • maxFee

  • maxFeeAbsolute

  • maxCut

  • maxRate

  • minMaintenance

  • minEfficiency

  • maxFee <= 24 bits

  • maxFeeAbsolute <= 48 bits

  • maxCut <= 24 bits

  • maxRate <= 32 bits

  • minMaintenance <= 24 bits

  • minEfficiency <= 24 bits

Last updated