Hello Nomads, it has been a monumental 4th quarter of 2024. During Q4, Onomy restructured its leadership, ripped out what did not meet high standards, conducted a rebrand, upgraded the chain to v0.50 of the SDK, built the entire Reserve stablecoin issuance platform, its interface, and launched a successful testnet leading to its currently production-ready status for mainnet launch. All of this was done with zero treasury, funded entirely by remaining founding team like Lalo at TGS Digital and OG Nomads like the X-Chain Validator Alliance (Nomadic, Nomblocks, DeFi Scandinavia) as a strong attempt to revive Onomy to fulfill its potential (Make Onomy Great Again) and overcome challenges rather than give up. We hope that the community appreciates these efforts and understands that what we have accomplished so far on limited resources is extremely rare. In fact, we have been told to “cut losses” back in July/August - but our ambition triumphed over the naysayers (still to this day). It has been nothing but expense for us, but we believe its worth giving everything we’ve got. Cosmos is undergoing a renaissance with new leadership from Interchain Labs (formerly Skip) and the tech stack is in the best place it has ever been for on-chain Forex.
Moving forward, we have completed our research for the architecture of the dCLOB Exchange in our path to become the home of IBC Stableswaps and on-chain Forex.
Development of the exchange will require support on ICS/PSS with Interchain Labs, and support on technical fronts, liquidity support, and ongoing funding. Each of these items are currently being addressed with a proposal made to Interchain Labs (the new steward of Cosmos) themselves - hence the delay on a community update as we look to arrange the support required.
Now, here’s a look at what’s cooking:
dCLOB Exchange Preview Design Documentation
- Perpetual market
- Spot market
- Multiple sub-accounts management
Perpetual Market
Perpetual futures contracts are leveraged trading instruments that enable traders to speculate on asset prices without a fixed expiration date. Unlike traditional futures contracts, perpetual futures remain open indefinitely and are settled in cash, removing the requirement for physical delivery of the underlying asset.
Onomy’s perpetual market will consist of trading pairs between various national currencies based stable coins (minted from Reserve protocol) paired with USD.
Perpetual Market Trading Lifecycle
Funding Payment Mechanism
As perpetual futures contracts never expire, creating a potential disconnect between the contract price and the underlying asset’s spot price. To keep these prices in sync, a mechanism called funding payments is used.
For the exchange module this will be done by a time epoch. At begin block we need to check when the epoch elapsed and execute the payment between longs and shorts. The perpetual market info stores all the params for funding payment process and epoch tracking. And also the cumulative funding and cumulative price for calculating the funding rate and funding payment
eg:
type PerpetualMarketInfo struct {
// hourly_funding_rate_cap defines the maximum absolute value of the funding rate
FundingRateCap math.LegacyDec
// hourly_interest_rate defines the cap value of interest rate
InterestRate math.LegacyDec
// next_funding_timestamp defines the next funding timestamp
NextFundingTimestamp int64
}
type PerpetualMarketFunding struct {
// cumulative_funding defines the cumulative funding of a perpetual market.
CumulativeFunding math.LegacyDec
// cumulative_price defines the cumulative price for the current hour up to the last timestamp
CumulativePrice math.LegacyDec
LastTimestamp int64
}
For every epoch, compute the funding rate as:
fundingRate = twap + hourlyInterestRate
where twap = cumulativePrice / (timeInterval * 24)
with timeInterval = lastTimestamp - startingTimestamp
The cumulativePrice
is calculated with every trade. There will be a funding rate cap if the absolute value of computed funding rate is higher than the cap then we will use the funding rate cap but retain the funding rate sign. When the funding rate is positive (contract price is higher than spot price), longs pay shorts. This incentivizes trading activity that could potentially bring the contract price down towards the spot price. And opposite when funding rate is negative.
FundingPayment = FundingRate * MarketPrice
. MarketPrice
is provided by oracle module.
Market Creation
A market can be created either by the instant launch functionality through a messages MsgInstantPerpetualMarketLaunch
which creates a market by paying an extra fee which doesn’t require governance to approve it. Or it is created in the normal way through governance through MsgPerpetualMarketLaunchProposal
. Market information can be updated by the market admin by broadcasting MsgUpdatePerpetualMarket
transaction.
Order Management
An orderbook will be created for each market containning list of resting orders. This orderbook also in charge of handling orders matching every block.
Initial Margin Requirement
This is the requirement for the initial margin of an order when creating a new position. The idea behind the additional mark price requirement is to minimize the liquidation risk when traded prices and market prices temporally diverge too far from each other. Given the initial margin ratio, an order must fulfill two requirements:
- The margin must fulfill:
Margin ≥ InitialMarginRatio * Price * Quantity
For example: in a market with maximally 20x leverage, the initial
margin ratio would be 0.05. Any new position will have a margin which is at least 0.05 of its notional. - The margin must fulfill the mark price requirement:
Margin >= Quantity * (InitialMarginRatio * MarkPrice - PNL)
PNL is the expected profit and loss of the position if it was closed at the current MarkPrice. Solved for MarkPrice this results in:
- For Buys:
MarkPrice ≥ (Margin - Price * Quantity / ((InitialMarginRatio - 1) * Quantity)
- For Sells:
MarkPrice ≥ (Margin + Price * Quantity / ((InitialMarginRatio + 1) * Quantity)
Initial margin ratio is different between markets and is provided when creating the market.
Placing Limit Orders
Limit orders allows a user to specify a maximum price they are willing to pay when buying, or a minimum price they are willing to accept when selling. The order is only executed if the market reaches the specified price (or better).
A user can place a limit buy or sell order by sending a MsgCreatePerpetualLimitOrder
. Upon submission, the order can be:
- Immediately (fully or partially) matched against other opposing resting orders on the orderbook in the Endblocker batch auction, thus establishing a position for the user.
- Added to the orderbook.
Note that in the case of a large order where there is insufficient asset amount it is possible for an order to be partially matched and for the remaining unmatched portion to be added to the
orderbook.
User must provide a portion of the total contract value as initial margin in stable coin as collateral. The list of allowed denoms are stored in the module params and can be updated through
governance proposal.
User cancels a limit buy or sell order by sending a MsgCancelPerpetualOrder
which removes the user’s limit order from the orderbook.
Placing Market Orders
Market orders allow user to buy or sell an asset immediately at the best available market price at the batch process during end block. Market orders prioritize execution speed over price control.
If the order is an atomic typed order then the order is execute immediately bypassing the batch execution during end block by paying an extra fees.
User can place a market buy/sell order at market price though MsgCreatePerpetualMarketOrder
message. Other order handler logic are the same as limit order ( partial fills )
The order margin collateral are the account available balance.
Position Management
After a market order or limit order matched, a Position will be created for the user. A trader’s position records the conditions under which the trader has entered into the derivative contract and is defined as follows
- Position Definition:
Quantity
EntryPrice
Margin
HoldQuantity
CumulativeFundingEntry
As an example, consider the following position in the NomYEN/NomUSD market:
Quantity
= -2EntryPrice
= 0.0064Margin
= 800HoldQuantity
= 1CumulativeFundingEntry
= 34323
This position represents short exposure for 2 contracts of the NomYEN/NomUSD market collateralized with 800 NomUSD, with an entry price of 0.0064. The HoldQuantity
represents the quantity of the position that the trader has opposing orders for.
CumulativeFundingEntry
represents the cumulative funding value that the position was last updated at. Users can then modify their poisition by:
Increase margin: User submit IncreasePositionMargin
message to increase position margin.
Decrease margin: User submit DecreasePositionMargin
message to increase position margin.
NewMargin must again be validated with the above mentioned (requirements)
Position Netting
When a new order is matched for a subaccount with an existing position, the new position will be the result from netting the existing position with the new order. A matched order produces a position delta defined by FillQuantity, FillMargin and ClearingPrice. New position state will be
update:
- Same direction:
Entry Price = (Quantity * EntryPrice + FillQuantity * ClearingPrice) / (Quantity + FillQuantity) Quantity = Quantity + FillQuantity Margin = Margin + FillMargin
- Opposite direction:
Entry Price no change Quantity = Quantity - FillQuantity Margin = Margin * (Quantity - FillQuantity) / Quantity
Liquidation
When your position falls below the maintenance margin ratio ( defined when
creating the market ), the position can be liquidated by anyone.
For longs:
Margin >= Quantity ∗ MaintenanceMarginRatio ∗ MarketPrice − (MarkPrice−EntryPrice)
For shorts:
Margin >= Quantity ∗ MaintenanceMarginRatio ∗ MarketPrice − (EntryPrice−MarkPrice)
What happens on-chain is that automatically a reduce-only market order of the same size as the position is created. The market order will have a worse price defined as Infinity or 0, implying it will be matched at whatever prices are available in the order book.
The payout from executing the reduce-only market order will not go towards the position owner. Instead, a part of the remaining funds are transferred to the liquidator and the other part is transferred to the insurance fund. The split rate is defined in the module params. If the
payout in the position was negative then the insurance fund will cover the missing funds.
Also note that liquidations are executed immediately in a block before any other order matching occurs.
Spot Market
Spot Market Creation
A market can be instant launched by broadcasting MsgInstantSpotMarketLaunch
transaction with an extra listing fee accounted in, this will be transfered to the community pool. Or through governance proposal.
Order Management
User can broadcast MsgBatchCreateSpotLimitOrders
or MsgCreateSpotMarketOrder
transaction to create a spot limit/market order. To cancel a limit order, use MsgCancelSpotOrder
transaction.
Market status
Spot market has four different statuses:
- Active
- Paused
- Suspended
- Demolished
Active State
If a spot market is an active state, it can accept orders and trades.
Paused State
If a spot market is a paused state, it will no longer accept orders and trades and will also not allow any users to take actions on that market (no order cancellations).
Suspended State
If a spot market is a suspended state, it will no longer accept orders and trades, and will only allow traders to cancel their orders.
Demolished State
When a market becomes demolished, all outstanding orders are cancelled.
During the beginning block, identifies and handles any spot markets that have been flagged for force-closure. Cancels open orders, updates relevant market data, and sets the market status to paused or closed.
Order Matching and Execution
The order matching and execution process involves the following steps:
- Match Orders: Match buy and sell orders based on price-time priority. This includes both limit and market orders.
- Execute Orders: Execute matched
orders and update user balances accordingly. For buy orders, the quote
asset is deducted, and the base asset is credited. For sell orders, the
base asset is deducted, and the quote asset is credited. - Update Order Book: Remove executed orders from the order book and update the state to reflect the remaining orders.
Sub-account
Users can manage multiple sub-accounts to allocate funds for specific positions. This feature enables efficient fund management.
Balance Management
Each of the sub accounts has it own balance record which is being manage by the DEX module. Each balance record will refer to a sub-account by the sub-account ID and denom and each record will track the sub-accounts available balance and total balance.
e.g
message Balance {
SubaccountId string
Denom string
Deposits *Deposit
}
type Deposit struct {
AvailableBalance math.LegacyDec
TotalBalance math.LegacyDec
}
Operations
Users can update their sub account balance records by:
Deposit
Token will be transferred from user account to the DEX module account. And a
balance records of subaccount will record the amount of money deposited.
To deposit, user can submit a MsgDeposit
transaction
MsgDeposit
are defined as:
type MsgDeposit struct {
// user account
Sender string
// subaccount address.
SubaccountId string
// deposit amount
Amount types.Coin
}
Withdraw
Users can withdraw token from their sub-account to their bank account. This will trigger
- Checking subaccount balance records for availibility
- Reduce subaccount balance records for the withdrawed amount
- Transfer token from DEX module account to user account
To withdraw token, user can submit a MsgWithdraw
transaction
MsgWithdraw
are defined as:
type MsgWithdraw struct {
// user address
Sender string
// bytes32 subaccount ID to withdraw funds from
SubaccountId string
// withdraw amount
Amount types.Coin
}
Sub-account transfer
Users can transfer token between sub-accounts they own. When transferring between sub-accounts, the amount in those account’s balance records will be updated accordingly.
To transfer token between their own sub-accounts, user can submit a MsgSubaccountTransfer
transaction. This transaction will check if the sub-account is b
MsgSubaccountTransfer
are defined as:
type MsgSubaccountTransfer struct {
// user address
Sender string
// sender sub-account address
SourceSubaccountId string
// receiver sub-account address
DestinationSubaccountId string
// transfer amount
Amount types.Coin
}
External Transfer
Like Sub-account transfer, users can also transfer token to other users sub-account. This will update those sub-accounts balance records respectively.
To transfer token to other user sub-account, user can submit a MsgExternalTransfer
transaction
MsgExternalTransfer
are defined as:
type MsgExternalTransfer struct {
// user address
Sender string
// sender sub-account address
SourceSubaccountId string
// receiver sub-account address
DestinationSubaccountId string
// transfer amount
Amount types.Coin
}
Estimated Development Roadmap & Milestones
1. Milestone 1
Goal :
- Finalize full documentation
- Initial design for web-app
Estimated time: 2 weeks
2. Milestone 2
Goal:
- Implement spot market module and multiple sub-accounts management mechanism
- Enhance the existing oracle module to support oracle requests from the DEX module
- Complete the spot market functionality within the web application (UI)
Estimated time: 6 weeks
3. Milestone 3
Goal:
- Implement perpetual market module and liquidate mechanism
- Implement funding rate mechanism
- Complete the perpetual market functionality within the web application (UI)
- Skip API support for DEX swap and PSM swap
Estimated time: 8 weeks
4. Milestone 4
Goal
- Upgrade current testnet or launch new testnet for testing features in protocol
- Get everything ready (code, documents, guide for user) for launching protocol onmainnet
Estimated time : 3 weeks
5. Milestone 5
Goal:
- Launch protocol on mainnet
Estimated time : 1 week
Please keep in mind that timelines are estimates and heavily dependent on resources available. The community will be made aware when development begins from various DAO contributors.