// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import "@openzeppelin/contracts/utils/Address.sol"; import "../../interfaces/IContractPayoffProvider.sol"; /// @dev PayoffDefinition tyoe struct PayoffDefinition { PayoffDefinitionLib.PayoffType payoffType; PayoffDefinitionLib.PayoffDirection payoffDirection; bytes30 data; } using PayoffDefinitionLib for PayoffDefinition global; type PayoffDefinitionStorage is bytes32; using PayoffDefinitionStorageLib for PayoffDefinitionStorage global; /** * @title PayoffDefinitionLib * @dev Library that surfaces logic for PayoffDefinition type functionality * @notice Library for the PayoffDefinition type. Performs validity and price transformation based on the payoff definition type. */ library PayoffDefinitionLib { using Address for address; error PayoffDefinitionUnsupportedTransform(PayoffType payoffType, PayoffDirection payoffDirection); error PayoffDefinitionNotContract(PayoffType payoffType, bytes30 data); /// @dev Payoff function type enum enum PayoffType { PASSTHROUGH, CONTRACT } enum PayoffDirection { LONG, SHORT } /** * @notice Checks validity of the payoff definition * @param self a payoff definition * @return Whether the payoff definition is valid for it's given type */ function valid(PayoffDefinition memory self) internal view returns (bool) { if (self.payoffType == PayoffType.CONTRACT) return address(_providerContract(self)).isContract(); // All other payoff types should have no data return uint(bytes32(self.data)) == 0; } /** * @notice Transforms a price based on the payoff definition * @param self a payoff definition * @param price raw oracle price * @return Price transformed by the payoff definition function */ function transform( PayoffDefinition memory self, Fixed18 price ) internal view returns (Fixed18) { PayoffType payoffType = self.payoffType; PayoffDirection payoffDirection = self.payoffDirection; Fixed18 transformedPrice; // First get the price depending on the type if (payoffType == PayoffType.PASSTHROUGH) transformedPrice = price; else if (payoffType == PayoffType.CONTRACT) transformedPrice = _payoffFromContract(self, price); else revert PayoffDefinitionUnsupportedTransform(payoffType, payoffDirection); // Then transform it depending on the direction flag if (self.payoffDirection == PayoffDirection.LONG) return transformedPrice; else if (self.payoffDirection == PayoffDirection.SHORT) return transformedPrice.mul(Fixed18Lib.NEG_ONE); else revert PayoffDefinitionUnsupportedTransform(payoffType, payoffDirection); } /** * @notice Parses the data field into an address * @dev Reverts if payoffType is not CONTRACT * @param self a payoff definition * @return IContractPayoffProvider address */ function _providerContract( PayoffDefinition memory self ) private pure returns (IContractPayoffProvider) { if (self.payoffType != PayoffType.CONTRACT) revert PayoffDefinitionNotContract(self.payoffType, self.data); // Shift to pull the last 20 bytes, then cast to an address return IContractPayoffProvider(address(bytes20(self.data << 80))); } /** * @notice Performs a price transformation by calling the underlying payoff contract * @param self a payoff definition * @param price raw oracle price * @return Price transformed by the payoff definition function on the contract */ function _payoffFromContract( PayoffDefinition memory self, Fixed18 price ) private view returns (Fixed18) { bytes memory ret = address(_providerContract(self)).functionStaticCall( abi.encodeCall(IContractPayoffProvider.payoff, price) ); return Fixed18.wrap(abi.decode(ret, (int256))); } } /** * @title PayoffDefinitionStorageLib * @notice Library that surfaces storage read and writes for the PayoffDefinition type */ library PayoffDefinitionStorageLib { function read(PayoffDefinitionStorage self) internal view returns (PayoffDefinition memory) { return _storagePointer(self); } function store(PayoffDefinitionStorage self, PayoffDefinition memory value) internal { PayoffDefinition storage storagePointer = _storagePointer(self); storagePointer.payoffType = value.payoffType; storagePointer.payoffDirection = value.payoffDirection; storagePointer.data = value.data; } function _storagePointer( PayoffDefinitionStorage self ) private pure returns (PayoffDefinition storage pointer) { assembly { pointer.slot := self } // solhint-disable-line no-inline-assembly } }