- rate limits on swap amount per time period
- max size per swap
- shut down if there’s a settled oracle price that’s far away from true price
- filter for tight max slippage
- shut down during very high volatility ( >0.1% on a 4-second timeframe for ETHUSD)
Basic swap life cycle
openSwap is an RFQ-like system, so there are multiple steps to each swap.- Swap is proposed, fulfillment fee starts escalating
- Liquidity provider matches swap, locking in fulfillment fee. Bounty to oracle game initial reporter starts escalating
- Initial reporter claims bounty, oracle game plays out and eventually settles
- Upon settlement, swap is executed at the final oracle price.
Core events
When a swap is created, you will see the following event:matchSwap reverts if block.timestamp > expiration. This is not a post-match timeout — see latencyBailout for the post-match cancel window.
priceTolerated and toleranceRange are the core slippage parameters. priceTolerated is in units of 1e18 * sellToken / buyToken, where the ratio of sellToken to buyToken is the current price. For example, if sellToken were ETH and buyToken were USDC, and 1 ETH = 3000 USDC, a priceTolerated of 1e18 * (1e18 / 3000e6) would be accurate.
toleranceRange is the max deviation from priceTolerated allowed by openSwap, in 1e7 units. Here, a toleranceRange of 1000 implies a 0.01% max deviation from priceTolerated. If the toleranceRange around priceTolerated is violated, the swap refunds (both participants get their original amounts back).
For safety it is recommended to filter swaps for priceTolerated and toleranceRange that leave you no more than 0.5% away from the slippage barrier on the side that hurts you. If a proposed swap does not match that filtering, reject it.
fulfillFeeParams represents the escalating fulfillment fee parameters which have their own dedicated section below.
blockTimestamp is simply the block.timestamp of the block the event is in.
oracleParams represents the openOracle game parameters which have their own dedicated section below.
bountyParams represents the initial reporter bounty contract parameters which have their own dedicated section below.
gasCompensation represents the amount in wei you as a matcher are compensated with in exchange for calling the matchSwap() function. This is paid for by the swapper. The matchSwap function is ~1 million gas.
If a swap has been matched, you will see the Swap Matched event, and you can no longer match that specific swapId:
Fulfillment fee parameters
The fulfillFeeParams object in the SwapCreated event looks like this:- 1000: 0.01%
- 1002: 0.01%
- 1004: 0.012%
- 1006: 0.012%
- 1008: 0.0144%
- 1010: 0.0144%
- 1012: 0.01728%
- 1014: 0.01728%
- 1016: 0.01728%
- 1000: 0.01%
- 1002: 0.02%
- 1004: 0.04%
- 1006: 0.08%
- 1008: 0.1%
- 1010: 0.1%
- 1012: 0.1%
- 1014: 0.1%
- 1016: 0.1%
Quick note on slippage
Since the swapper sets the slippage parameters at time of swap creation, and you accept the swap later, the market may drift while you wait for the fulfillment fee to increase enough to be worth it. It’s more of a cost to the swapper than the matcher if slippage is breached, since you as a matcher get your original funds returned, while the swapper paid the initial reporter in the oracle game as well as gas compensation to you as the matcher. It is up to you if you want to match swaps that are close to the priceTolerated walls when the fulfill fee is worth it to match.Oracle parameters
Once you match a swap, the oracle game is kicked off. The rules of the game are determined by the oracleParams object in the SwapCreated event:Bounty parameters
A bounty is paid to the first person to report a price in the oracle game. In openSwap, the swapper pays the bounty, and its structure is defined by the bountyParams object in the SwapCreated event:How much of a fulfill fee to demand?
From our work on linear notional manipulation, it seems like ~30% of the settlement time volatility is a reasonable fulfillment fee floor with oracle parameters of initial liquidity >10% of swap size and multipliers between ~1.15x and 1.3x. For example, if settlementTime is 4 seconds, you can calculate a standard deviation of a time series of 4-second returns and use that. protocolFee and gas fees as % initial liquidity should be added to your minimum demanded fulfillment fee as well. To the extent the swapping flow is not adversarial, you can reduce the volatility-scaled portion of the demanded fulfillment fee significantly, though it is still recommended to follow the basic safety precautions outlined at the top of the page.Reserving tokens to dispute
As a matcher, you can reserve some WETH and USDC tokens to play the oracle game and defend your position if desired. Once the swap is matched, you see the reportId of the oracle game that decides your fate:Dynamic slippage
Since costs in the oracle game are proportional to volatility and people can attempt drain-bribery attacks where they pay all capital attached to the oracle game to deposit money into the contract and try to slip a bad price through, you can have your slippage tolerated (distance to slippage barrier that hurts you) be a function of prevailing market volatility. Something like max(0.5%, 7 * settlementTime volatility) may be acceptable. As the dispute network becomes harder to reason about, defenses like this become less necessary.Matching the swap
Once you have determined the SwapCreated event parameters are enticing enough per above filtering rules, you can call the below smart contract function to match the swap:getSwapHash() function in the smart contract unless you are running a full node, since this fully trusts the RPC to give you the hash corresponding to the SwapCreated event data.