// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/utils/math/Math.sol"; import "@equilibria/root/number/types/UFixed18.sol"; import "../IProduct.sol"; import "./Accumulator.sol"; import "./PrePosition.sol"; import "./PackedPosition.sol"; /// @dev Position type struct Position { /// @dev Quantity of the maker position UFixed18 maker; /// @dev Quantity of the taker position UFixed18 taker; } using PositionLib for Position global; /** * @title PositionLib * @notice Library that surfaces math and settlement computations for the Position type. * @dev Positions track the current quantity of the account's maker and taker positions respectively * denominated as a unit of the product's payoff function. */ library PositionLib { /** * @notice Creates a packed position from an position * @param self A position * @return New packed position */ function pack(Position memory self) internal pure returns (PackedPosition memory) { return PackedPosition({maker: self.maker.pack(), taker: self.taker.pack()}); } /** * @notice Returns whether the position is fully empty * @param self A position * @return Whether the position is empty */ function isEmpty(Position memory self) internal pure returns (bool) { return self.maker.isZero() && self.taker.isZero(); } /** * @notice Adds position `a` and `b` together, returning the result * @param a The first position to sum * @param b The second position to sum * @return Resulting summed position */ function add(Position memory a, Position memory b) internal pure returns (Position memory) { return Position({maker: a.maker.add(b.maker), taker: a.taker.add(b.taker)}); } /** * @notice Subtracts position `b` from `a`, returning the result * @param a The position to subtract from * @param b The position to subtract * @return Resulting subtracted position */ function sub(Position memory a, Position memory b) internal pure returns (Position memory) { return Position({maker: a.maker.sub(b.maker), taker: a.taker.sub(b.taker)}); } /** * @notice Multiplies position `self` by accumulator `accumulator` and returns the resulting accumulator * @param self The Position to operate on * @param accumulator The accumulator to multiply by * @return Resulting multiplied accumulator */ function mul(Position memory self, Accumulator memory accumulator) internal pure returns (Accumulator memory) { return Accumulator({ maker: Fixed18Lib.from(self.maker).mul(accumulator.maker), taker: Fixed18Lib.from(self.taker).mul(accumulator.taker) }); } /** * @notice Scales position `self` by fixed-decimal `scale` and returns the resulting position * @param self The Position to operate on * @param scale The Fixed-decimal to scale by * @return Resulting scaled position */ function mul(Position memory self, UFixed18 scale) internal pure returns (Position memory) { return Position({maker: self.maker.mul(scale), taker: self.taker.mul(scale)}); } /** * @notice Divides position `self` by `b` and returns the resulting accumulator * @param self The Position to operate on * @param b The number to divide by * @return Resulting divided accumulator */ function div(Position memory self, uint256 b) internal pure returns (Accumulator memory) { return Accumulator({ maker: Fixed18Lib.from(self.maker).div(Fixed18Lib.from(UFixed18Lib.from(b))), taker: Fixed18Lib.from(self.taker).div(Fixed18Lib.from(UFixed18Lib.from(b))) }); } /** * @notice Returns the maximum of `self`'s maker and taker values * @param self The struct to operate on * @return Resulting maximum value */ function max(Position memory self) internal pure returns (UFixed18) { return UFixed18Lib.max(self.maker, self.taker); } /** * @notice Sums the maker and taker together from a single position * @param self The struct to operate on * @return The sum of its maker and taker */ function sum(Position memory self) internal pure returns (UFixed18) { return self.maker.add(self.taker); } /** * @notice Computes the next position after the pending-settlement position delta is included * @param self The current Position * @param pre The pending-settlement position delta * @return Next Position */ function next(Position memory self, PrePosition memory pre) internal pure returns (Position memory) { return sub(add(self, pre.openPosition), pre.closePosition); } /** * @notice Returns the settled position at oracle version `toOracleVersion` * @dev Checks if a new position is ready to be settled based on the provided `toOracleVersion` * and `pre` and returns accordingly * @param self The current Position * @param pre The pending-settlement position delta * @param toOracleVersion The oracle version to settle to * @return Settled position at oracle version * @return Whether a new position was settled */ function settled( Position memory self, PrePosition memory pre, IOracleProvider.OracleVersion memory toOracleVersion ) internal pure returns (Position memory, bool) { return pre.canSettle(toOracleVersion) ? (next(self, pre), true) : (self, false); } /** * @notice Returns the socialization factor for the current position * @dev Socialization account for the case where `taker` > `maker` temporarily due to a liquidation * on the maker side. This dampens the taker's exposure pro-rata to ensure that the maker side * is never exposed over 1 x short. * @param self The Position to operate on * @return Socialization factor */ function socializationFactor(Position memory self) internal pure returns (UFixed18) { return self.taker.isZero() ? UFixed18Lib.ONE : UFixed18Lib.min(UFixed18Lib.ONE, self.maker.div(self.taker)); } }