Technical Overview

User Example

Proposed end-user transaction example for interacting with the YCabal

NOTE: Since the JSON-RPC spec allows responses to be returned in a different order than sent, we need a mechanism for choosing a canonical id from a list that doesn't depend on the order. This chooses the "minimum" id by an arbitrary ordering: the smallest string if possible, otherwise the smallest number, otherwise null.

order = {
	Give: ETH,
	Want: DAI,
	SlippageLimit: 10%,
	Amount: 1000ETH,
	Cabal: 0xabc...,
	FeesIn: DAI,
	TargetDEX: SushiSwap,
	Deadline: time.Now() + 1*time.Minute
	Signature: sign(order.SignBytes())
}

Now if the Cabal broadcasts this transaction with an arbitrage order, the transaction contains 2 orders:

Note: the transaction below is a mock-up for the proposed data fields

transactions = [
	{
		Give: ETH,
		Want: DAI,
		SlippageLimit: 10%,
		Amount: 1000ETH,
		Cabal: 0xabc...,
		FeesIn: DAI,
		TargetDEX: SushiSwap,
		Deadline: time.Now() + 1*time.Minute
		Signature: sign(order.SignBytes())
	},
	{
		Give: DAI,
		Want: ETH,
		SlippageLimit: 1%,
		Amount: 10ETH,
		Cabal: 0xabc...,
		FeesIn: DAI,
		TargetDEX: SushiSwap,
		Deadline: time.Now() + 1*time.Minute
		Signature: sign(order.SignBytes()),
		IsBackbone Cabal: true,
		TransferProfitTo: transactions[0].signer
	}
]

The arbitrage profit generated by second order is sent to the msg.sender of the first order.

The first order will still lose 5%(assumption) in slippage.

Arbitrage profits will rarely be more than the slippage loss.

If someone front runs the transaction sent by the Cabal:

  1. They pay for the gas while post confirmation of transaction the fees for order1 goes to the relayer in the signed order.
  2. They lose 5% in slippage as our real user does.

Engine

YCabal uses a batch auction-based matching engine to execute orders. Batch auctions were chosen to reduce the impact of frontrunning on the exchange.

  1. All orders for the given market are collected.

  2. Orders beyond their time-in-force are canceled.

  3. Orders are placed into separate lists by market side, and aggregate supply and demand curves are calculated.

  4. The matching engine discovers the price at which the aggregate supply and demand curves cross, which yields the clearing price. If there is a horizontal cross - i.e., two prices for which aggregate supply and demand are equal - then the clearing price is the midpoint between the two prices.

  5. If both sides of the market have equal volume, then all orders are completely filled. If one side has more volume than the other, then the side with higher volume is rationed pro-rata based on how much its volume exceeds the other side. For example, if aggregate demand is 100 and aggregate supply is 90, then every order on the demand side of the market will be matched by 90%.

Orders are sorted based on their price, and order ID. Order IDs are generated at post time and is the only part of the matching engine that is time-dependent. However, the oldest order IDs are matched first so there is no incentive to post an order ahead of someone else's.