KIP-100: Smart Margin v3
Simple Summary
Smart Margin v3 (SMv3) will serve as Kwenta's on-chain margin engine for trading derivatives powered by Synthetix v3. SMv3 will handle all trades created on the platform and serve as a single entry point for any on-chain integrators.
Abstract
Kwenta's SMv3 system will be a unified gateway for all platform trading, prioritizing minimalism and immutability. SMv3's "Engine" will operate as a singleton contract that can manage trader collateral, async order committing, and conditional order verification/execution.
Motivation
Synthetix v3
SMv2 was designed to interact exclusively with Synthetix v2 based perp markets. Consequently, Kwenta's current margin engine solution cannot support the newly introduced Synthetix v3 perp markets. SMv3 represents the first step in Kwenta's transition to supporting Synthetix v3 perp markets from an on-chain perspective.
Furthermore, SMv3 initiates Kwenta's shift to a fully immutable and trustless margin engine design. While maintaining the flexibility found in SMv2, SMv3 strives to remove state complexity and emphasize trade execution and trader experience.
Protocol Strength
Delegation
In SMv2, delegation was seldom used. However, SMv3 prioritizes it to support engine selectivity. This is managed through Synthetix v3's account module, which allows for permission-based delegation (3). This feature enables Kwenta to roll out new SM versions without requiring trader migration. Traders can choose their preferred trading engine and revoke permissions at will.
Protocol Architecture
Ultimately, SMv3 will unify trader interactions through a single contract rather than using individual SM accounts for each trader. This modification offers several benefits, including lower gas expenses, simplified complexity, and decreased risk linked to upgradeability, as SMv3 will be immutable (1).
Specifications
Overview
Engine
The margin engine will interact with Synthetix v3 perp markets, guided by functions like modifyCollateral
and commitOrder
on the Engine.sol
contract. With adequate interactions, the engine can completely manage Synthetix v3 perp market accounts for traders (1).
Multi-leg interactions within a single transaction are possible, similar to SMv2, thanks to its multicallable feature. However, prior Synthetix v3 account level authorization is needed for the engine to access/interact with these accounts. Traders must grant the Engine admin permission to facilitate interactions (1). These permissions are adjustable at any time, allowing account creators to include or exclude engines (4).
Any state-changing interaction will require an account ID as a parameter, allowing the margin engine to authenticate the trader through the Synthetix v3 account module, except when depositing collateral (which can be done by any address) (4).
Here is an example of a function that exists on the Engine contract that allows a trader to deposit or withdraw collateral:
function modifyCollateral(
uint128 _accountId,
uint128 _synthMarketId,
int256 _amount
) external override {
IERC20 synth = IERC20(_getSynthAddress(_synthMarketId));
if (_amount > 0) {
_depositCollateral(
msg.sender, synth, _accountId, _synthMarketId, _amount
);
} else {
if (!isAccountOwner(_accountId, msg.sender)) revert Unauthorized();
_withdrawCollateral(
msg.sender, synth, _accountId, _synthMarketId, _amount
);
}
}
Conditional Orders
In SMv3, Conditional Orders gain enhanced functionality due to a restructured defining object. With order submission moving off-chain, the object can be more intricate without inflating gas costs (2).
Conditional Orders specify both order details (IEngine.OrderDetails
) and on-chain verification criteria. This bundled data (IEngine.ConditionalOrder
) is signed off-chain by the account owner and executed on-chain when conditions are met, either by a trustedExecutor
or publicly if requireVerified
is true. See the IEngine.ConditionalOrder
and IEngine.OrderDetails
structs below:
struct OrderDetails {
uint128 marketId;
int128 accountId;
int128 sizeDelta;
uint128 settlementStrategyId;
uint256 acceptablePrice;
bool isReduceOnly;
bytes32 trackingCode;
address referrer;
}
struct ConditionalOrder {
OrderDetails orderDetails;
address signer;
uint128 nonce;
bool requireVerified;
address trustedExecutor;
bytes[] conditions;
}
Before execution, each order mandates basic on-chain verification for elements like nonce
, signer
, and signature
. Additional conditions can be specified through IEngine.ConditionalOrder.conditions
, a bytes[]
array containing function selectors for external calls on the engine contract. Example conditions include isTimestampAfter
, isPriceAbove
, and isMarketOpen
.
If requireVerified
is set to true, all specified conditions must be verified on-chain, allowing any address to execute the order. Conversely, if set to false, only a trustedExecutor
can execute, enabling platforms like Kwenta to manage and execute orders without incurring extra gas costs for condition verification.