Contracts Documentation 0.8.4
Symmio Change-log Version 0.8.4
Settlement of Unrealized PnL (UPNL) in Symmio
In our latest upgrade to Symmio, we've introduced an important feature: the ability to settle unrealized profit and loss (UPNL) through the settleUpnl
method. This enhancement addresses the challenges that arise when users leverage their UPNL to open new positions but face difficulties during the closing of positions due to insufficient allocated balances.
Background
In Symmio, users can use their unrealized profits to open new positions, effectively allowing their UPNL to back them up from being liquidated. While this provides greater flexibility and leverage for users, it introduces a potential issue during the closing of positions. When a position is closed, the losing party must pay the profit to the winning party from their allocated balances, i.e., realized PNL, not from UPNL.
Consider a scenario where a user has multiple positions:
Position A: Significant unrealized profit.
Position B: New position opened using the unrealized profit from Position A.
If Position B incurs a loss, the user may not have sufficient allocated balance to cover the loss when closing Position B, even though their overall UPNL is positive. This situation prevents the hedger (counterparty) from being able to fill the close request, as the required funds are not available in the user's allocated balance.
Previously, we mitigated this by warning users through the frontend that they couldn't close certain positions due to insufficient realized PNL, advising them to close profitable positions first. However, with the introduction of automated features like stop-loss and take-profit bots (e.g., in IntentX), this manual intervention is no longer feasible.
Additionally, users could exploit the force close option by quickly allocating more funds and forcing the close of their positions, potentially leading to losses for hedgers if they cannot react in time.
The settleUpnl
Method
settleUpnl
MethodTo resolve these issues, we've introduced the settleUpnl
method, which allows hedgers to settle a portion of the user's unrealized PNL, effectively converting it into realized PNL. This enables the hedger to fill the user's close requests, even when the user lacks sufficient allocated balance.
The settleUpnl
method functions similarly to our implementation for funding rates, where we updated the openedPrice
of quotes.
Method Interface
Parameters:
settlementSig: Contains settlement data, including UPNL of Party A and Party Bs, and signatures.
updatedPrices: An array of updated prices for the quotes, reflecting the settlement of PNL.
partyA: The address of the user (Party A).
All quotes involved must be associated with the same Party A but can involve multiple Party Bs (hedgers). The method ensures that all parties are solvent before proceeding.
Key Features
Nonce Incrementation: The nonce for all parties involved is incremented, ensuring the integrity and synchronization of state changes.
Cooldown Mechanism: To prevent hedgers from repeatedly settling UPNL for quotes involving other hedgers, a cooldown period is enforced. A hedger cannot settle UPNL for another hedger's quotes more than once in a period.
Force Close Handling
In the code above, there's a check called isForceClose
. What's that about? Well, until now in this document, I've only discussed scenarios where Party A doesn't have enough realized money for hedgers to fill their close requests. However, the reverse can also happenâa user might want to force-close a position, but the hedger doesn't have sufficient allocated balance. For these scenarios, we've added a method called settleAndForceClosePosition
. As the name suggests, it first settles UPNLs for the hedger and then closes the position.
Reserve Vault for Hedgers
We've also introduced a reserve vault for hedgers. This vault allows hedgers to deposit funds that are not tied to any specific Party A and are used exclusively during force close operations when the hedger might become insolvent with the close. The reserve vault helps protect hedgers from liquidation in these scenarios.
Deposit and Withdraw Interface
Detailed Example
Let's illustrate how the settleUpnl
method works with a detailed example.
Scenario
Bob (Party A) has three open positions and zero allocated balance:
Position 1 with Rasa Hedger (Party B1):
Unrealized Profit: $300
Quote ID:
1
Position 2 with PerpsHub Hedger (Party B2):
Unrealized Profit: $100
Quote ID:
2
Position 3 with PerpsHub Hedger (Party B2):
Unrealized Loss: $250
Quote ID:
3
Bob wants to close Position 3, which has an unrealized loss of $250. However, since Bob has zero allocated balance, he cannot cover this loss upon closing.
Problem
PerpsHub Hedger (Party B2) cannot fill Bob's close request for Position 3 because Bob doesn't have enough allocated balance to cover the $250 loss. Even though Bob has an overall positive UPNL ($150 net profit), his profits are unrealized and cannot be used to settle the loss directly.
Solution with settleUpnl
settleUpnl
To enable the closing of Position 3, PerpsHub Hedger needs to settle Bob's unrealized profits from Positions 1 and 2, converting them into realized profits. This will increase Bob's allocated balance sufficiently to cover the loss.
PerpsHub only needs to settle enough unrealized profit to cover the $250 loss. They can achieve this by settling Position 2 and part of Position 1. Hedgers should prioritize settling UPNLs from their own positions with users, as they face a cooldown period when settling positions involving other hedgers.
PerpsHub initiates
settleUpnl
for Position1 and Position2:
This call will:
Update the opened prices for Position1 and Position2
Realize $150 profit for Position1 and $100 profit for Position2
Adjust Bob's allocated balance: +$150 +$100 = $250 increase
Adjust Rasa's and PerpsHub's allocated balances accordingly
After settlement, Bob's state:
Allocated balance: $250
Position2 has fully settled profit
Position1 has partially settled profit ($150 out of $300)
Position3 remains unchanged
PerpsHub can now successfully fill the close request for Position3, as Bob has sufficient allocated balance ($250) to cover the loss ($250).
MultiAccount Changes
In the previous version of multiAccount, we introduced an option that allows users to delegate access to hedgers or any address to call functions on Symmio on their behalf. This feature laid the foundation for many powerful capabilities in Symmio, such as stopLoss, takeProfit, InstantOpen, and InstantClose. Users could revoke this access at any time. However, this freedom in revoking access created challenges for hedgers. For instance, hedgers might assume they could sendQuote on behalf of users during the instantOpen process, only to discover their access had been revoked after they had already hedged the position. In this update, we've implemented a cooldown period between when a user decides to revoke access and when that revocation takes effect. The process now requires users to first propose revoking their access using the following method:
After the revokeCooldown
period has elapsed, users can then actually revoke their access by calling the following method:
This two-step process allows hedgers to know in advance that they will lose their access, preventing potential complications later.
Internal Transfer For PartyBs
In the previous version, we introduced an option for users to transfer funds between their accounts without waiting for the deallocate cooldown. This function was initially limited to Party As only.
In this version, we've removed the notPartyB modifier on this method, allowing Party Bs to use it as well. Although Party Bs don't have multiple accounts to transfer between, they can use this method for a different purpose. Currently, Party Bs have balance manager processes that constantly monitor and allocate/deallocate funds for their users. This frequent activity resets their deallocate cooldown each time. With this new method, Party Bs can transfer some of their funds to a different account and let them remain there, allowing the cooldown period for that account to pass without interruption.
SymmioPartyB Changes
Given that the _multicastCall
function allows hedgers to call any method from any contract, it was necessary to address potential reentrancy attacks. As a result, we've added the nonReentrant
modifier to the _executeCall
method. We chose to implement this logic ourselves rather than using OpenZeppelin's version because we needed to upgrade the existing contracts. Changing the inheritance structure would have prevented us from doing so, as it would have altered the contract's storage layout.
Other Code Improvements
Due to the growth of the PartyAFacet and PartyBFacet contracts in this version, they exceeded the size limit for on-chain deployment. To address this, we restructured the codebase:
We divided PartyBFacet into three separate facets:
PartyBQuoteActionsFacet
PartyBPositionsActionsFacet
PartyBGroupActionsFacet
All force actions of PartyA were moved to a new facet called ForceActionsFacet.
The corresponding libraries were also split to maintain consistency with the new structure.
Libraries like LibMuon were further divided into smaller components to manage contract size limitations.
Last updated