v10 Update

Changelog

  • TradingCallbacksUtils and TradingInteractionsUtils are now external

  • New external libraries: FundingFeesUtils (new holding fees logic), TradeManagementCallbacks (pnl withdrawal, negative pnl/holding fees realization), TradingStorageGetters (previously ArrayGetters)

  • New GOV_EMERGENCY_TIMELOCK role (setRoles, diamondCut) => 10-hour emergency timelock so we can do emergency fixes

  • Post-v10 OI stored separately from pre-v10 (collateral + token OIs)

    • Old borrowing fees and new borrowing fees are charged on all OI (pre-v10 and post-v10)

    • New funding fees are only charged on post-v10 OI

    • Counter trade skew calculation uses post-v10 OI only

    • Skew price impact calculation uses post-v10 OI only

    • Max skew check uses post-v10 OI only

    • Pair max OI check uses all OI (pre-v10 and post-v10)

    • Pair OI updates now done from _updateOi instead of handleTradeBorrowingCallback (calls pre-v10 setter for pre-v10 trades and post-v10 setter for post-v10 trades), group OI still updated from handleTradeBorrowingCallback

  • New funding fees facet: funding fees and new borrowing fees

    • All new funding/borrowing fees data and settings are stored per pair and collateral

    • Everything is backwards compatible: a pair can have old borrowing fees, new funding fees, and new borrowing fees (any combination)

    • Funding rate per second's velocity per year = skew coefficient per year * net exposure in tokens, absolute value capped at max velocity setting, only > 0 when net exposure usd > theta threshold usd

    • Funding rate = last rate + seconds elapsed * velocity per year / seconds in a year, absolute value capped at max rate setting (hard cap at 10,000% APR)

    • Acc funding fees delta = avg funding rate per second % since last update * seconds elapsed * current pair price (incremented at every trade, whenever OI or a funding parameter changes), trade funding fee = (current acc funding fee - initial acc funding fee) * position size collateral / entry price / 100 = sum(avg funding rate per second % * seconds elapsed * current pair price) since trade opened * position size collateral / entry price / 100 = sum(funding rate % * current pair price * position size token / 100) since trade opened = sum(funding rate % * position size collateral dynamic / 100) since trade opened

    • Can enable/disable funding fees and enable/disable APR multiplier logic on each pair/collateral (resumes from last rate when we re-enable)

    • APR multipliers use the OI in token amounts (not collateral amounts) to calculate the ratio of longs/shorts (capped at 100x to prevent overflows), otherwise it wouldn't be balanced since the total funding fee on one side is acc fee delta / 100 * total OI token

    • New borrowing rate (same on each side) can be defined on each pair/collateral (hard cap at 1,000% APR)

    • Funding fees and borrowing fees are charged on the dynamic position size in collateral tokens (scaled by current price / entry price) to mimic CEXes behavior

  • New max OI skew in collateral tokens per pair/collateral (uses net skew in tokens * current pair price => dynamic OI collateral skew)

  • Pair max OI check now uses dynamic OIs in collateral tokens (= pre-v10 OI collateral + post-v10 OI token * current pair price)

  • Old borrowing fees refactor:

    • Old borrowing fees calculations now require current pair price (rate is calculated using dynamic OIs collateral)

    • Still charged on initial position size collateral for now, would require factoring acc fee

    • Borrowing pair params setter was refactored to use callback pattern

    • Borrowing groups however still use initial OIs for borrowing rate calculation and max OI check

  • New param updates callback mechanism:

    • Specific job id (market)

    • Uses address(1) to store pending order in order to have a global id

    • Pending param updates are stored in funding fees facet

    • New parameter value has to fit into uint224 (pending param update = collateral index, pair index, update type, and new value packed into 1 slot)

  • Before any trading action that impacts size of an open trade (partial add/close, full close), pending holding fees are realized (reset to 0, stored as realized pnl, if positive sent to vault without burn)

  • Realized pnl of each trade is split between realized pnl (only impacts trade value) and realized trading fees (impacts both trade value and available collateral in diamond)

  • 3 new other vars impact availble collateral in diamond:

    • manuallyRealizedNegativePnlCollateral: equal to the negative pnl we sent to the vault to balance potential closed pnl imbalances (eg. huge negative open pnl but all trades closing have positive pnl)

    • alreadyTransferredNegativePnlCollateral: equal to the total partial close negative pnl sent to the vault (for leverage delta > 0)

    • virtualAvailableCollateralInDiamond: when withdrawing collateral with positive pnl it's possible there isn't enough collateral in diamond, however available collateral in diamond must be >= 0 after withdrawing collateral (we prioritize capping manuallyRealizedNegativePnlCollateral at new collateral amount and if not enough we compensate through this variable); otherwise if negative then when user deposits collateral it doesn't increase available in diamond and can deposit/withdraw in loop to drain the vault

  • The function to realize trading fees on an open trade also checks if the trade can be liquidated after charging the fee, and if so liquidates it in order to avoid someone spamming trade actions potentially before we have time to liquidate him with trigger bots resulting in more fees than his trade can afford

  • Partial close refactor:

    • The value sent to the trader is now the partial collateral + net pnl - closing fee, exactly equivalent to the full close formula (later we could refactor to remove full close code and only use partial close code and just close trade when partial close size = trade size)

    • For collateral delta > 0 we just scale the full trade down (realized pnl and available collateral in diamond) so new trade value + sent to trader = prev trade value, the trading fee is taken from what we send to the trader (collateral sent to trader < 0 not allowed), and the available collateral in diamond used is the proportional available collateral in diamond based on the position size delta

    • For leverage delta > 0 we realize the partial unrealized raw pnl (since position size is lower if we didn't do this it would lower the trade's pnl) minus what we sent to the trader so new trade value + sent to trader (when positive, otherwise gets sent to vault) = prev trade value, we charge the trading fee on the open trade (transfers fee from vault if available in diamond = 0), the available collateral in diamond is always zero (removed old traderDebt logic which used to realize partial negative pnl by reducing trade collateral), and if net pnl is negative we send the partial negative pnl (pending + realized) minus the manually realized pnl and already transferred negative pnl (already sent to vault in a previous partial close) to the vault, capped at total trade available collateral in diamond, we don't just send the partial net pnl to the vault because for example realized trading fees are not in the diamond so it wouldn't make sense

  • UI realized pnl data contains a useful data breakdown to display for the website backend/frontend, since the realized pnl value used on-chain to calculate the trade value is not really what the user expects to see as realized pnl (eg. when withdrawing collateral we realize negative pnl even if this action corresponds to realizing positive pnl because we have to impact the trade's value negatively to prevent withdrawing the same pnl, or eg. the trade's realized pnl gets scaled down on partial closes so it doesn't correspond to the total anymore)

  • New counter trade mechanism:

    • Can only be below a defined leverage to qualify (check on trade opening, partial adds, leverage increases), set per pair

    • Has to improve skew (any size that worsens the skew is returned to the user as collateral or leverage delta reduced for partial adds when collateral delta = 0)

    • Uses dynamic OI collateral skew (skew token * current pair price)

    • Benefit from trading fees discount, set per pair

    • Both trades and limit/stop orders can be counter trades (once executed they turn into counter-trade trades)

  • New skew price impact:

    • Calculated using (existing skew token + trade size token / 2) / skew depth token (existing skew and trade size being signed integers)

    • Trade size token is calculated using position size collateral / price after fixed spread and cumulative volume price impact on partial add and full open, and using position size collateral delta * trade position size token / trade position size collateral on partial close and full close (the same value is used to update pair token OIs)

    • Not included in max negative pnl on open check since it's neutral for pnl when opening and closing (otherwise couldn't trade when skew is too high)

    • Calculated using post-v10 skew only, pre-v10 trades don't pay any skew price impact on close

    • Specific skew depth stored per pair and collateral (same on both sides)

  • Price impact refactor: now fixed spread, cumulative vol price impact, and skew price impact percentages are first summed and then applied to the oracle price instead of applying them one on top of another

  • Pre-v10 trades can no longer partial add otherwise it would be a way to bypass the new funding fees and skew price impact

  • Traders can now withdraw net pnl from their trades without impacting their position size

  • We can now realize the pending holding fees of an open trade (eg. to solve imbalance if more trades closed with earned funding fees than paid funding fees temporarily)

  • We can now realize the negative pnl of an open trade (eg. to solve imbalance if more trades closed with positive pnl than negative pnl temporarily) => includes closing fixed spread and closing skew price impact, not closing cumulative volume price impact to avoid over-estimating negative PnL

  • Significant gas optimization: getTrade and getTradeInfo turned internal and use the library directly to avoid overhead from calling diamond proxy

  • GToken vaults receiveAssets function now supports a _burn argument so we can send funding fees to the vault without burning them (since they are neutral, an equivalent amount of funding fees will be taken out of vault later)

  • All trading fees (opening, partial add/close etc.) / holding fees / partial negative pnl (leverage delta > 0) no longer impact the trade's collateral / position size when charged, they are stored as realized trading fees and just impact the trade's value / available collateral in diamond, so when a user opens a trade with 100 DAI at 20x it now opens exactly with 2,000 DAI size

  • Leverage validation refactor:

    • Partial add / increase leverage: Instead of validating the adjusted initial leverage to be below the max leverage, we now do this check on the new effective leverage (which increases when pnl is negative and decreases when pnl is positive)

    • Partial add/close and increase/decrease leverage: We only validate the adjusted initial leverage to be below 0.1x and uint24.max (unlikely to be hit)

    • Partial close / decrease leverage: removed minimum leverage checks apart from the adjusted initial leverage one (none on effective leverage), only one left is on full open (can easily be bypassed by partially closing or decreasing leverage after trade is opened but doesn't matter)

  • Partial close / Increase leverage: simplified minimum new collateral amount in requestion validation, now just has to be > min fee

  • Partial add refactor:

    • Removed new pos size > old pos size check in request validation since fees no longer impact the position size (still checks in callback because can happen due to rounding down of leverage when collateral delta > 0 but very unlikely)

    • Re-calculates the new position size after calculating new collateral / leverage (can be different than old pos + pos size delta due to leverage rounding)

    • Major bugfix: calculates existingPnlCollateral for new open price calculation using price after impact (before used raw oracle price)

    • New open price now rounded up for longs and down for shorts to avoid generating positive pnl due to rounding

    • Simplified oldPosSizePlusPnlCollateral calculation using int

  • Net trade value and liquidation price calculations now take into account the pending new funding/borrowing fees too as well as the realized pnl

  • Oracle changes:

    • Now expected to no longer reply anything when the market is closed (because now we need the current pair price to realize trading fees on a trade to check if it can be liquidated and if we don't have it and just allow requests there can be a spam vector)

    • Requests can no longer be responded to 1 hour after they are initiated to prevent from keeping them pending

    • Expected to pass the current pair price too for lookback orders now since we need it to calculate pending holding fees

  • Closing fees are now always charged on full close, before it used to check if collateral was higher than the closing fee but it doesn't make sense anymore since a trade's collateral can't go below the min fee (check in partial close and increase leverage), and if there isn't enough available collateral in diamond it becomes negative so it's sent from vault to diamond and then distributed

  • Leverage updates and partial closes now both check the existing and new liq price

  • Leverage updates no longer result in _updateOi calls even if there's a tiny position size difference due to rounding, doesn't matter

  • Only amount that gets burned anymore is the negative pnl on full close which is still available in diamond as well as partial close negative pnl (not holding fees, trading fees, or manually realized negative pnl)

  • Added liquidation check in _openTradePrep to prevent executing a limit/stop order with lookbacks which would be immediately liquidated due to current pair price having moved too much against trade

  • Liquidation price calc function now accepts additional fee as input to simulate liq price after charging a fee / realizing pnl

  • Old price aggregator orderAnswers mapping deprecated in favor of new one that can store the current pair price (prices of OrderAnswer are now uint56 instead of uint64, max price is 7,205,759), any pending order from before v10 that wasn't fulfilled will have to be canceled after timeout and re-initiated

  • AggregatorAnswer no longer contains spreadP, now fetched from within getTradeOpeningPriceImpact and getTradeClosingPriceImpact

  • New ContractsVersion value of V10 to differentiate from trades opened before v10 (for trades opened before v10: no funding fee, no skew price impact, separate OI mapping, cannot partial add)

  • Trade struct now contains two additional properties: isCounterTrade and positionSizeToken (still fits into 3 slots)

  • Removed isWithinExposureLimit check in partial add and open trade request validation because it now requires the current pair price (for dynamic max skew calculation), check is still in callback

  • getTradeFeesCollateral / processFees now accept fee as input so calling function has to refresh fee tier and calculate the fee before calling, and the current trade fee tier points are only counted towards the next trade (before, a trade's fee tier points were credited before this same trade was executed, potentially making it reach the next fee tier for this same trade, impacting its fee)

  • No longer refresh fee tier in triggerOrder when byPassesLinkCost = false because we use a fixed link cost

Last updated

Was this helpful?