Technical Integration
Preamble
This guide is for new integrators starting from scratch. If you have an existing v1 integration and want to migrate to V2 then refer please to FuturesV1 -> PerpsV2 (Mintaka).
Contracts
All PerpsV2
contracts are routed proxies. That is, for the most part, there's a single entry point to perform most of your integration needs. You can see all available PerpsV2
contracts on GitHub by filtering by PerpsV2
prefix or infix on .sol
file names (see GitHub). Here is a quick snapshot:
There are 3 primary points of interaction: ProxyPerpsV2
, PerpsV2MarketSettings
, and FuturesMarketManager
(note: does not have a PerpsV2
prefix/suffix).
ProxyPerpsV2
Each PerpsV2 market is deployed as a distinct instance of the ProxyPerpsV2
contract, uniquely identified by a marketKey
. Below are all available PerpsV2 markets:
Optimism
Optimism Sepolia
Due to contract size limitations, we've designed ProxyPerpsV2
to be a router of sorts. The router proxies calls downstream to a collection of registered targets, which can be described by calling getAllTargets
on ProxyPerpsV2
. Although an implementation detail and likely not impactful for integration, it may helpful to understand how these contracts link together.
In the past, we've found integrating with proxy contracts can sometimes be difficult to work with as definitions are scattered across an array of different targets. For simplicity, we've defined a consolidated interface for ABI generation which can be found here.
FuturesMarketManager
As the name would suggest, it's simply just a management contract for markets available on perps. It's a top-level contract that provides a holistic scan of all available markets, consolidated views to query data in aggregate, and acts as a registry to track new markets as they are deployed.
Although this does not provide mutative methods to perform actions externally (as mutative operations are applied on the market level), it may be useful during debugging/integration to retrieve metadata for either display or inference or even to help avoid hardcoded values in your keepers.
PerpsV2MarketSettings
Checkout the Useful Links section for details.
Contract Addresses
How to Transfer Margin
All PerpsV2 markets have isolated margin. Position A on the ETH market will not affect position B on the BTC market. This is important as it means margin must always be transferred before positions can be opened on new markets.
A positive marginDelta
is considered a deposit, negative is a withdrawal and zero is a no-op. To ensure users do not receive revert errors refer to the minInitialMargin
on the minimum required margin a position must have.
How to Open a Position
There is only ONE method to open a position: delayed off-chain orders.
🚨🚨🚨 ONLY integrate using delayed off-chain orders. DO NOT use the legacy order types listed at the bottom of the section. 🚨🚨
Delayed off-chain orders (ONLY Use This Trading Method)
Similar to DelayedOrders
, Delayed off-chain orders also follow a familiar interface and operate asynchronously. From an integration standpoint, this is almost exactly the same as delayed orders (with the exception of differing function names and execution).
Off-chain orders require off-chain prices, price feed update data needs to be submitted upon execution. Off-chain prices are queried against Pyth and passed into executeOffchainOrder
as a bytes[]
array. On-chain we perform a variety of checks and if all pass, the order is executed and a position is opened.
If the Pyth off-chain price deviates too far from on-chain prices, the execution will revert and a position will not be opened. This deviation can also be found in PerpsV2MarketSettings
.
Executing or canceling an off-chain order using a non-off-chain method will result in revert. The inverse is also true.
Orders can go stale. We consider an order stale if it hasn't been executed for a long enough period. Stale orders can no longer be executed and can only be cancelled.
A keeper fee is also charged on order submission however completely refunded if the executing account is the same as the submitter.
Building a keeper that is reliable, cost efficient, and up to date to execute orders across all markets in a timely manner is hard. We provide a keeper that you can run, fork, or use as documentation to build one of your own.
It's recommended to use off-chain delayed orders as the preferred method to open positions. It has the lowest fees and provides the fastest execution, giving traders the best UX.
❌❌❌ DO NOT use the below order types, and instead use Delayed off-chain orders documented above. The below order types are only included for completeness. ❌❌❌
How to Close a Position
To close a position is the same as modifying an existing position with equal but inverted size. For example to close a 100 ETH long, you simply short -100 ETH. Once a position has been opened, regardless of how (i.e. order type), it can be closed in any method, async or otherwise. The only difference is the execution and fees charged on exit.
What is Price Impact / Fill Price?
Price impact refers to the price change in the market that is directly caused by your trade. In perps, price impact is relative to skew. Skew is the size delta between all open long and short positions, always optimising for a perfectly balanced skew. For example, market that is 80% long and 20% short is skewed 60% long.
If the position’s size increases/expands the market skew, they are charged a premium in the form of a higher fill price but if the skew is decreases/contracts, a discount is applied. This happens automatically when a trade is made and it affects both atomic and delayed orders.
If you're building a frontend to quote trader's entry/exit price, it's important to display this fill price. We provide a fillPrice
view to help with this:
This topic is extensively covered in SIP-279, example simulation code, and intrinsically within the smart contract codebase.
Price Impact Delta (units)
priceImpactDelta
argument passed to open/close methods are percentages between 0 and 1. For clarity below are some examples:
The specified priceImpactDelta
provides trade price protection, particularly for delayed orders. Price may move and many orders/trades may be executed between submission and execution. Providing an acceptable delta prevents the trade from proceeding if the price moves too much.
priceImpactDelta
is then used to derive the trade's upper/lower acceptable price limit and reverting if above or below (depending on long or short respectively).
What are Funding Rates?
Funding rates on Synthetix perps is one mechanism that encourages a balanced market. A balanced market is one such that the size of longs is equal to size of shorts. A market skew is present when longs do not balance shorts or vice versa. Whenever the market is imbalanced in the sense that there is more open interest on one side, SNX holders take on market risk.
Funding rates floats. It moves up and down proportional to the skew with a funding rate velocity that dictates how quickly it can move in either direction. In practice, the effect of this is that funding rates will continuously drift higher/lower in the presence of uncorrected position imbalances, creating a natural price discovery mechanism for funding rates while simultaneously smoothing out funding rate trajectories.
We provide two methods to retrieve both the current funding rate and current funding velocity.
Funding rates are stored in 24hr periods. To calculate the hourly funding rate:
Trading Fees
Traders are charged makerFee
if the size of their order reduces the market skew and a takerFee
if the order increases the skew. However, in certain situations, an order can decrease and increase the skew. In these scenarios, the proportion of size that decreases the skew should be charged a makerFee
and the remaining, a takerFee
. Below is a concrete example:
We provide an orderFee
view to assist with fee calculation.
Note that orderFee
does not consider the keeper fee paid out for async order executions. A static fee is paid out from a trader's available margin on successful execution. However is fully refunded if the trader is the executor.
Trade Simulations
In certain situations, it's often useful to see the result of a trade without making it. You can simulate a trade for a particular sender
(i.e. trader) by invoking the postTradeDetails
view.
Note that if tradePrice == 0
then we use the current oracle price.
Looking to migrate an existing integration? Take a look at our migration guides.
Last updated