Source Code
Overview
APE Balance
APE Value
$0.00Multichain Info
N/A
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
GNSTradingInteractions
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 750 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../abstract/GNSAddressStore.sol";
import "../abstract/GNSReentrancyGuard.sol";
import "../../interfaces/libraries/ITradingInteractionsUtils.sol";
import "../../interfaces/types/ITradingStorage.sol";
import "../../libraries/TradingInteractionsUtils.sol";
/**
* @dev Facet #7: Trading (user interactions)
*/
contract GNSTradingInteractions is GNSAddressStore, GNSReentrancyGuard, ITradingInteractionsUtils {
// Initialization
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @inheritdoc ITradingInteractionsUtils
function initializeTrading(
uint16 _marketOrdersTimeoutBlocks,
address[] memory _usersByPassTriggerLink
) external reinitializer(8) {
TradingInteractionsUtils.initializeTrading(_marketOrdersTimeoutBlocks, _usersByPassTriggerLink);
}
// Management Setters
/// @inheritdoc ITradingInteractionsUtils
function updateMarketOrdersTimeoutBlocks(uint16 _valueBlocks) external onlyRole(Role.GOV) {
TradingInteractionsUtils.updateMarketOrdersTimeoutBlocks(_valueBlocks);
}
/// @inheritdoc ITradingInteractionsUtils
function updateByPassTriggerLink(
address[] memory _users,
bool[] memory _shouldByPass
) external onlyRoles(Role.GOV_TIMELOCK, Role.GOV_EMERGENCY) {
TradingInteractionsUtils.updateByPassTriggerLink(_users, _shouldByPass);
}
// Interactions
/// @inheritdoc ITradingInteractionsUtils
function initiateManualNegativePnlRealization(
address _trader,
uint32 _index,
bool _isHoldingFees
) external nonReentrant onlyRoles(Role.GOV, Role.MANAGER) {
TradingInteractionsUtils.initiateManualNegativePnlRealization(_trader, _index, _isHoldingFees);
}
/// @inheritdoc ITradingInteractionsUtils
function setTradingDelegate(address _delegate) external nonReentrant {
TradingInteractionsUtils.setTradingDelegate(_delegate);
}
/// @inheritdoc ITradingInteractionsUtils
function removeTradingDelegate() external nonReentrant {
TradingInteractionsUtils.removeTradingDelegate();
}
/// @inheritdoc ITradingInteractionsUtils
function delegatedTradingAction(address _trader, bytes calldata _callData) external returns (bytes memory) {
return TradingInteractionsUtils.delegatedTradingAction(_trader, _callData);
}
/// @inheritdoc ITradingInteractionsUtils
function openTrade(
ITradingStorage.Trade memory _trade,
uint16 _maxSlippageP,
address _referrer
) external nonReentrant {
TradingInteractionsUtils.openTrade(_trade, _maxSlippageP, _referrer);
}
/// @inheritdoc ITradingInteractionsUtils
function openTradeNative(
ITradingStorage.Trade memory _trade,
uint16 _maxSlippageP,
address _referrer
) external payable nonReentrant {
TradingInteractionsUtils.openTradeNative(_trade, _maxSlippageP, _referrer);
}
/// @inheritdoc ITradingInteractionsUtils
function updateMaxClosingSlippageP(uint32 _index, uint16 _maxSlippageP) external nonReentrant {
TradingInteractionsUtils.updateMaxClosingSlippageP(_index, _maxSlippageP);
}
/// @inheritdoc ITradingInteractionsUtils
function closeTradeMarket(uint32 _index, uint64 _expectedPrice) external nonReentrant {
TradingInteractionsUtils.closeTradeMarket(_index, _expectedPrice);
}
/// @inheritdoc ITradingInteractionsUtils
function updateOpenOrder(
uint32 _index,
uint64 _triggerPrice,
uint64 _tp,
uint64 _sl,
uint16 _maxSlippageP
) external nonReentrant {
TradingInteractionsUtils.updateOpenOrder(_index, _triggerPrice, _tp, _sl, _maxSlippageP);
}
/// @inheritdoc ITradingInteractionsUtils
function cancelOpenOrder(uint32 _index) external nonReentrant {
TradingInteractionsUtils.cancelOpenOrder(_index);
}
/// @inheritdoc ITradingInteractionsUtils
function updateTp(uint32 _index, uint64 _newTp) external nonReentrant {
TradingInteractionsUtils.updateTp(_index, _newTp);
}
/// @inheritdoc ITradingInteractionsUtils
function updateSl(uint32 _index, uint64 _newSl) external nonReentrant {
TradingInteractionsUtils.updateSl(_index, _newSl);
}
/// @inheritdoc ITradingInteractionsUtils
function updateLeverage(uint32 _index, uint24 _newLeverage) external nonReentrant {
TradingInteractionsUtils.updateLeverage(_index, _newLeverage);
}
/// @inheritdoc ITradingInteractionsUtils
function updateLeverageNative(uint32 _index, uint24 _newLeverage) external payable nonReentrant {
TradingInteractionsUtils.updateLeverageNative(_index, _newLeverage);
}
/// @inheritdoc ITradingInteractionsUtils
function increasePositionSize(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice,
uint16 _maxSlippageP
) external nonReentrant {
TradingInteractionsUtils.increasePositionSize(
_index,
_collateralDelta,
_leverageDelta,
_expectedPrice,
_maxSlippageP
);
}
/// @inheritdoc ITradingInteractionsUtils
function increasePositionSizeNative(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice,
uint16 _maxSlippageP
) external payable nonReentrant {
TradingInteractionsUtils.increasePositionSizeNative(
_index,
_collateralDelta,
_leverageDelta,
_expectedPrice,
_maxSlippageP
);
}
/// @inheritdoc ITradingInteractionsUtils
function decreasePositionSize(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice
) external nonReentrant {
TradingInteractionsUtils.decreasePositionSize(_index, _collateralDelta, _leverageDelta, _expectedPrice);
}
/// @inheritdoc ITradingInteractionsUtils
function withdrawPositivePnl(uint32 _index, uint120 _amountCollateral) external nonReentrant {
TradingInteractionsUtils.withdrawPositivePnl(_index, _amountCollateral);
}
/// @inheritdoc ITradingInteractionsUtils
function triggerOrder(uint256 _packed) external nonReentrant {
TradingInteractionsUtils.triggerOrder(_packed);
}
/// @inheritdoc ITradingInteractionsUtils
function triggerOrderWithSignatures(
uint256 _packed,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external nonReentrant {
TradingInteractionsUtils.triggerOrderWithSignatures(_packed, _signedPairPrices);
}
/// @inheritdoc ITradingInteractionsUtils
function cancelOrderAfterTimeout(uint32 _orderIndex) external nonReentrant {
TradingInteractionsUtils.cancelOrderAfterTimeout(_orderIndex);
}
// Getters
/// @inheritdoc ITradingInteractionsUtils
function getWrappedNativeToken() external view returns (address) {
return TradingInteractionsUtils.getWrappedNativeToken();
}
/// @inheritdoc ITradingInteractionsUtils
function isWrappedNativeToken(address _token) external view returns (bool) {
return TradingInteractionsUtils.isWrappedNativeToken(_token);
}
/// @inheritdoc ITradingInteractionsUtils
function getTradingDelegate(address _trader) external view returns (address) {
return TradingInteractionsUtils.getTradingDelegate(_trader);
}
/// @inheritdoc ITradingInteractionsUtils
function getMarketOrdersTimeoutBlocks() external view returns (uint16) {
return TradingInteractionsUtils.getMarketOrdersTimeoutBlocks();
}
/// @inheritdoc ITradingInteractionsUtils
function getByPassTriggerLink(address _user) external view returns (bool) {
return TradingInteractionsUtils.getByPassTriggerLink(_user);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {CBORChainlink} from "./vendor/CBORChainlink.sol";
import {BufferChainlink} from "./vendor/BufferChainlink.sol";
/**
* @title Library for common Chainlink functions
* @dev Uses imported CBOR library for encoding to buffer
*/
library Chainlink {
uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase
using CBORChainlink for BufferChainlink.buffer;
struct Request {
bytes32 id;
address callbackAddress;
bytes4 callbackFunctionId;
uint256 nonce;
BufferChainlink.buffer buf;
}
/**
* @notice Initializes a Chainlink request
* @dev Sets the ID, callback address, and callback function signature on the request
* @param self The uninitialized request
* @param jobId The Job Specification ID
* @param callbackAddr The callback address
* @param callbackFunc The callback function signature
* @return The initialized request
*/
function initialize(
Request memory self,
bytes32 jobId,
address callbackAddr,
bytes4 callbackFunc
) internal pure returns (Chainlink.Request memory) {
BufferChainlink.init(self.buf, defaultBufferSize);
self.id = jobId;
self.callbackAddress = callbackAddr;
self.callbackFunctionId = callbackFunc;
return self;
}
/**
* @notice Sets the data for the buffer without encoding CBOR on-chain
* @dev CBOR can be closed with curly-brackets {} or they can be left off
* @param self The initialized request
* @param data The CBOR data
*/
function setBuffer(Request memory self, bytes memory data) internal pure {
BufferChainlink.init(self.buf, data.length);
BufferChainlink.append(self.buf, data);
}
/**
* @notice Adds a string value to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param value The string value to add
*/
function add(
Request memory self,
string memory key,
string memory value
) internal pure {
self.buf.encodeString(key);
self.buf.encodeString(value);
}
/**
* @notice Adds a bytes value to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param value The bytes value to add
*/
function addBytes(
Request memory self,
string memory key,
bytes memory value
) internal pure {
self.buf.encodeString(key);
self.buf.encodeBytes(value);
}
/**
* @notice Adds a int256 value to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param value The int256 value to add
*/
function addInt(
Request memory self,
string memory key,
int256 value
) internal pure {
self.buf.encodeString(key);
self.buf.encodeInt(value);
}
/**
* @notice Adds a uint256 value to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param value The uint256 value to add
*/
function addUint(
Request memory self,
string memory key,
uint256 value
) internal pure {
self.buf.encodeString(key);
self.buf.encodeUInt(value);
}
/**
* @notice Adds an array of strings to the request with a given key name
* @param self The initialized request
* @param key The name of the key
* @param values The array of string values to add
*/
function addStringArray(
Request memory self,
string memory key,
string[] memory values
) internal pure {
self.buf.encodeString(key);
self.buf.startArray();
for (uint256 i = 0; i < values.length; i++) {
self.buf.encodeString(values[i]);
}
self.buf.endSequence();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface LinkTokenInterface {
function allowance(address owner, address spender) external view returns (uint256 remaining);
function approve(address spender, uint256 value) external returns (bool success);
function balanceOf(address owner) external view returns (uint256 balance);
function decimals() external view returns (uint8 decimalPlaces);
function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
function increaseApproval(address spender, uint256 subtractedValue) external;
function name() external view returns (string memory tokenName);
function symbol() external view returns (string memory tokenSymbol);
function totalSupply() external view returns (uint256 totalTokensIssued);
function transfer(address to, uint256 value) external returns (bool success);
function transferAndCall(
address to,
uint256 value,
bytes calldata data
) external returns (bool success);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool success);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev A library for working with mutable byte buffers in Solidity.
*
* Byte buffers are mutable and expandable, and provide a variety of primitives
* for writing to them. At any time you can fetch a bytes object containing the
* current contents of the buffer. The bytes object should not be stored between
* operations, as it may change due to resizing of the buffer.
*/
library BufferChainlink {
/**
* @dev Represents a mutable buffer. Buffers have a current value (buf) and
* a capacity. The capacity may be longer than the current value, in
* which case it can be extended without the need to allocate more memory.
*/
struct buffer {
bytes buf;
uint256 capacity;
}
/**
* @dev Initializes a buffer with an initial capacity.
* @param buf The buffer to initialize.
* @param capacity The number of bytes of space to allocate the buffer.
* @return The buffer, for chaining.
*/
function init(buffer memory buf, uint256 capacity) internal pure returns (buffer memory) {
if (capacity % 32 != 0) {
capacity += 32 - (capacity % 32);
}
// Allocate space for the buffer data
buf.capacity = capacity;
assembly {
let ptr := mload(0x40)
mstore(buf, ptr)
mstore(ptr, 0)
mstore(0x40, add(32, add(ptr, capacity)))
}
return buf;
}
/**
* @dev Initializes a new buffer from an existing bytes object.
* Changes to the buffer may mutate the original value.
* @param b The bytes object to initialize the buffer with.
* @return A new buffer.
*/
function fromBytes(bytes memory b) internal pure returns (buffer memory) {
buffer memory buf;
buf.buf = b;
buf.capacity = b.length;
return buf;
}
function resize(buffer memory buf, uint256 capacity) private pure {
bytes memory oldbuf = buf.buf;
init(buf, capacity);
append(buf, oldbuf);
}
function max(uint256 a, uint256 b) private pure returns (uint256) {
if (a > b) {
return a;
}
return b;
}
/**
* @dev Sets buffer length to 0.
* @param buf The buffer to truncate.
* @return The original buffer, for chaining..
*/
function truncate(buffer memory buf) internal pure returns (buffer memory) {
assembly {
let bufptr := mload(buf)
mstore(bufptr, 0)
}
return buf;
}
/**
* @dev Writes a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The start offset to write to.
* @param data The data to append.
* @param len The number of bytes to copy.
* @return The original buffer, for chaining.
*/
function write(
buffer memory buf,
uint256 off,
bytes memory data,
uint256 len
) internal pure returns (buffer memory) {
require(len <= data.length);
if (off + len > buf.capacity) {
resize(buf, max(buf.capacity, len + off) * 2);
}
uint256 dest;
uint256 src;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Start address = buffer address + offset + sizeof(buffer length)
dest := add(add(bufptr, 32), off)
// Update buffer length if we're extending it
if gt(add(len, off), buflen) {
mstore(bufptr, add(len, off))
}
src := add(data, 32)
}
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
unchecked {
uint256 mask = (256**(32 - len)) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
return buf;
}
/**
* @dev Appends a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @param len The number of bytes to copy.
* @return The original buffer, for chaining.
*/
function append(
buffer memory buf,
bytes memory data,
uint256 len
) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, len);
}
/**
* @dev Appends a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, data.length);
}
/**
* @dev Writes a byte to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write the byte at.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function writeUint8(
buffer memory buf,
uint256 off,
uint8 data
) internal pure returns (buffer memory) {
if (off >= buf.capacity) {
resize(buf, buf.capacity * 2);
}
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Address = buffer address + sizeof(buffer length) + off
let dest := add(add(bufptr, off), 32)
mstore8(dest, data)
// Update buffer length if we extended it
if eq(off, buflen) {
mstore(bufptr, add(buflen, 1))
}
}
return buf;
}
/**
* @dev Appends a byte to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function appendUint8(buffer memory buf, uint8 data) internal pure returns (buffer memory) {
return writeUint8(buf, buf.buf.length, data);
}
/**
* @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
* exceed the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @param len The number of bytes to write (left-aligned).
* @return The original buffer, for chaining.
*/
function write(
buffer memory buf,
uint256 off,
bytes32 data,
uint256 len
) private pure returns (buffer memory) {
if (len + off > buf.capacity) {
resize(buf, (len + off) * 2);
}
unchecked {
uint256 mask = (256**len) - 1;
// Right-align data
data = data >> (8 * (32 - len));
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Address = buffer address + sizeof(buffer length) + off + len
let dest := add(add(bufptr, off), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length if we extended it
if gt(add(off, len), mload(bufptr)) {
mstore(bufptr, add(off, len))
}
}
}
return buf;
}
/**
* @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function writeBytes20(
buffer memory buf,
uint256 off,
bytes20 data
) internal pure returns (buffer memory) {
return write(buf, off, bytes32(data), 20);
}
/**
* @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chhaining.
*/
function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, bytes32(data), 20);
}
/**
* @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, 32);
}
/**
* @dev Writes an integer to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @param len The number of bytes to write (right-aligned).
* @return The original buffer, for chaining.
*/
function writeInt(
buffer memory buf,
uint256 off,
uint256 data,
uint256 len
) private pure returns (buffer memory) {
if (len + off > buf.capacity) {
resize(buf, (len + off) * 2);
}
uint256 mask = (256**len) - 1;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Address = buffer address + off + sizeof(buffer length) + len
let dest := add(add(bufptr, off), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length if we extended it
if gt(add(off, len), mload(bufptr)) {
mstore(bufptr, add(off, len))
}
}
return buf;
}
/**
* @dev Appends a byte to the end of the buffer. Resizes if doing so would
* exceed the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer.
*/
function appendInt(
buffer memory buf,
uint256 data,
uint256 len
) internal pure returns (buffer memory) {
return writeInt(buf, buf.buf.length, data, len);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.4.19;
import {BufferChainlink} from "./BufferChainlink.sol";
library CBORChainlink {
using BufferChainlink for BufferChainlink.buffer;
uint8 private constant MAJOR_TYPE_INT = 0;
uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1;
uint8 private constant MAJOR_TYPE_BYTES = 2;
uint8 private constant MAJOR_TYPE_STRING = 3;
uint8 private constant MAJOR_TYPE_ARRAY = 4;
uint8 private constant MAJOR_TYPE_MAP = 5;
uint8 private constant MAJOR_TYPE_TAG = 6;
uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7;
uint8 private constant TAG_TYPE_BIGNUM = 2;
uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3;
function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure {
if(value <= 23) {
buf.appendUint8(uint8((major << 5) | value));
} else if (value <= 0xFF) {
buf.appendUint8(uint8((major << 5) | 24));
buf.appendInt(value, 1);
} else if (value <= 0xFFFF) {
buf.appendUint8(uint8((major << 5) | 25));
buf.appendInt(value, 2);
} else if (value <= 0xFFFFFFFF) {
buf.appendUint8(uint8((major << 5) | 26));
buf.appendInt(value, 4);
} else {
buf.appendUint8(uint8((major << 5) | 27));
buf.appendInt(value, 8);
}
}
function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure {
buf.appendUint8(uint8((major << 5) | 31));
}
function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure {
if(value > 0xFFFFFFFFFFFFFFFF) {
encodeBigNum(buf, value);
} else {
encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value));
}
}
function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure {
if(value < -0x10000000000000000) {
encodeSignedBigNum(buf, value);
} else if(value > 0xFFFFFFFFFFFFFFFF) {
encodeBigNum(buf, uint(value));
} else if(value >= 0) {
encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(uint256(value)));
} else {
encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(uint256(-1 - value)));
}
}
function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure {
encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length));
buf.append(value);
}
function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure {
buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM));
encodeBytes(buf, abi.encode(value));
}
function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure {
buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM));
encodeBytes(buf, abi.encode(uint256(-1 - input)));
}
function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure {
encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length));
buf.append(bytes(value));
}
function startArray(BufferChainlink.buffer memory buf) internal pure {
encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY);
}
function startMap(BufferChainlink.buffer memory buf) internal pure {
encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP);
}
function endSequence(BufferChainlink.buffer memory buf) internal pure {
encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../../interfaces/IGNSAddressStore.sol";
/**
* @dev Proxy base for the diamond and its facet contracts to store addresses and manage access control
*/
abstract contract GNSAddressStore is Initializable, IGNSAddressStore {
AddressStore private addressStore;
/// @inheritdoc IGNSAddressStore
function initialize(address _govTimelock) external initializer {
if (_govTimelock == address(0)) {
revert IGeneralErrors.InitError();
}
_setRole(_govTimelock, Role.GOV_TIMELOCK, true);
}
/// @inheritdoc IGNSAddressStore
function initializeGovEmergencyTimelock(address _govEmergencyTimelock) external reinitializer(23) {
if (_govEmergencyTimelock == address(0)) {
revert IGeneralErrors.InitError();
}
_setRole(_govEmergencyTimelock, Role.GOV_EMERGENCY_TIMELOCK, true);
}
// Addresses
/// @inheritdoc IGNSAddressStore
function getAddresses() external view returns (Addresses memory) {
return addressStore.globalAddresses;
}
// Roles
/// @inheritdoc IGNSAddressStore
function hasRole(address _account, Role _role) public view returns (bool) {
return addressStore.accessControl[_account][_role];
}
/// @inheritdoc IGNSAddressStore
function hasRoles(address _account, Role _roleA, Role _roleB) public view returns (bool) {
return addressStore.accessControl[_account][_roleA] || addressStore.accessControl[_account][_roleB];
}
/**
* @dev Update role for account
* @param _account account to update
* @param _role role to set
* @param _value true if allowed, false if not
*/
function _setRole(address _account, Role _role, bool _value) internal {
addressStore.accessControl[_account][_role] = _value;
emit AccessControlUpdated(_account, _role, _value);
}
/// @inheritdoc IGNSAddressStore
function setRoles(
address[] calldata _accounts,
Role[] calldata _roles,
bool[] calldata _values
) external onlyRoles(Role.GOV_TIMELOCK, Role.GOV_EMERGENCY_TIMELOCK) {
if (_accounts.length != _roles.length || _accounts.length != _values.length) {
revert IGeneralErrors.InvalidInputLength();
}
for (uint256 i = 0; i < _accounts.length; ++i) {
if (
(_roles[i] == Role.GOV_TIMELOCK && _accounts[i] == msg.sender) ||
(_roles[i] == Role.GOV_EMERGENCY_TIMELOCK && _accounts[i] == msg.sender)
) {
revert NotAllowed();
}
_setRole(_accounts[i], _roles[i], _values[i]);
}
}
/**
* @dev Reverts if caller does not have role
* @param _role role to enforce
*/
function _enforceRole(Role _role) internal view {
if (!hasRole(msg.sender, _role)) {
revert WrongAccess();
}
}
/**
* @dev Reverts if caller does not have at least one of the two roles
* @param _roleA role to enforce
* @param _roleB role to enforce
*/
function _enforceRoles(Role _roleA, Role _roleB) internal view {
if (!hasRoles(msg.sender, _roleA, _roleB)) {
revert WrongAccess();
}
}
/**
* @dev Reverts if caller does not have role
*/
modifier onlyRole(Role _role) {
_enforceRole(_role);
_;
}
/**
* @dev Reverts if caller does not have either of the roles
*/
modifier onlyRoles(Role _roleA, Role _roleB) {
_enforceRoles(_roleA, _roleB);
_;
}
/**
* @dev Reverts if caller isn't this same contract (facets calling other facets)
*/
modifier onlySelf() {
if (msg.sender != address(this)) {
revert WrongAccess();
}
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../../libraries/ChainConfigUtils.sol";
/**
* @dev Reentrancy guard contract for the diamond. Uses `ChainConfigStorage.reentrancyLock` state var as the locking slot.
*
* Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.5/contracts/security/ReentrancyGuard.sol
*/
abstract contract GNSReentrancyGuard {
error ReentrancyGuardReentrantCall();
uint256 private constant UNLOCKED = 0;
uint256 private constant LOCKED = 1;
/**
* @dev Prevents the diamond from calling other `nonReentrant` functions, directly or indirectly.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
/**
* @dev `nonReentrant` modifier helper. Ensures reentrancy guard is UNLOCKED and then updates it to LOCKED.
*
* Reverts with {ReentrancyGuardReentrantCall()} if guard is LOCKED.
*/
function _nonReentrantBefore() internal {
// If state is currently LOCKED, revert with `ReentrancyGuardReentrantCall()`
if (_reentrancyGuardLocked()) {
revert ReentrancyGuardReentrantCall();
}
// Set state to LOCKED
_setReentrancyGuard(LOCKED);
}
/**
* @dev `nonReentrant` modifier helper. Sets reentrancy guard to UNLOCKED.
*/
function _nonReentrantAfter() internal {
_setReentrancyGuard(UNLOCKED);
}
/**
* @dev Updated the value of the reentrancy guard slot
* @param _value the new reentrancy guard value
*/
function _setReentrancyGuard(uint256 _value) private {
uint256 storageSlot = ChainConfigUtils._getSlot();
assembly {
sstore(storageSlot, _value)
}
}
/**
* @dev Returns whether reentrancy guard is set to LOCKED
*/
function _reentrancyGuardLocked() internal view returns (bool isLocked) {
uint256 storageSlot = ChainConfigUtils._getSlot();
assembly {
isLocked := eq(sload(storageSlot), LOCKED)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Interface for Arbitrum special l2 functions
*/
interface IArbSys {
function arbBlockNumber() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Interface for Chainlink feeds
*/
interface IChainlinkFeed {
function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80);
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface for ERC20 tokens
*/
interface IERC20 is IERC20Metadata {
function burn(address, uint256) external;
function mint(address, uint256) external;
function hasRole(bytes32, address) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Interface for errors potentially used in all libraries (general names)
*/
interface IGeneralErrors {
error InitError();
error InvalidAddresses();
error InvalidAddress();
error InvalidInputLength();
error InvalidCollateralIndex();
error InvalidAmount();
error WrongParams();
error WrongLength();
error WrongOrder();
error WrongIndex();
error BlockOrder();
error Overflow();
error ZeroAddress();
error ZeroValue();
error AlreadyExists();
error DoesntExist();
error Paused();
error BelowMin();
error AboveMax();
error NotAuthorized();
error WrongTradeType();
error WrongOrderType();
error InsufficientBalance();
error UnsupportedChain();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./types/IAddressStore.sol";
import "./IGeneralErrors.sol";
/**
* @dev Interface for AddressStoreUtils library
*/
interface IGNSAddressStore is IAddressStore, IGeneralErrors {
/**
* @dev Initializes address store facet
* @param _rolesManager roles manager address
*/
function initialize(address _rolesManager) external;
/**
* @dev Initializes gov emergency timelock role
* @param _govEmergencyTimelock address to be granted emergency timelock role
*/
function initializeGovEmergencyTimelock(address _govEmergencyTimelock) external;
/**
* @dev Returns addresses current values
*/
function getAddresses() external view returns (Addresses memory);
/**
* @dev Returns whether an account has been granted a particular role
* @param _account account address to check
* @param _role role to check
*/
function hasRole(address _account, Role _role) external view returns (bool);
/**
* @dev Returns whether an account has been granted at least one of two roles
* @param _account address to check
* @param _roleA first role to check
* @param _roleB second role to check
*/
function hasRoles(address _account, Role _roleA, Role _roleB) external view returns (bool);
/**
* @dev Updates access control for a list of accounts
* @param _accounts accounts addresses to update
* @param _roles corresponding roles to update
* @param _values corresponding new values to set
*/
function setRoles(address[] calldata _accounts, Role[] calldata _roles, bool[] calldata _values) external;
/**
* @dev Emitted when addresses are updated
* @param addresses new addresses values
*/
event AddressesUpdated(Addresses addresses);
/**
* @dev Emitted when access control is updated for an account
* @param target account address to update
* @param role role to update
* @param access whether role is granted or revoked
*/
event AccessControlUpdated(address target, Role role, bool access);
error NotAllowed();
error WrongAccess();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./IGNSAddressStore.sol";
import "./IGNSDiamondCut.sol";
import "./IGNSDiamondLoupe.sol";
import "./types/ITypes.sol";
/**
* @dev the non-expanded interface for multi-collat diamond, only contains types/structs/enums
*/
interface IGNSDiamond is IGNSAddressStore, IGNSDiamondCut, IGNSDiamondLoupe, ITypes {
}// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import "./types/IDiamondStorage.sol"; /** * @author Nick Mudge <[email protected]> (https://twitter.com/mudgen) * @author Gains Network * @dev Based on EIP-2535: Diamonds (https://eips.ethereum.org/EIPS/eip-2535) * @dev Follows diamond-3 implementation (https://github.com/mudgen/diamond-3-hardhat/) * @dev One of the diamond standard interfaces, used for diamond management. */ interface IGNSDiamondCut is IDiamondStorage { /** * @notice Add/replace/remove any number of functions and optionally execute a function with delegatecall * @param _diamondCut Contains the facet addresses and function selectors * @param _init The address of the contract or facet to execute _calldata * @param _calldata A function call, including function selector and arguments _calldata is executed with delegatecall on _init */ function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external; /** * @dev Emitted when function selectors of a facet of the diamond is added, replaced, or removed * @param _diamondCut Contains the update data (facet addresses, action, function selectors) * @param _init The address of the contract or facet to execute _calldata * @param _calldata Function call to execute after the diamond cut */ event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); error InvalidFacetCutAction(); error NotContract(); error NotFound(); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; /** * @author Nick Mudge <[email protected]> (https://twitter.com/mudgen) * @author Gains Network * @dev Based on EIP-2535: Diamonds (https://eips.ethereum.org/EIPS/eip-2535) * @dev Follows diamond-3 implementation (https://github.com/mudgen/diamond-3-hardhat/) * @dev One of the diamond standard interfaces, used to inspect the diamond like a magnifying glass. */ interface IGNSDiamondLoupe { /// These functions are expected to be called frequently /// by tools. struct Facet { address facetAddress; bytes4[] functionSelectors; } /// @notice Gets all facet addresses and their four byte function selectors. /// @return facets_ Facet function facets() external view returns (Facet[] memory facets_); /// @notice Gets all the function selectors supported by a specific facet. /// @param _facet The facet address. /// @return facetFunctionSelectors_ function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_); /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view returns (address[] memory facetAddresses_); /// @notice Gets the facet that supports the given selector. /// @dev If facet is not found return address(0). /// @param _functionSelector The function selector. /// @return facetAddress_ The facet address. function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_); }
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./IGNSDiamond.sol";
import "./libraries/IPairsStorageUtils.sol";
import "./libraries/IReferralsUtils.sol";
import "./libraries/IFeeTiersUtils.sol";
import "./libraries/IPriceImpactUtils.sol";
import "./libraries/ITradingStorageUtils.sol";
import "./libraries/ITriggerRewardsUtils.sol";
import "./libraries/ITradingInteractionsUtils.sol";
import "./libraries/ITradingCallbacksUtils.sol";
import "./libraries/IBorrowingFeesUtils.sol";
import "./libraries/IPriceAggregatorUtils.sol";
import "./libraries/IOtcUtils.sol";
import "./IMulticall.sol";
import "./libraries/IChainConfigUtils.sol";
import "./libraries/IFundingFeesUtils.sol";
/**
* @dev Expanded version of multi-collat diamond that includes events and function signatures
* Technically this interface is virtual since the diamond doesn't directly implement these functions.
* It only forwards the calls to the facet contracts using delegatecall.
*/
interface IGNSMultiCollatDiamond is
IGNSDiamond,
IPairsStorageUtils,
IReferralsUtils,
IFeeTiersUtils,
IPriceImpactUtils,
ITradingStorageUtils,
ITriggerRewardsUtils,
ITradingInteractionsUtils,
ITradingCallbacksUtils,
IBorrowingFeesUtils,
IPriceAggregatorUtils,
IOtcUtils,
IMulticall,
IChainConfigUtils,
IFundingFeesUtils
{}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Interface for GNSStaking contract
*/
interface IGNSStaking {
struct Staker {
uint128 stakedGns; // 1e18
uint128 debtDai; // 1e18
}
struct StakerInfo {
uint48 lastDepositTs;
uint208 __placeholder;
}
struct RewardState {
uint128 accRewardPerGns; // 1e18
uint128 precisionDelta;
}
struct RewardInfo {
uint128 debtToken; // 1e18
uint128 __placeholder;
}
struct UnlockSchedule {
uint128 totalGns; // 1e18
uint128 claimedGns; // 1e18
uint128 debtDai; // 1e18
uint48 start; // block.timestamp (seconds)
uint48 duration; // in seconds
bool revocable;
UnlockType unlockType;
uint16 __placeholder;
}
struct UnlockScheduleInput {
uint128 totalGns; // 1e18
uint48 start; // block.timestamp (seconds)
uint48 duration; // in seconds
bool revocable;
UnlockType unlockType;
}
enum UnlockType {
LINEAR,
CLIFF
}
function owner() external view returns (address);
function distributeReward(address _rewardToken, uint256 _amountToken) external;
function createUnlockSchedule(UnlockScheduleInput calldata _schedule, address _staker) external;
event UnlockManagerUpdated(address indexed manager, bool authorized);
event DaiHarvested(address indexed staker, uint128 amountDai);
event RewardHarvested(address indexed staker, address indexed token, uint128 amountToken);
event RewardHarvestedFromUnlock(
address indexed staker,
address indexed token,
bool isOldDai,
uint256[] ids,
uint128 amountToken
);
event RewardDistributed(address indexed token, uint256 amount);
event GnsStaked(address indexed staker, uint128 amountGns);
event GnsUnstaked(address indexed staker, uint128 amountGns);
event GnsClaimed(address indexed staker, uint256[] ids, uint128 amountGns);
event UnlockScheduled(address indexed staker, uint256 indexed index, UnlockSchedule schedule);
event UnlockScheduleRevoked(address indexed staker, uint256 indexed index);
event RewardTokenAdded(address token, uint256 index, uint128 precisionDelta);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Interface for GToken contract
*/
interface IGToken {
struct GnsPriceProvider {
address addr;
bytes signature;
}
struct LockedDeposit {
address owner;
uint256 shares; // collateralConfig.precision
uint256 assetsDeposited; // collateralConfig.precision
uint256 assetsDiscount; // collateralConfig.precision
uint256 atTimestamp; // timestamp
uint256 lockDuration; // timestamp
}
struct ContractAddresses {
address asset;
address owner; // 2-week timelock contract
address manager; // 3-day timelock contract
address admin; // bypasses timelock, access to emergency functions
address gnsToken;
address lockedDepositNft;
address pnlHandler;
address openTradesPnlFeed;
GnsPriceProvider gnsPriceProvider;
}
struct Meta {
string name;
string symbol;
}
function manager() external view returns (address);
function admin() external view returns (address);
function currentEpoch() external view returns (uint256);
function currentEpochStart() external view returns (uint256);
function currentEpochPositiveOpenPnl() external view returns (uint256);
function updateAccPnlPerTokenUsed(
uint256 prevPositiveOpenPnl,
uint256 newPositiveOpenPnl
) external returns (uint256);
function getLockedDeposit(uint256 depositId) external view returns (LockedDeposit memory);
function sendAssets(uint256 assets, address receiver) external;
function receiveAssets(uint256 assets, address user, bool _burn) external;
function distributeReward(uint256 assets) external;
function tvl() external view returns (uint256);
function marketCap() external view returns (uint256);
function shareToAssetsPrice() external view returns (uint256);
function collateralConfig() external view returns (uint128, uint128);
event ManagerUpdated(address newValue);
event AdminUpdated(address newValue);
event PnlHandlerUpdated(address newValue);
event OpenTradesPnlFeedUpdated(address newValue);
event GnsPriceProviderUpdated(GnsPriceProvider newValue);
event WithdrawLockThresholdsPUpdated(uint256[2] newValue);
event MaxAccOpenPnlDeltaUpdated(uint256 newValue);
event MaxDailyAccPnlDeltaUpdated(uint256 newValue);
event MaxSupplyIncreaseDailyPUpdated(uint256 newValue);
event LossesBurnPUpdated(uint256 newValue);
event MaxGnsSupplyMintDailyPUpdated(uint256 newValue);
event MaxDiscountPUpdated(uint256 newValue);
event MaxDiscountThresholdPUpdated(uint256 newValue);
event CurrentMaxSupplyUpdated(uint256 newValue);
event DailyAccPnlDeltaReset();
event ShareToAssetsPriceUpdated(uint256 newValue);
event OpenTradesPnlFeedCallFailed();
event WithdrawRequested(
address indexed sender,
address indexed owner,
uint256 shares,
uint256 currEpoch,
uint256 indexed unlockEpoch
);
event WithdrawCanceled(
address indexed sender,
address indexed owner,
uint256 shares,
uint256 currEpoch,
uint256 indexed unlockEpoch
);
event DepositLocked(address indexed sender, address indexed owner, uint256 depositId, LockedDeposit d);
event DepositUnlocked(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 depositId,
LockedDeposit d
);
event RewardDistributed(address indexed sender, uint256 assets);
event AssetsSent(address indexed sender, address indexed receiver, uint256 assets);
event AssetsReceived(address indexed sender, address indexed user, uint256 assets, uint256 assetsLessDeplete, bool burn);
event Depleted(address indexed sender, uint256 assets, uint256 amountGns);
event Refilled(address indexed sender, uint256 assets, uint256 amountGns);
event AccPnlPerTokenUsedUpdated(
address indexed sender,
uint256 indexed newEpoch,
uint256 prevPositiveOpenPnl,
uint256 newPositiveOpenPnl,
uint256 newEpochPositiveOpenPnl,
int256 newAccPnlPerTokenUsed
);
error OnlyManager();
error OnlyTradingPnlHandler();
error OnlyPnlFeed();
error AddressZero();
error PriceZero();
error ValueZero();
error BytesZero();
error NoActiveDiscount();
error BelowMin();
error AboveMax();
error WrongValue();
error WrongValues();
error GnsPriceCallFailed();
error GnsTokenPriceZero();
error PendingWithdrawal();
error EndOfEpoch();
error NotAllowed();
error NoDiscount();
error NotUnlocked();
error NotEnoughAssets();
error MaxDailyPnl();
error NotUnderCollateralized();
error AboveInflationLimit();
// Ownable
error OwnableInvalidOwner(address owner);
// ERC4626
error ERC4626ExceededMaxDeposit();
error ERC4626ExceededMaxMint();
error ERC4626ExceededMaxWithdraw();
error ERC4626ExceededMaxRedeem();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @custom:version 8
* @dev Generic interface for liquidity pool methods for fetching observations (to calculate TWAP) and other basic information
*/
interface ILiquidityPool {
/**
* @dev AlgebraPool V1.9 equivalent of Uniswap V3 `observe` function
* See https://github.com/cryptoalgebra/AlgebraV1.9/blob/main/src/core/contracts/interfaces/pool/IAlgebraPoolDerivedState.sol for more information
*/
function getTimepoints(
uint32[] calldata secondsAgos
)
external
view
returns (
int56[] memory tickCumulatives,
uint160[] memory secondsPerLiquidityCumulatives,
uint112[] memory volatilityCumulatives,
uint256[] memory volumePerAvgLiquiditys
);
/**
* @dev Uniswap V3 `observe` function
* See `https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol` for more information
*/
function observe(
uint32[] calldata secondsAgos
) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
/**
* @notice The first of the two tokens of the pool, sorted by address
* @return The token contract address
*/
function token0() external view returns (address);
/**
* @notice The second of the two tokens of the pool, sorted by address
* @return The token contract address
*/
function token1() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
interface IMulticall {
/**
* @dev Call multiple functions in a single call.
* @param data The data for the calls.
* @return results The results of the calls.
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Interface for WETH9 token
*/
interface IWETH9 {
function approve(address spender, uint256 amount) external returns (bool);
function transfer(address to, uint256 amount) external returns (bool);
function deposit() external payable;
function withdraw(uint256) external;
function balanceOf(address account) external view returns (uint256);
event Approval(address indexed src, address indexed guy, uint256 wad);
event Transfer(address indexed src, address indexed dst, uint256 wad);
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IFundingFees.sol";
import "../types/IPriceAggregator.sol";
import "../types/IBorrowingFees.sol";
/**
* @dev Interface for GNSBorrowingFees facet (inherits types and also contains functions, events, and custom errors)
*/
interface IBorrowingFeesUtils is IBorrowingFees {
/**
* @dev Initializes default fee per block caps
* @param _feePerBlockCap the new cap values
*/
function initializeBorrowingFeePerBlockCap(BorrowingFeePerBlockCap memory _feePerBlockCap) external;
/**
* @dev Updates borrowing pair params of a pair
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _value new value
* @param _signedPairPrices signed pair market prices
*/
function setBorrowingPairParams(
uint8 _collateralIndex,
uint16 _pairIndex,
BorrowingPairParams calldata _value,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Updates borrowing pair params of multiple pairs
* @param _collateralIndex index of the collateral
* @param _indices indices of the pairs
* @param _values new values
* @param _signedPairPrices signed pair market prices
*/
function setBorrowingPairParamsArray(
uint8 _collateralIndex,
uint16[] calldata _indices,
BorrowingPairParams[] calldata _values,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Updates borrowing group params of a group
* @param _collateralIndex index of the collateral
* @param _groupIndex index of the borrowing group
* @param _value new value
*/
function setBorrowingGroupParams(
uint8 _collateralIndex,
uint16 _groupIndex,
BorrowingGroupParams calldata _value
) external;
/**
* @dev Updates borrowing group params of multiple groups
* @param _collateralIndex index of the collateral
* @param _indices indices of the groups
* @param _values new values
*/
function setBorrowingGroupParamsArray(
uint8 _collateralIndex,
uint16[] calldata _indices,
BorrowingGroupParams[] calldata _values
) external;
/**
* @dev Updates default borrowing fee per block caps
* @param _feePerBlockCap new value
*/
function setBorrowingFeePerBlockCap(IBorrowingFees.BorrowingFeePerBlockCap memory _feePerBlockCap) external;
/**
* @dev Updates borrowing fee per block caps for multiple pairs
* @param _collateralIndex index of the collateral
* @param _indices indices of the pairs
* @param _values new values
*/
function setBorrowingPairFeePerBlockCapArray(
uint8 _collateralIndex,
uint16[] calldata _indices,
IBorrowingFees.BorrowingFeePerBlockCap[] calldata _values
) external;
/**
* @dev Callback for borrowing param update
* @param _paramUpdate param update details
* @param _currentPairPrice current pair price (1e10)
*/
function borrowingParamUpdateCallback(
IFundingFees.PendingParamUpdate memory _paramUpdate,
uint256 _currentPairPrice
) external;
/**
* @dev Callback after a trade is opened/closed to store pending borrowing fees and update group OI
* @param _collateralIndex index of the collateral
* @param _trader address of the trader
* @param _pairIndex index of the pair
* @param _index index of the trade
* @param _positionSizeCollateral position size of the trade in collateral tokens
* @param _open true if trade has been opened, false if trade has been closed
* @param _long true if trade is long, false if trade is short
* @param _currentPairPrice current pair price (1e10)
*/
function handleTradeBorrowingCallback(
uint8 _collateralIndex,
address _trader,
uint16 _pairIndex,
uint32 _index,
uint256 _positionSizeCollateral,
bool _open,
bool _long,
uint256 _currentPairPrice
) external;
/**
* @dev Updates a pair's open interest (before v10)
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _long true if long side
* @param _increase true if open interest is increased, false if decreased
* @param _amountCollateral change in open interest in collateral tokens (collateral precision)
*/
function updatePairOiBeforeV10(
uint8 _collateralIndex,
uint16 _pairIndex,
bool _long,
bool _increase,
uint256 _amountCollateral
) external;
/**
* @dev Returns the pending acc borrowing fees for a pair on both sides
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _currentBlock current block number
* @param _currentPairPrice current pair price (1e10)
* @return accFeeLong new pair acc borrowing fee on long side
* @return accFeeShort new pair acc borrowing fee on short side
* @return pairAccFeeLongDelta pair acc borrowing fee delta on long side
* @return pairAccFeeShortDelta pair acc borrowing fee delta on short side
*/
function getBorrowingPairPendingAccFees(
uint8 _collateralIndex,
uint16 _pairIndex,
uint256 _currentBlock,
uint256 _currentPairPrice
)
external
view
returns (uint64 accFeeLong, uint64 accFeeShort, uint64 pairAccFeeLongDelta, uint64 pairAccFeeShortDelta);
/**
* @dev Returns the pending acc borrowing fees for a borrowing group on both sides
* @param _collateralIndex index of the collateral
* @param _groupIndex index of the borrowing group
* @param _currentBlock current block number
* @return accFeeLong new group acc borrowing fee on long side
* @return accFeeShort new group acc borrowing fee on short side
* @return groupAccFeeLongDelta group acc borrowing fee delta on long side
* @return groupAccFeeShortDelta group acc borrowing fee delta on short side
*/
function getBorrowingGroupPendingAccFees(
uint8 _collateralIndex,
uint16 _groupIndex,
uint256 _currentBlock
)
external
view
returns (uint64 accFeeLong, uint64 accFeeShort, uint64 groupAccFeeLongDelta, uint64 groupAccFeeShortDelta);
/**
* @dev Returns the borrowing fee for a trade
* @param _input input data (collateralIndex, trader, pairIndex, index, long, collateral, leverage)
* @return feeAmountCollateral borrowing fee (collateral precision)
*/
function getTradeBorrowingFee(BorrowingFeeInput memory _input) external view returns (uint256 feeAmountCollateral);
/**
* @dev Returns the liquidation price for a trade
* @param _input input data (collateralIndex, trader, pairIndex, index, openPrice, long, collateral, leverage)
*/
function getTradeLiquidationPrice(LiqPriceInput calldata _input) external view returns (uint256);
/**
* @dev Returns the open interests for a pair (before v10)
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @return longOi open interest on long side
* @return shortOi open interest on short side
*/
function getPairOisBeforeV10Collateral(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (uint256 longOi, uint256 shortOi);
/**
* @dev Returns the borrowing group index for a pair
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @return groupIndex borrowing group index
*/
function getBorrowingPairGroupIndex(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (uint16 groupIndex);
/**
* @dev Returns the open interest in collateral tokens for a pair on one side (before v10)
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _long true if long side
*/
function getPairOiBeforeV10Collateral(
uint8 _collateralIndex,
uint16 _pairIndex,
bool _long
) external view returns (uint256);
/**
* @dev Returns whether a trade is within the max group borrowing open interest
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _long true if long side
* @param _positionSizeCollateral position size of the trade in collateral tokens
*/
function withinMaxBorrowingGroupOi(
uint8 _collateralIndex,
uint16 _pairIndex,
bool _long,
uint256 _positionSizeCollateral
) external view returns (bool);
/**
* @dev Returns a borrowing group's data
* @param _collateralIndex index of the collateral
* @param _groupIndex index of the borrowing group
*/
function getBorrowingGroup(
uint8 _collateralIndex,
uint16 _groupIndex
) external view returns (BorrowingData memory group);
/**
* @dev Returns a borrowing group's oi data
* @param _collateralIndex index of the collateral
* @param _groupIndex index of the borrowing group
*/
function getBorrowingGroupOi(
uint8 _collateralIndex,
uint16 _groupIndex
) external view returns (OpenInterest memory group);
/**
* @dev Returns a borrowing pair's data
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getBorrowingPair(uint8 _collateralIndex, uint16 _pairIndex) external view returns (BorrowingData memory);
/**
* @dev Returns a borrowing pair's oi data (before v10)
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getBorrowingPairOiBeforeV10(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (OpenInterest memory);
/**
* @dev Returns a borrowing pair's oi data
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getBorrowingPairGroups(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (BorrowingPairGroup[] memory);
/**
* @dev Returns all borrowing pairs' borrowing data, oi data, and pair groups data
* @param _collateralIndex index of the collateral
*/
function getAllBorrowingPairs(
uint8 _collateralIndex
) external view returns (BorrowingData[] memory, OpenInterest[] memory, BorrowingPairGroup[][] memory);
/**
* @dev Returns borrowing groups' data and oi data
* @param _collateralIndex index of the collateral
* @param _indices indices of the groups
*/
function getBorrowingGroups(
uint8 _collateralIndex,
uint16[] calldata _indices
) external view returns (BorrowingData[] memory, OpenInterest[] memory);
/**
* @dev Returns borrowing groups' data
* @param _collateralIndex index of the collateral
* @param _trader address of trader
* @param _index index of trade
*/
function getBorrowingInitialAccFees(
uint8 _collateralIndex,
address _trader,
uint32 _index
) external view returns (BorrowingInitialAccFees memory);
/**
* @dev Returns the max open interest for a pair
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getPairMaxOi(uint8 _collateralIndex, uint16 _pairIndex) external view returns (uint256);
/**
* @dev Returns the max open interest in collateral tokens for a pair
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getPairMaxOiCollateral(uint8 _collateralIndex, uint16 _pairIndex) external view returns (uint256);
/**
* @dev Returns the default minimum and maximum fee per block percentage (1e3 precision)
*/
function getBorrowingFeePerBlockCap() external view returns (BorrowingFeePerBlockCap memory);
/**
* @dev Returns the minimum and maximum fee per block percentage for a pair (1e3 precision)
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getBorrowingPairFeePerBlockCap(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (BorrowingFeePerBlockCap memory);
/**
* @dev Returns the minimum and maximum fee per block percentage for multiple pairs
* @param _collateralIndex index of the collateral
* @param _indices indices of the pairs
*/
function getBorrowingPairFeePerBlockCaps(
uint8 _collateralIndex,
uint16[] calldata _indices
) external view returns (BorrowingFeePerBlockCap[] memory);
/**
* @dev Emitted when a pair's borrowing params is updated
* @param pairIndex index of the pair
* @param groupIndex index of its new group
* @param feePerBlock new fee per block
* @param feeExponent new fee exponent
* @param maxOi new max open interest
*/
event BorrowingPairParamsUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint16 indexed groupIndex,
uint32 feePerBlock,
uint48 feeExponent,
uint72 maxOi
);
/**
* @dev Emitted when a pair's borrowing group has been updated
* @param pairIndex index of the pair
* @param prevGroupIndex previous borrowing group index
* @param newGroupIndex new borrowing group index
*/
event BorrowingPairGroupUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint16 prevGroupIndex,
uint16 newGroupIndex
);
/**
* @dev Emitted when a group's borrowing params is updated
* @param groupIndex index of the group
* @param feePerBlock new fee per block
* @param maxOi new max open interest
* @param feeExponent new fee exponent
*/
event BorrowingGroupUpdated(
uint8 indexed collateralIndex,
uint16 indexed groupIndex,
uint32 feePerBlock,
uint72 maxOi,
uint48 feeExponent
);
/**
* @dev Emitted when a trade's initial acc borrowing fees are stored
* @param collateralIndex index of collateral
* @param trader address of the trader
* @param pairIndex index of the pair
* @param index index of the trade
* @param long true if trade is long, false if trade is short
* @param currentPairPrice current pair price (1e10)
* @param initialPairAccFee initial pair acc fee (for the side of the trade)
* @param initialGroupAccFee initial group acc fee (for the side of the trade)
*/
event BorrowingInitialAccFeesStored(
uint8 indexed collateralIndex,
address indexed trader,
uint16 indexed pairIndex,
uint32 index,
bool long,
uint256 currentPairPrice,
uint64 initialPairAccFee,
uint64 initialGroupAccFee
);
/**
* @dev Emitted when a trade is executed and borrowing callback is handled
* @param trader address of the trader
* @param pairIndex index of the pair
* @param index index of the trade
* @param open true if trade has been opened, false if trade has been closed
* @param long true if trade is long, false if trade is short
* @param currentPairPrice current pair price (1e10)
* @param positionSizeCollateral position size of the trade in collateral tokens
*/
event TradeBorrowingCallbackHandled(
uint8 indexed collateralIndex,
address indexed trader,
uint16 indexed pairIndex,
uint32 index,
bool open,
bool long,
uint256 currentPairPrice,
uint256 positionSizeCollateral
);
/**
* @dev Emitted when a pair's borrowing acc fees are updated
* @param pairIndex index of the pair
* @param currentBlock current block number
* @param currentPairPrice current pair price (1e10)
* @param accFeeLong new pair acc borrowing fee on long side
* @param accFeeShort new pair acc borrowing fee on short side
*/
event BorrowingPairAccFeesUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint256 currentBlock,
uint256 currentPairPrice,
uint64 accFeeLong,
uint64 accFeeShort
);
/**
* @dev Emitted when a group's borrowing acc fees are updated
* @param groupIndex index of the borrowing group
* @param currentBlock current block number
* @param accFeeLong new group acc borrowing fee on long side
* @param accFeeShort new group acc borrowing fee on short side
*/
event BorrowingGroupAccFeesUpdated(
uint8 indexed collateralIndex,
uint16 indexed groupIndex,
uint256 currentBlock,
uint64 accFeeLong,
uint64 accFeeShort
);
/**
* @dev Emitted when a borrowing pair's open interests are updated
* @param pairIndex index of the pair
* @param long true if long side
* @param increase true if open interest is increased, false if decreased
* @param delta change in open interest in collateral tokens (1e10 precision)
* @param newOiLong new open interest on long side
* @param newOiShort new open interest on short side
*/
event BorrowingPairOiBeforeV10Updated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
bool long,
bool increase,
uint72 delta,
uint72 newOiLong,
uint72 newOiShort
);
/**
* @dev Emitted when a borrowing group's open interests are updated
* @param groupIndex index of the borrowing group
* @param long true if long side
* @param increase true if open interest is increased, false if decreased
* @param delta change in open interest in collateral tokens (1e10 precision)
* @param newOiLong new open interest on long side
* @param newOiShort new open interest on short side
*/
event BorrowingGroupOiUpdated(
uint8 indexed collateralIndex,
uint16 indexed groupIndex,
bool long,
bool increase,
uint72 delta,
uint72 newOiLong,
uint72 newOiShort
);
/**
* @dev Emitted when the default fee per block cap is updated
* @param minP new min value (1e3% precision)
* @param maxP new max value (1e3% precision)
*/
event BorrowingFeePerBlockCapUpdated(uint32 minP, uint32 maxP);
/**
* @dev Emitted when the a pair's fee per block cap is updated
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param minP new min value (1e3% precision)
* @param maxP new max value (1e3% precision)
*/
event BorrowingPairFeePerBlockCapUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint32 minP,
uint32 maxP
);
error BorrowingZeroGroup();
error BorrowingWrongExponent();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IChainConfig.sol";
/**
* @dev Interface for GNSChainConfig facet (inherits types and also contains functions, events, and custom errors)
*/
interface IChainConfigUtils is IChainConfig {
/**
* @dev Initializer for ChainConfig facet
* @param _nativeTransferGasLimit new native transfer gas limit
* @param _nativeTransferEnabled whether native transfers should be enabled
*/
function initializeChainConfig(uint16 _nativeTransferGasLimit, bool _nativeTransferEnabled) external;
/**
* @dev Updates native transfer gas limit
* @param _nativeTransferGasLimit new native transfer gas limit. Must be greater or equal to MIN_NATIVE_TRANSFER_GAS_LIMIT.
*/
function updateNativeTransferGasLimit(uint16 _nativeTransferGasLimit) external;
/**
* @dev Updates `nativeTransferEnabled`. When true, the diamond is allowed to unwrap native tokens on transfer-out.
* @param _nativeTransferEnable the new value
*/
function updateNativeTransferEnabled(bool _nativeTransferEnable) external;
/**
* @dev Returns gas limit to be used for native transfers, with a minimum of `MIN_NATIVE_TRANSFER_GAS_LIMIT` (21k gas)
*/
function getNativeTransferGasLimit() external returns (uint16);
/**
* @dev Returns whether native transfers are enabled
*/
function getNativeTransferEnabled() external returns (bool);
/**
* @dev Returns the current value for reentrancy lock
*/
function getReentrancyLock() external returns (uint256);
/**
* @dev Emitted when `nativeTransferGasLimit` is updated
* @param newLimit new gas limit
*/
event NativeTransferGasLimitUpdated(uint16 newLimit);
/**
* @dev Emitted when `nativeTransferEnabled` is updated
* @param enabled the new value
*/
event NativeTransferEnabledUpdated(bool enabled);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IFeeTiers.sol";
/**
* @dev Interface for GNSFeeTiers facet (inherits types and also contains functions, events, and custom errors)
*/
interface IFeeTiersUtils is IFeeTiers {
/**
*
* @param _groupIndices group indices (pairs storage fee index) to initialize
* @param _groupVolumeMultipliers corresponding group volume multipliers (1e3)
* @param _feeTiersIndices fee tiers indices to initialize
* @param _feeTiers fee tiers values to initialize (feeMultiplier, pointsThreshold)
*/
function initializeFeeTiers(
uint256[] calldata _groupIndices,
uint256[] calldata _groupVolumeMultipliers,
uint256[] calldata _feeTiersIndices,
IFeeTiersUtils.FeeTier[] calldata _feeTiers
) external;
/**
* @dev Updates groups volume multipliers
* @param _groupIndices indices of groups to update
* @param _groupVolumeMultipliers corresponding new volume multipliers (1e3)
*/
function setGroupVolumeMultipliers(
uint256[] calldata _groupIndices,
uint256[] calldata _groupVolumeMultipliers
) external;
/**
* @dev Updates fee tiers
* @param _feeTiersIndices indices of fee tiers to update
* @param _feeTiers new fee tiers values (feeMultiplier, pointsThreshold)
*/
function setFeeTiers(uint256[] calldata _feeTiersIndices, IFeeTiersUtils.FeeTier[] calldata _feeTiers) external;
/**
* @dev Updates traders enrollment status in fee tiers program, including staking tier discounts
* @param _traders group of traders
* @param _values corresponding enrollment values
*/
function setTradersFeeTiersEnrollment(
address[] calldata _traders,
IFeeTiersUtils.TraderEnrollment[] calldata _values
) external;
/**
* @dev Credits points to traders
* @param _traders traders addresses
* @param _creditTypes types of credit (IMMEDIATE, CLAIMABLE)
* @param _points points to credit (1e18)
*/
function addTradersUnclaimedPoints(
address[] calldata _traders,
IFeeTiersUtils.CreditType[] calldata _creditTypes,
uint224[] calldata _points
) external;
/**
* @dev Increases daily points from a new trade, re-calculate trailing points, and cache daily fee tier for a trader.
* @param _trader trader address
* @param _volumeUsd trading volume in USD (1e18)
* @param _pairIndex pair index
*/
function updateTraderPoints(address _trader, uint256 _volumeUsd, uint256 _pairIndex) external;
/**
* @dev Returns fee amount after applying the trader's active fee tier and staking tier multiplier
* @param _trader address of trader
* @param _normalFeeAmountCollateral base fee amount (collateral precision)
*/
function calculateFeeAmount(address _trader, uint256 _normalFeeAmountCollateral) external view returns (uint256);
/**
* Returns the current number of active fee tiers
*/
function getFeeTiersCount() external view returns (uint256);
/**
* @dev Returns a fee tier's details (feeMultiplier, pointsThreshold)
* @param _feeTierIndex fee tier index
*/
function getFeeTier(uint256 _feeTierIndex) external view returns (IFeeTiersUtils.FeeTier memory);
/**
* @dev Returns fee tier details (feeMultiplier, pointsThreshold) for all active tiers
*/
function getFeeTiers() external view returns (IFeeTiers.FeeTier[] memory);
/**
* @dev Returns a group's volume multiplier
* @param _groupIndex group index (pairs storage fee index)
*/
function getGroupVolumeMultiplier(uint256 _groupIndex) external view returns (uint256);
/**
* @dev Returns a list of group volume multipliers
* @param _groupIndices group indices
*/
function getGroupVolumeMultipliers(uint256[] calldata _groupIndices) external view returns (uint256[] memory);
/**
* @dev Returns a trader's info (lastDayUpdated, trailingPoints)
* @param _trader trader address
*/
function getFeeTiersTraderInfo(address _trader) external view returns (IFeeTiersUtils.TraderInfo memory);
/**
* @dev Returns a trader's daily fee tier info (feeMultiplierCache, points)
* @param _trader trader address
* @param _day day
*/
function getFeeTiersTraderDailyInfo(
address _trader,
uint32 _day
) external view returns (IFeeTiersUtils.TraderDailyInfo memory);
/**
* @dev Returns a trader's daily fee tier info (feeMultiplierCache, points) for an array of days
* @param _trader trader address
* @param _days array of days
*/
function getFeeTiersTraderDailyInfoArray(
address _trader,
uint32[] calldata _days
) external view returns (IFeeTiersUtils.TraderDailyInfo[] memory);
/**
* @dev Returns a trader's fee tiers enrollment status
* @param _trader trader address
*/
function getTraderFeeTiersEnrollment(
address _trader
) external view returns (IFeeTiersUtils.TraderEnrollment memory);
/**
* @dev Returns a trader's unclaimed points, credited by Governance
* @param _trader trader address
*/
function getTraderUnclaimedPoints(address _trader) external view returns (uint224);
/**
* @dev Initializes GNS staking with tier configuration
* @param _tierIndices tier indices to initialize
* @param _tiers tier configurations (discountP, gnsThreshold)
* @param _gnsVaultAddress address for gGNS or another ERC4626 GNS vault
* @param _useGnsVaultBalance if true, gnsVaultAddress balance is used for tier calculation
*/
function initializeGnsStakingTiers(
uint256[] calldata _tierIndices,
IFeeTiers.FeeTier[] calldata _tiers,
address _gnsVaultAddress,
bool _useGnsVaultBalance
) external;
/**
* @dev Updates GNS staking tiers
* @param _tierIndices indices of tiers to update
* @param _tiers new tier configurations (discountP, gnsThreshold)
*/
function setGnsStakingTiers(uint256[] calldata _tierIndices, IFeeTiers.FeeTier[] calldata _tiers) external;
/**
* @dev Updated whether to use gGNS balance for tier calculation
* @param _useGnsVaultBalance new value
*/
function setUseGnsVaultBalance(bool _useGnsVaultBalance) external;
/**
* @dev Sets bonus GNS amounts for traders. Counts towards staking tier calculation, but is not withdrawable.
* @param _traders trader addresses
* @param _bonusAmounts bonus GNS amounts (no precision)
*/
function setGnsStakingBonusAmounts(address[] calldata _traders, uint24[] calldata _bonusAmounts) external;
/**
* @dev Syncs cached discount tiers for traders based on current tier configuration. Used to force a tier refresh in case of tier updates or gGNS repricing.
* @param _traders trader addresses to sync
*/
function syncGnsStakingTiers(address[] calldata _traders) external;
/**
* @dev Stakes GNS tokens for fee discounts
* @param _amountGns GNS amount to stake (1e18)
* @param _amountVaultGns gGNS amount to stake (1e18)
*/
function stakeGns(uint88 _amountGns, uint88 _amountVaultGns) external;
/**
* @dev Unstakes GNS tokens
* @param _amountGns GNS amount to unstake (1e18)
* @param _amountVaultGns gGNS amount to unstake (1e18)
*/
function unstakeGns(uint88 _amountGns, uint88 _amountVaultGns) external;
/**
* Returns the current number of active staking tiers
*/
function getGnsStakingTiersCount() external view returns (uint256);
/**
* @dev Returns GNS staking tier configuration
* @param _tierIndex tier index
*/
function getGnsStakingTier(uint256 _tierIndex) external view returns (IFeeTiers.FeeTier memory);
/**
* @dev Returns GNS staking tier configuration for all active tiers
*/
function getGnsStakingTiers() external view returns (IFeeTiers.FeeTier[] memory);
/**
* @dev Returns GNS vault address used for staking tiers
*/
function getGnsVaultAddress() external view returns (address);
/**
* @dev Returns whether gGNS balance is used for tier calculation
*/
function getUseGnsVaultBalance() external view returns (bool);
/**
* @dev Returns trader's GNS staking information
* @param _trader trader address
*/
function getGnsStakingInfo(address _trader) external view returns (IFeeTiers.GnsStakingInfo memory);
/**
* @dev Returns multiple traders' GNS staking information
* @param _traders trader addresses
*/
function getGnsStakingInfos(address[] calldata _traders) external view returns (IFeeTiers.GnsStakingInfo[] memory);
/**
* @dev Returns the GNS value of staked gGNS amount, based on the vault's current price per share. Returns 0 when `useGnsVaultBalance` is false
* @param _stakedVaultGns staked gGNS amount (1e18)
*/
function getStakedVaultGnsValue(uint88 _stakedVaultGns) external view returns (uint256);
/**
* @dev Emitted when group volume multipliers are updated
* @param groupIndices indices of updated groups
* @param groupVolumeMultipliers new corresponding volume multipliers (1e3)
*/
event GroupVolumeMultipliersUpdated(uint256[] groupIndices, uint256[] groupVolumeMultipliers);
/**
* @dev Emitted when fee tiers are updated
* @param feeTiersIndices indices of updated fee tiers
* @param feeTiers new corresponding fee tiers values (feeMultiplier, pointsThreshold)
*/
event FeeTiersUpdated(uint256[] feeTiersIndices, IFeeTiersUtils.FeeTier[] feeTiers);
/**
* @dev Emitted when a trader's daily points are updated
* @param trader trader address
* @param day day
* @param points points added (1e18 precision)
*/
event TraderDailyPointsIncreased(address indexed trader, uint32 indexed day, uint224 points);
/**
* @dev Emitted when a trader info is updated for the first time
* @param trader address of trader
* @param day day
*/
event TraderInfoFirstUpdate(address indexed trader, uint32 day);
/**
* @dev Emitted when a trader's trailing points are updated
* @param trader trader address
* @param fromDay from day
* @param toDay to day
* @param expiredPoints expired points amount (1e18 precision)
*/
event TraderTrailingPointsExpired(address indexed trader, uint32 fromDay, uint32 toDay, uint224 expiredPoints);
/**
* @dev Emitted when a trader's info is updated
* @param trader address of trader
* @param traderInfo new trader info value (lastDayUpdated, trailingPoints)
*/
event TraderInfoUpdated(address indexed trader, IFeeTiersUtils.TraderInfo traderInfo);
/**
* @dev Emitted when a trader's cached fee multiplier is updated (this is the one used in fee calculations)
* @param trader address of trader
* @param day day
* @param feeMultiplier new fee multiplier (1e3 precision)
*/
event TraderFeeMultiplierCached(address indexed trader, uint32 indexed day, uint32 feeMultiplier);
/**
* @dev Emitted when a trader's enrollment status is updated
* @param trader address of trader
* @param enrollment trader's new enrollment status
*/
event TraderEnrollmentUpdated(address indexed trader, IFeeTiersUtils.TraderEnrollment enrollment);
/**
* @dev Emitted when a trader is credited points by governance
* @param trader trader address
* @param day day the points were credited on, may be different from the day the points were claimed
* @param creditType credit type (IMMEDIATE, CLAIMABLE)
* @param points points added (1e18 precision)
*/
event TraderPointsCredited(
address indexed trader,
uint32 indexed day,
IFeeTiers.CreditType creditType,
uint224 points
);
/**
* @dev Emitted when a trader's unclaimed points are claimed
* @param trader trader address
* @param day day of claim
* @param points points added (1e18 precision)
*/
event TraderUnclaimedPointsClaimed(address indexed trader, uint32 indexed day, uint224 points);
/**
* @dev Emitted when GNS staking tiers are updated
* @param indices tier indices updated
* @param tiers new tier configurations
*/
event GnsStakingTiersUpdated(uint256[] indices, IFeeTiers.FeeTier[] tiers);
/**
* @dev Emitted when the use of GNS vault balance for tier calculation is updated
* @param newValue new value
*/
event UseGnsVaultBalanceUpdated(bool newValue);
/**
* @dev Emitted when a trader stakes GNS tokens
* @param trader trader address
* @param amountGns GNS amount staked (1e18)
* @param amountVaultGns gGNS amount staked (1e18)
*/
event GnsStaked(address indexed trader, uint88 amountGns, uint88 amountVaultGns);
/**
* @dev Emitted when a trader unstakes GNS tokens
* @param trader trader address
* @param amountGns GNS amount unstaked (1e18)
* @param amountVaultGns gGNS amount unstaked (1e18)
*/
event GnsUnstaked(address indexed trader, uint88 amountGns, uint88 amountVaultGns);
/**
* @dev Emitted when a trader's gns staking fee multiplier is updated (used in fee calculations, in conjunction with volume fee tiers multiplier)
* @param feeMultiplier new fee multiplier (1e3 precision)
*/
event GnsStakingFeeMultiplierCached(address indexed trader, uint32 feeMultiplier);
/**
* @dev Emitted when traders' bonus GNS amount is updated
* @param traders trader addresses
* @param bonusAmounts bonus amounts (no precision)
*/
event GnsStakingBonusUpdated(address[] traders, uint24[] bonusAmounts);
error WrongFeeTier();
error PointsOverflow();
error StakingCooldownActive();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IFundingFees.sol";
import "../types/IPriceAggregator.sol";
import "../types/ITradingCallbacks.sol";
/**
* @dev Interface for GNSFundingFees facet (inherits types and also contains functions, events, and custom errors)
*/
interface IFundingFeesUtils is IFundingFees {
/**
* @dev Updates max skew in collateral tokens for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _maxSkewCollateral new value (1e10)
*/
function setMaxSkewCollateral(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex,
uint80[] calldata _maxSkewCollateral
) external;
/**
* @dev Updates funding skew coefficient per year for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _skewCoefficientPerYear new value (1e26)
* @param _signedPairPrices signed pair market prices
*/
function setSkewCoefficientPerYear(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex,
uint112[] calldata _skewCoefficientPerYear,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Updates absolute funding velocity per year cap for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _absoluteVelocityPerYearCap new value (1e7)
* @param _signedPairPrices signed pair market prices
*/
function setAbsoluteVelocityPerYearCap(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex,
uint24[] calldata _absoluteVelocityPerYearCap,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Updates funding rate % per second absolute cap for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _absoluteRatePerSecondCap new value (1e10)
* @param _signedPairPrices signed pair market prices
*/
function setAbsoluteRatePerSecondCap(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex,
uint24[] calldata _absoluteRatePerSecondCap,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Updates funding theta threshold for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _thetaThresholdUsd new value (USD)
* @param _signedPairPrices signed pair market prices
*/
function setThetaThresholdUsd(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex,
uint32[] calldata _thetaThresholdUsd,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Enables/disables funding fees for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _fundingFeesEnabled new value
* @param _signedPairPrices signed pair market prices
*/
function setFundingFeesEnabled(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex,
bool[] calldata _fundingFeesEnabled,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Enables/disables APR multiplier for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _aprMultiplierEnabled new value
* @param _signedPairPrices signed pair market prices
*/
function setAprMultiplierEnabled(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex,
bool[] calldata _aprMultiplierEnabled,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Updates a pair's borrowing rate % per second
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _borrowingRatePerSecondP new value (1e10, %)
* @param _signedPairPrices signed pair market prices
*/
function setBorrowingRatePerSecondP(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex,
uint24[] calldata _borrowingRatePerSecondP,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Executes pending param update callback using signed prices stored in initial context call
* @param _paramUpdate param update to execute
*/
function paramUpdateCallbackWithSignedPrices(IFundingFees.PendingParamUpdate memory _paramUpdate) external;
/**
* @dev Stores initial acc funding / borrowing fees for a trade
* @dev HAS TO BE CALLED when a new trade is opened or when a trade's position size changes (+ store pending holding fees until now separately)
* @param _trader trader address
* @param _index index of trade
* @param _collateralIndex index of collateral
* @param _pairIndex index of the pair
* @param _long true if long, false if short
* @param _currentPairPrice current pair price (1e10)
*/
function storeTradeInitialAccFees(
address _trader,
uint32 _index,
uint8 _collateralIndex,
uint16 _pairIndex,
bool _long,
uint64 _currentPairPrice
) external;
/**
* @dev Realizes pending holding fees on an open trade
* @param _trader trader address
* @param _index index of trade
* @param _currentPairPrice current pair price (1e10)
*/
function realizeHoldingFeesOnOpenTrade(address _trader, uint32 _index, uint64 _currentPairPrice) external;
/**
* @dev Stores trade manually realized negative pnl in collateral tokens
* @param _trader address of trader
* @param _index index of trade
* @param _amountCollateral new amount of realized negative pnl in collateral tokens (collateral precision)
*/
function storeManuallyRealizedNegativePnlCollateral(
address _trader,
uint32 _index,
uint256 _amountCollateral
) external;
/**
* @dev Realizes pnl on an open trade
* @param _trader trader address
* @param _index index of trade
* @param _pnlCollateral pnl to realize in collateral tokens (collateral precision)
*/
function realizePnlOnOpenTrade(address _trader, uint32 _index, int256 _pnlCollateral) external;
/**
* @dev Realizes trading fees on an open trade
* @param _trader trader address
* @param _index index of trade
* @param _feesCollateral trading fees to charge in collateral tokens (collateral precision)
* @param _currentPairPrice current pair price (1e10)
* @return finalFeesCollateral trading fees charged in collateral tokens (collateral precision), same as input unless liquidated then 0
*/
function realizeTradingFeesOnOpenTrade(
address _trader,
uint32 _index,
uint256 _feesCollateral,
uint64 _currentPairPrice
) external returns (uint256 finalFeesCollateral);
/**
* @dev Decreases a trade's realized pnl and available collateral in diamond proportionally to size delta ratio over full size (for partial closes with collateral delta > 0)
* @param _trader trader address
* @param _index index of trade
* @param _positionSizeCollateralDelta position size collateral delta (collateral precision)
* @param _existingPositionSizeCollateral existing position size collateral (collateral precision)
* @param _newCollateralAmount new trade collateral amount after partial close (collateral precision)
*/
function downscaleTradeFeesData(
address _trader,
uint32 _index,
uint256 _positionSizeCollateralDelta,
uint256 _existingPositionSizeCollateral,
uint256 _newCollateralAmount
) external;
/**
* @dev Stores already transferred negative pnl for a trade (for partial closes with leverage delta > 0 and negative PnL)
* @param _trader address of trader
* @param _index index of trade
* @param _deltaCollateral delta in collateral tokens to store (collateral precision)
*/
function storeAlreadyTransferredNegativePnl(address _trader, uint32 _index, uint256 _deltaCollateral) external;
/**
* @dev Stores virtual available collateral in diamond to compensate if available in diamond would be < 0 without it (for collateral withdrawals)
* @param _trader address of trader
* @param _index index of trade
* @param _newTradeCollateralAmount new trade collateral amount (collateral precision)
*/
function storeVirtualAvailableCollateralInDiamond(
address _trader,
uint32 _index,
uint256 _newTradeCollateralAmount
) external;
/**
* @dev Stores UI partial close realized pnl for a trade
* @param _trader address of trader
* @param _index index of trade
* @param _deltaCollateral raw pnl realized in collateral tokens (collateral precision)
*/
function storeUiRealizedPnlPartialCloseCollateral(address _trader, uint32 _index, int256 _deltaCollateral) external;
/**
* @dev Stores UI withdrawn pnl for a trade
* @param _trader address of trader
* @param _index index of trade
* @param _deltaCollateral pnl withdrawn in collateral tokens (collateral precision)
*/
function storeUiPnlWithdrawnCollateral(address _trader, uint32 _index, uint256 _deltaCollateral) external;
/**
* @dev Stores UI realized trading fees for a trade
* @param _trader address of trader
* @param _index index of trade
* @param _deltaCollateral realized trading fees in collateral tokens (collateral precision)
*/
function storeUiRealizedTradingFeesCollateral(address _trader, uint32 _index, uint256 _deltaCollateral) external;
/**
* @dev Returns pending funding fees in collateral tokens for an open trade (collateral precision)
* @param _trader trader address
* @param _index index of trade
* @param _currentPairPrice current pair price (1e10)
*/
function getTradeFundingFeesCollateral(
address _trader,
uint32 _index,
uint64 _currentPairPrice
) external view returns (int256);
/**
* @dev Returns pending borrowing fees in collateral tokens for an open trade (collateral precision)
* @param _trader trader address
* @param _index index of trade
* @param _currentPairPrice current pair price (1e10)
*/
function getTradeBorrowingFeesCollateral(
address _trader,
uint32 _index,
uint64 _currentPairPrice
) external view returns (uint256);
/**
* @dev Returns trade pending funding fees, borrowing fees, old borrowing fees, and total holding fees in collateral tokens (collateral precision)
* @param _trader trader address
* @param _index index of trade
* @param _currentPairPrice current pair price (1e10)
* @return tradeHoldingFees trade holding fees in collateral tokens (collateral precision)
*/
function getTradePendingHoldingFeesCollateral(
address _trader,
uint32 _index,
uint64 _currentPairPrice
) external view returns (IFundingFees.TradeHoldingFees memory tradeHoldingFees);
/**
* @dev Returns pending acc funding fees for a pair
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _currentPairPrice current pair price (1e10)
* @return accFundingFeeLongP pending acc funding fee % for longs (1e20)
* @return accFundingFeeShortP pending acc funding fee % for shorts (1e20)
* @return currentFundingRatePerSecondP current funding rate % per second (1e18)
*/
function getPairPendingAccFundingFees(
uint8 _collateralIndex,
uint16 _pairIndex,
uint64 _currentPairPrice
) external view returns (int128 accFundingFeeLongP, int128 accFundingFeeShortP, int56 currentFundingRatePerSecondP);
/**
* @dev Returns pending acc borrowing fees for a pair
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
* @param _currentPairPrice current pair price (1e10)
* @return accBorrowingFeeP pending acc borrowing fee % (1e20)
*/
function getPairPendingAccBorrowingFees(
uint8 _collateralIndex,
uint16 _pairIndex,
uint64 _currentPairPrice
) external view returns (uint128 accBorrowingFeeP);
/**
* @dev Returns max skew in collateral tokens for a pair (1e10)
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getMaxSkewCollateral(uint8 _collateralIndex, uint16 _pairIndex) external view returns (uint80);
/**
* @dev Returns max skew in collateral tokens for pairs (1e10)
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getPairGlobalParamsArray(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex
) external view returns (PairGlobalParams[] memory);
/**
* @dev Returns funding fee params for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getPairFundingFeeParams(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex
) external view returns (IFundingFees.FundingFeeParams[] memory);
/**
* @dev Returns borrowing fee params for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getPairBorrowingFeeParams(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex
) external view returns (IFundingFees.BorrowingFeeParams[] memory);
/**
* @dev Returns funding fee data for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getPairFundingFeeData(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex
) external view returns (IFundingFees.PairFundingFeeData[] memory);
/**
* @dev Returns borrowing fee data for pairs
* @param _collateralIndex index of the collateral
* @param _pairIndex index of the pair
*/
function getPairBorrowingFeeData(
uint8[] calldata _collateralIndex,
uint16[] calldata _pairIndex
) external view returns (IFundingFees.PairBorrowingFeeData[] memory);
/**
* @dev Returns fees data for trade
* @param _trader trader address
* @param _index trade index
*/
function getTradeFeesData(address _trader, uint32 _index) external view returns (IFundingFees.TradeFeesData memory);
/**
* @dev Returns fees data for trades
* @param _trader trader address
* @param _index trade index
*/
function getTradeFeesDataArray(
address[] calldata _trader,
uint32[] calldata _index
) external view returns (IFundingFees.TradeFeesData[] memory);
/**
* @dev Returns UI realized pnl data for trades
* @param _trader trader address
* @param _index trade index
*/
function getTradeUiRealizedPnlDataArray(
address[] calldata _trader,
uint32[] calldata _index
) external view returns (IFundingFees.UiRealizedPnlData[] memory);
/**
* @dev Returns trade manually realized negative pnl in collateral tokens (collateral precision)
* @param _trader address of trader
* @param _index index of trade
*/
function getTradeManuallyRealizedNegativePnlCollateral(
address _trader,
uint32 _index
) external view returns (uint256);
/**
* @dev Returns pending param updates
* @param _index update index
*/
function getPendingParamUpdates(
uint32[] calldata _index
) external view returns (IFundingFees.PendingParamUpdate[] memory);
/**
* @dev Returns realized pnl in collateral tokens for an open trade
* @param _trader trader address
* @param _index trade index
* @return realizedPnlCollateral realized pnl in collateral tokens (collateral precision)
* @return realizedTradingFeesCollateral realized trading fees in collateral tokens (collateral precision)
* @return totalRealizedPnlCollateral total realized pnl in collateral tokens (collateral precision)
*/
function getTradeRealizedPnlCollateral(
address _trader,
uint32 _index
)
external
view
returns (
int256 realizedPnlCollateral,
uint256 realizedTradingFeesCollateral,
int256 totalRealizedPnlCollateral
);
/**
* @dev Returns realized trading fees in collateral tokens for an open trade (collateral precision)
* @param _trader trader address
* @param _index trade index
*/
function getTradeRealizedTradingFeesCollateral(address _trader, uint32 _index) external view returns (uint256);
/**
* @dev Emitted when a pair's max skew collateral is updated
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param maxSkewCollateral new value (1e10)
*/
event MaxSkewCollateralUpdated(uint8 indexed collateralIndex, uint16 indexed pairIndex, uint80 maxSkewCollateral);
/**
* @dev Emitted when a pair's skew coefficient per year is updated
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param skewCoefficientPerYear new value (1e26)
*/
event SkewCoefficientPerYearUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint112 skewCoefficientPerYear
);
/**
* @dev Emitted when a pair's absolute velocity per year cap is updated
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param absoluteVelocityPerYearCap new value (1e7)
*/
event AbsoluteVelocityPerYearCapUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint24 absoluteVelocityPerYearCap
);
/**
* @dev Emitted when a pair's funding rate % per second absolute cap is updated
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param absoluteRatePerSecondCap new value (1e10)
*/
event AbsoluteRatePerSecondCapUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint24 absoluteRatePerSecondCap
);
/**
* @dev Emitted when a pair's theta USD threshold is updated
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param thetaThresholdUsd new value (USD)
*/
event ThetaThresholdUsdUpdated(uint8 indexed collateralIndex, uint16 indexed pairIndex, uint32 thetaThresholdUsd);
/**
* @dev Emitted when a pair's funding fees are enabled/disabled
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param fundingFeesEnabled new value
*/
event FundingFeesEnabledUpdated(uint8 indexed collateralIndex, uint16 indexed pairIndex, bool fundingFeesEnabled);
/**
* @dev Emitted when a pair's APR multiplier is enabled/disabled
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param aprMultiplierEnabled new value
*/
event AprMultiplierEnabledUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
bool aprMultiplierEnabled
);
/**
* @dev Emitted when a pair's borrowing rate % per second is updated
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param borrowingRatePerSecondP new value (1e10, %)
*/
event BorrowingRatePerSecondPUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint24 borrowingRatePerSecondP
);
/**
* @dev Emitted when a pair's pending acc funding fees are stored
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param data pair funding fee data
*/
event PendingAccFundingFeesStored(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
IFundingFees.PairFundingFeeData data
);
/**
* @dev Emitted when a pair's pending acc borrowing fees are stored
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param data pair borrowing fee data
*/
event PendingAccBorrowingFeesStored(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
IFundingFees.PairBorrowingFeeData data
);
/**
* @dev Emitted when a trade's initial acc fees are stored or reset
* @param trader trader address
* @param index index of trade
* @param collateralIndex index of collateral
* @param pairIndex index of the pair
* @param long true if long, false if short
* @param currentPairPrice current pair price (1e10)
* @param newInitialAccFundingFeeP new initial acc funding fee % (1e20)
* @param newInitialAccBorrowingFeeP new initial acc borrowing fee % (1e20)
*/
event TradeInitialAccFeesStored(
address indexed trader,
uint32 indexed index,
uint8 collateralIndex,
uint16 pairIndex,
bool long,
uint64 currentPairPrice,
int128 newInitialAccFundingFeeP,
uint128 newInitialAccBorrowingFeeP
);
/**
* @dev Emitted when holding fees are realized (earned) on an open trade
* @param collateralIndex index of the collateral
* @param trader trader address
* @param index index of trade
* @param currentPairPrice current pair price (1e10)
* @param tradeHoldingFees trade holding fees in collateral tokens (collateral precision)
* @param newRealizedPnlCollateral new realized pnl value for trade in collateral tokens (collateral precision)
*/
event HoldingFeesRealizedOnTrade(
uint8 indexed collateralIndex,
address indexed trader,
uint32 indexed index,
uint64 currentPairPrice,
IFundingFees.TradeHoldingFees tradeHoldingFees,
int256 newRealizedPnlCollateral
);
/**
* @dev Emitted when holding fees are realized (charged) on an open trade
* @param collateralIndex index of the collateral
* @param trader trader address
* @param index index of trade
* @param currentPairPrice current pair price (1e10)
* @param tradeHoldingFees trade holding fees in collateral tokens (collateral precision)
* @param availableCollateralInDiamond trade available collateral in diamond contract (collateral precision)
* @param amountSentToVaultCollateral amount sent to vault in collateral tokens (collateral precision)
* @param newRealizedTradingFeesCollateral new realized trading fees in collateral tokens (collateral precision)
* @param newRealizedPnlCollateral new realized pnl in collateral tokens (collateral precision)
*/
event HoldingFeesChargedOnTrade(
uint8 indexed collateralIndex,
address indexed trader,
uint32 indexed index,
uint64 currentPairPrice,
IFundingFees.TradeHoldingFees tradeHoldingFees,
uint256 availableCollateralInDiamond,
uint256 amountSentToVaultCollateral,
uint256 newRealizedTradingFeesCollateral,
int256 newRealizedPnlCollateral
);
/**
* @dev Emitted when negative pnl is manually realized on an open trade
* @param trader trader address
* @param index trade index
* @param newManuallyRealizedNegativePnlCollateral new manually realized negative pnl in collateral tokens (collateral precision)
*/
event ManuallyRealizedNegativePnlCollateralStored(
address indexed trader,
uint32 indexed index,
uint128 newManuallyRealizedNegativePnlCollateral
);
/**
* @dev Emitted when pnl is realized on an open trade
* @param trader trader address
* @param index index of trade
* @param pnlCollateral pnl realized in collateral tokens (collateral precision)
* @param newRealizedPnlCollateral new realized pnl value for trade in collateral tokens (collateral precision)
*/
event PnlRealizedOnOpenTrade(
address indexed trader,
uint32 indexed index,
int256 pnlCollateral,
int256 newRealizedPnlCollateral
);
/**
* @dev Emitted when trading fees are realized on an open trade
* @param collateralIndex index of the collateral
* @param trader trader address
* @param index index of trade
* @param tradingFeesCollateral trading fees input in collateral tokens (collateral precision)
* @param finalTradingFeesCollateral trading fees realized in collateral tokens (collateral precision)
* @param newRealizedFeesCollateral new realized fees value for trade in collateral tokens (collateral precision)
* @param newRealizedPnlCollateral new realized pnl value for trade in collateral tokens (collateral precision)
* @param amountSentFromVaultCollateral amount sent from vault in collateral tokens (collateral precision)
*/
event TradingFeesRealized(
uint8 indexed collateralIndex,
address indexed trader,
uint32 indexed index,
uint256 tradingFeesCollateral,
uint256 finalTradingFeesCollateral,
uint256 newRealizedFeesCollateral,
int256 newRealizedPnlCollateral,
uint256 amountSentFromVaultCollateral
);
/**
* @dev Emitted when a trade's realized pnl and available in diamond is scaled down
* @param trader trader address
* @param index trade index
* @param positionSizeCollateralDelta position size collateral delta (collateral precision)
* @param existingPositionSizeCollateral existing position size collateral (collateral precision)
* @param newCollateralAmount new trade collateral amount (collateral precision)
* @param newTradeFeesData new trade fees data
*/
event TradeFeesDataDownscaled(
address indexed trader,
uint32 indexed index,
uint256 positionSizeCollateralDelta,
uint256 existingPositionSizeCollateral,
uint256 newCollateralAmount,
IFundingFees.TradeFeesData newTradeFeesData
);
/**
* @dev Emitted when a trade's already transferred negative pnl is stored
* @param trader trader address
* @param index trade index
* @param deltaCollateral delta in collateral tokens to store (collateral precision)
* @param newAlreadyTransferredNegativePnlCollateral new already transferred negative pnl in collateral tokens (collateral precision)
*/
event AlreadyTransferredNegativePnlStored(
address indexed trader,
uint32 indexed index,
uint256 deltaCollateral,
uint128 newAlreadyTransferredNegativePnlCollateral
);
/**
* @dev Emitted when a trade's virtual available collateral in diamond is stored
* @param trader trader address
* @param index trade index
* @param newTradeCollateralAmount new trade collateral amount (collateral precision)
* @param currentManuallyRealizedNegativePnlCollateral current manually realized negative pnl collateral value (collateral precision)
* @param manuallyRealizedNegativePnlCollateralCapped whether manually realized negative pnl collateral value was capped (collateral precision)
* @param virtualAvailableCollateralInDiamondDelta virtual available collateral in diamond delta to keep available collateral in diamond >= 0 (collateral precision)
* @param newVirtualAvailableCollateralInDiamond new virtual available collateral in diamond (collateral precision)
*/
event VirtualAvailableCollateralInDiamondStored(
address indexed trader,
uint32 indexed index,
uint256 newTradeCollateralAmount,
uint256 currentManuallyRealizedNegativePnlCollateral,
bool manuallyRealizedNegativePnlCollateralCapped,
uint256 virtualAvailableCollateralInDiamondDelta,
uint256 newVirtualAvailableCollateralInDiamond
);
/**
* @dev Emitted when a param update is requested
* @param collateralIndex index of the collateral
* @param pairIndex index of the pair
* @param updateType type of update
* @param newValue new value
*/
event ParamUpdateRequested(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
IFundingFees.ParamUpdateType updateType,
uint224 newValue
);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IOtc.sol";
/**
* @dev Interface for GNSOtc facet (inherits types and also contains functions, events, and custom errors)
*/
interface IOtcUtils is IOtc {
/**
* @dev Initializer for OTC facet
* @param _config new OTC Config
*/
function initializeOtc(IOtcUtils.OtcConfig memory _config) external;
/**
* @dev Updates OTC config
* @param _config new OTC Config. Sum of `treasuryShareP`, `stakingShareP`, `burnShareP` must equal 100 and `premiumP` must be less than or equal to MAX_PREMIUM_P
*/
function updateOtcConfig(IOtcUtils.OtcConfig memory _config) external;
/**
* @dev Increases OTC balance for a collateral
* @param _collateralIndex collateral index
* @param _collateralAmount amount of collateral to increase (collateral precision)
*/
function addOtcCollateralBalance(uint8 _collateralIndex, uint256 _collateralAmount) external;
/**
* @dev OTC Buys GNS from caller for `_amountCollateral` of `_collateralIndex`.
* When collateral is GNS, no tokens are transferred to or from the caller and no premium is paid.
*
* @param _collateralIndex collateral index
* @param _collateralAmount amount of collateral to trade (collateral precision)
*/
function sellGnsForCollateral(uint8 _collateralIndex, uint256 _collateralAmount) external;
/**
* @dev Returns OTC Config
*/
function getOtcConfig() external view returns (IOtcUtils.OtcConfig memory);
/**
* @dev Returns OTC balance for a collateral (collateral precision)
* @param _collateralIndex collateral index
*/
function getOtcBalance(uint8 _collateralIndex) external view returns (uint256);
/**
* @dev Returns OTC rate (price + premium) of GNS in collateral (1e10)
* @param _collateralIndex collateral index
*/
function getOtcRate(uint8 _collateralIndex) external view returns (uint256);
/**
* @dev Emitted when OTCConfig is updated
* @param config new OTC config
*/
event OtcConfigUpdated(IOtcUtils.OtcConfig config);
/**
* @dev Emitted when OTC balance is updated
* @param collateralIndex collateral index
* @param balanceCollateral new balance (collateral precision)
*/
event OtcBalanceUpdated(uint8 indexed collateralIndex, uint256 balanceCollateral);
/**
* @dev Emitted when an OTC trade is executed
* @param collateralIndex collateral index
* @param collateralAmount amount of collateral traded (collateral precision)
* @param gnsPriceCollateral effective gns/collateral price, including premium (1e10)
* @param treasuryAmountGns amount of GNS sent to treasury (1e18)
* @param stakingAmountGns amount of GNS sent to GNS Staking (1e18)
* @param burnAmountGns amount of GNS burned (1e18)
*/
event OtcExecuted(
uint8 indexed collateralIndex,
uint256 collateralAmount,
uint256 gnsPriceCollateral,
uint256 treasuryAmountGns,
uint256 stakingAmountGns,
uint256 burnAmountGns
);
error InvalidShareSum();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IPairsStorage.sol";
/**
* @dev Interface for GNSPairsStorage facet (inherits types and also contains functions, events, and custom errors)
*/
interface IPairsStorageUtils is IPairsStorage {
/**
* @dev Initializes liquidation params for all existing groups
* @param _groupLiquidationParams liquidation params for each group (index corresponds to group index)
*/
function initializeGroupLiquidationParams(
IPairsStorage.GroupLiquidationParams[] memory _groupLiquidationParams
) external;
/**
* @dev Copies all existing fee groups to new mapping, multiplies existing groups min/max lev by 1e3, initializes new global trade fee params
* @param _tradeFeeParams global trade fee params
*/
function initializeNewFees(IPairsStorage.GlobalTradeFeeParams memory _tradeFeeParams) external;
/**
* @dev Initializes referral fee change (adds 50% of referral share to gov and otc, now gov+otc+trigger+gToken = 100%, referral fee charged first)
* @dev Only useful for v9.4.4 -> v9.4.5 transition
*/
function initializeReferralFeeChange() external;
/**
* @dev Adds new trading pairs
* @param _pairs pairs to add
*/
function addPairs(Pair[] calldata _pairs) external;
/**
* @dev Updates trading pairs
* @param _pairIndices indices of pairs
* @param _pairs new pairs values
*/
function updatePairs(uint256[] calldata _pairIndices, Pair[] calldata _pairs) external;
/**
* @dev Adds new pair groups
* @param _groups groups to add
*/
function addGroups(Group[] calldata _groups) external;
/**
* @dev Updates pair groups
* @param _ids indices of groups
* @param _groups new groups values
*/
function updateGroups(uint256[] calldata _ids, Group[] calldata _groups) external;
/**
* @dev Adds new pair fees groups
* @param _fees fees to add
*/
function addFees(FeeGroup[] calldata _fees) external;
/**
* @dev Updates pair fees groups
* @param _ids indices of fees
* @param _fees new fees values
*/
function updateFees(uint256[] calldata _ids, FeeGroup[] calldata _fees) external;
/**
* @dev Updates pair custom max leverages (if unset group default is used); useful to delist a pair if new value is below the pair's group minLeverage
* @param _indices indices of pairs
* @param _values new custom max leverages (1e3 precision)
*/
function setPairCustomMaxLeverages(uint256[] calldata _indices, uint256[] calldata _values) external;
/**
* @dev Updates group liquidation params (will only apply for trades opened after the change)
* @param _groupIndex index of group
* @param _params new liquidation params
*/
function setGroupLiquidationParams(
uint256 _groupIndex,
IPairsStorage.GroupLiquidationParams memory _params
) external;
/**
* @dev Updates global trade fee params
* @param _feeParams new fee params
*/
function setGlobalTradeFeeParams(IPairsStorage.GlobalTradeFeeParams memory _feeParams) external;
/**
* @dev Updates pairs counter trade max leverage
* @param _indices indices of pairs
* @param _values new max leverages (1e3), 0 disables counter trades for the pair
*/
function setPairCounterTradeMaxLeverages(uint16[] calldata _indices, uint24[] calldata _values) external;
/**
* @dev Updates counter trade fee rate multiplier
* @param _indices indices of pairs
* @param _values new multipliers (1e3), 1e3 disables counter trade discount
*/
function setPairCounterTradeFeeRateMultipliers(uint16[] calldata _indices, uint16[] calldata _values) external;
/**
* @dev Updates pair trade fee params override for pairs
* @param _collateralIndices array of collateral indices
* @param _pairIndices array of pair indices
* @param _feeParams array of new fee params
*/
function setPairGlobalTradeFeeParamsOverrides(
uint8[] calldata _collateralIndices,
uint16[] calldata _pairIndices,
IPairsStorage.GlobalTradeFeeParams[] calldata _feeParams
) external;
/**
* @dev Updates pair flags for pairs
* @param _collateralIndices array of collateral indices
* @param _pairIndices array of pair indices
* @param _pairFlags array of new pair flags
*/
function setPairFlags(
uint8[] calldata _collateralIndices,
uint16[] calldata _pairIndices,
IPairsStorage.PairFlags[] calldata _pairFlags
) external;
/**
* @dev Returns data needed by price aggregator when doing a new price request
* @param _pairIndex index of pair
* @return from pair from (eg. BTC)
* @return to pair to (eg. USD)
*/
function pairJob(uint256 _pairIndex) external view returns (string memory from, string memory to);
/**
* @dev Returns whether a pair is listed
* @param _from pair from (eg. BTC)
* @param _to pair to (eg. USD)
*/
function isPairListed(string calldata _from, string calldata _to) external view returns (bool);
/**
* @dev Returns whether a pair index is listed
* @param _pairIndex index of pair to check
*/
function isPairIndexListed(uint256 _pairIndex) external view returns (bool);
/**
* @dev Returns a pair's details
* @param _index index of pair
*/
function pairs(uint256 _index) external view returns (Pair memory);
/**
* @dev Returns number of listed pairs
*/
function pairsCount() external view returns (uint256);
/**
* @dev Returns the sum of trader's custom spread + global pair's spread % (1e10 precision)
* @param _trader trader address
* @param _pairIndex index of pair
*/
function pairSpreadP(address _trader, uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns array of each traders' custom spread + global pair's spread % (1e10 precision)
* @param _trader array of traders
* @param _pairIndex array of pairs indices
*/
function pairSpreadPArray(
address[] calldata _trader,
uint256[] calldata _pairIndex
) external view returns (uint256[] memory);
/**
* @dev Returns a pair's min leverage (1e3 precision)
* @param _pairIndex index of pair
*/
function pairMinLeverage(uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns a pair's total position size fee % (1e10 precision)
* @param _pairIndex index of pair
*/
function pairTotalPositionSizeFeeP(uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns a pair's total liquidation collateral fee % (1e10 precision)
* @param _pairIndex index of pair
*/
function pairTotalLiqCollateralFeeP(uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns a pair's oracle position size fee % (1e10 precision)
* @param _pairIndex index of pair
*/
function pairOraclePositionSizeFeeP(uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns a pair's min position size in USD (1e18 precision)
* @param _pairIndex index of pair
*/
function pairMinPositionSizeUsd(uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns global trade fee params
*/
function getGlobalTradeFeeParams() external view returns (IPairsStorage.GlobalTradeFeeParams memory);
/**
* @dev Returns a pair's minimum trading fee in USD (1e18 precision)
* @param _pairIndex index of pair
*/
function pairMinFeeUsd(uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns a group details
* @param _index index of group
*/
function groups(uint256 _index) external view returns (Group memory);
/**
* @dev Returns number of listed groups
*/
function groupsCount() external view returns (uint256);
/**
* @dev Returns a fee group details
* @param _index index of fee group
*/
function fees(uint256 _index) external view returns (FeeGroup memory);
/**
* @dev Returns number of listed fee groups
*/
function feesCount() external view returns (uint256);
/**
* @dev Returns a pair's active max leverage; custom if set, otherwise group default (1e3 precision)
* @param _pairIndex index of pair
*/
function pairMaxLeverage(uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns a pair's custom max leverage; 0 if not set (1e3 precision)
* @param _pairIndex index of pair
*/
function pairCustomMaxLeverage(uint256 _pairIndex) external view returns (uint256);
/**
* @dev Returns all listed pairs custom max leverages (1e3 precision)
*/
function getAllPairsRestrictedMaxLeverage() external view returns (uint256[] memory);
/**
* @dev Returns a group's liquidation params
*/
function getGroupLiquidationParams(
uint256 _groupIndex
) external view returns (IPairsStorage.GroupLiquidationParams memory);
/**
* @dev Returns a pair's group liquidation params
*/
function getPairLiquidationParams(
uint256 _pairIndex
) external view returns (IPairsStorage.GroupLiquidationParams memory);
/**
* @dev Returns a pair's counter trade max leverage (1e3)
* @param _pairIndex index of pair
*/
function getPairCounterTradeMaxLeverage(uint16 _pairIndex) external view returns (uint24);
/**
* @dev Returns multiple pairs counter trade max leverage (1e3)
* @param _pairIndex indices of pairs
*/
function getPairCounterTradeMaxLeverages(uint16[] calldata _pairIndex) external view returns (uint24[] memory);
/**
* @dev Returns a pair's counter trade fee rate multiplier (1e3)
* @param _pairIndex index of pair
*/
function getPairCounterTradeFeeRateMultiplier(uint16 _pairIndex) external view returns (uint16);
/**
* @dev Returns multiple pairs counter trade fee rate multipliers (1e3)
* @param _pairIndex indices of pairs
*/
function getPairCounterTradeFeeRateMultipliers(
uint16[] calldata _pairIndex
) external view returns (uint16[] memory);
/**
* @dev Returns the effective total position size fee % (1e10 precision) after counter trade multiplier where applicable
* @param _pairIndex index of pair
* @param _isCounterTrade whether the trade is a counter trade
*/
function getEffectiveTotalPositionSizeFeeP(uint16 _pairIndex, bool _isCounterTrade) external view returns (uint256);
/**
* @dev Returns global trade fee params for pair
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
*/
function getPairGlobalTradeFeeParamsOverride(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (IPairsStorage.GlobalTradeFeeParams memory);
/**
* @dev Returns global trade fee params for array of pairs
* @param _collateralIndex index of collateral
* @param _pairIndices array of pair indices
*/
function getPairGlobalTradeFeeParamsOverrideArray(
uint8 _collateralIndex,
uint16[] calldata _pairIndices
) external view returns (IPairsStorage.GlobalTradeFeeParams[] memory);
/**
* @dev Returns pair flags for a collateral and pair index
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
*/
function getPairFlags(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (IPairsStorage.PairFlags memory);
/**
* @dev Returns list of pair flags for a collateral and pair indices
* @param _collateralIndex index of collateral
* @param _pairIndices array of pair indices
*/
function getPairFlagsArray(
uint8 _collateralIndex,
uint16[] calldata _pairIndices
) external view returns (IPairsStorage.PairFlags[] memory);
/**
* @dev Returns the active trade fee params for a pair
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
*/
function getTradeFeeParams(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (IPairsStorage.GlobalTradeFeeParams memory);
/**
* @dev Returns the active trade fee params for a list of pairs
* @param _collateralIndex index of collateral
* @param _pairIndices array of pair indices
*/
function getTradeFeeParamsArray(
uint8 _collateralIndex,
uint16[] calldata _pairIndices
) external view returns (IPairsStorage.GlobalTradeFeeParams[] memory);
/**
* @dev Emitted when a new pair is listed
* @param index index of pair
* @param from pair from (eg. BTC)
* @param to pair to (eg. USD)
*/
event PairAdded(uint256 index, string from, string to);
/**
* @dev Emitted when a pair is updated
* @param index index of pair
*/
event PairUpdated(uint256 index);
/**
* @dev Emitted when a pair's custom max leverage is updated
* @param index index of pair
* @param maxLeverage new max leverage (1e3 precision)
*/
event PairCustomMaxLeverageUpdated(uint256 indexed index, uint256 maxLeverage);
/**
* @dev Emitted when a new group is added
* @param index index of group
* @param name name of group
*/
event GroupAdded(uint256 index, string name);
/**
* @dev Emitted when a group is updated
* @param index index of group
*/
event GroupUpdated(uint256 index);
/**
* @dev Emitted when a new fee group is added
* @param index index of fee group
* @param feeGroup fee group
*/
event FeeAdded(uint256 index, FeeGroup feeGroup);
/**
* @dev Emitted when a fee group is updated
* @param index index of fee group
* @param feeGroup updated fee group
*/
event FeeUpdated(uint256 index, FeeGroup feeGroup);
/**
* @dev Emitted when a group liquidation params are updated
* @param index index of group
* @param params new group liquidation params
*/
event GroupLiquidationParamsUpdated(uint256 index, IPairsStorage.GroupLiquidationParams params);
/**
* @dev Emitted when global trade fee params are updated
* @param feeParams new fee params
*/
event GlobalTradeFeeParamsUpdated(IPairsStorage.GlobalTradeFeeParams feeParams);
/**
* @dev Emitted when a pair's counter trade max leverage is updated
* @param pairIndex index of pair
* @param newValue new max leverage (1e3)
*/
event CounterTradeMaxLeverageUpdated(uint16 indexed pairIndex, uint24 newValue);
/**
* @dev Emitted when counter trade fee rate multiplier is updated
* @param newValue new multiplier (1e3)
*/
event CounterTradeFeeRateMultiplierUpdated(uint16 indexed pairIndex, uint16 newValue);
/**
* @dev Emitted when a pair's override global trade fee params are updated
* @param collateralIndex index of collateral
* @param pairIndex index of pair
* @param feeParams new fee params
*/
event PairGlobalTradeFeeParamsOverridesUpdated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
IPairsStorage.GlobalTradeFeeParams feeParams
);
/**
* @dev Emitted when a pair's flags are updated
* @param collateralIndex index of collateral
* @param pairIndex index of pair
* @param pairFlags new pair flags
*/
event PairFlagsUpdated(uint8 indexed collateralIndex, uint16 indexed pairIndex, IPairsStorage.PairFlags pairFlags);
error PairNotListed();
error GroupNotListed();
error FeeNotListed();
error WrongLeverages();
error WrongFees();
error PairAlreadyListed();
error MaxLiqSpreadPTooHigh();
error WrongLiqParamsThresholds();
error WrongLiqParamsLeverages();
error StartLiqThresholdTooHigh();
error EndLiqThresholdTooLow();
error StartLeverageTooLow();
error EndLeverageTooHigh();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Chainlink} from "@chainlink/contracts/src/v0.8/Chainlink.sol";
import "../types/IPriceAggregator.sol";
import "../types/ITradingStorage.sol";
import "../types/ITradingCallbacks.sol";
/**
* @dev Interface for GNSPriceAggregator facet (inherits types and also contains functions, events, and custom errors)
*/
interface IPriceAggregatorUtils is IPriceAggregator {
/**
* @dev Initializes price aggregator facet
* @param _linkToken LINK token address
* @param _linkUsdPriceFeed LINK/USD price feed address
* @param _twapInterval TWAP interval (seconds)
* @param _minAnswers answers count at which a trade is executed with median
* @param _oracles chainlink oracle addresses
* @param _jobIds chainlink job ids (market/lookback)
* @param _collateralIndices collateral indices
* @param _gnsCollateralLiquidityPools corresponding GNS/collateral liquidity pool values
* @param _collateralUsdPriceFeeds corresponding collateral/USD chainlink price feeds
*/
function initializePriceAggregator(
address _linkToken,
IChainlinkFeed _linkUsdPriceFeed,
uint24 _twapInterval,
uint8 _minAnswers,
address[] memory _oracles,
bytes32[2] memory _jobIds,
uint8[] calldata _collateralIndices,
LiquidityPoolInput[] memory _gnsCollateralLiquidityPools,
IChainlinkFeed[] memory _collateralUsdPriceFeeds
) external;
/**
* @dev Initializes lookback job count
* @param _limitJobCount lookback job count
*/
function initializeLimitJobCount(uint8 _limitJobCount) external;
/**
* @dev Initializes max deviation percentages
* @param _maxMarketDeviationP max market order deviation percentage (1e3, %)
* @param _maxLookbackDeviationP max lookback order deviation percentage (1e3, %)
*/
function initializeMaxDeviationsP(uint24 _maxMarketDeviationP, uint24 _maxLookbackDeviationP) external;
/**
* @dev Updates LINK/USD chainlink price feed
* @param _value new value
*/
function updateLinkUsdPriceFeed(IChainlinkFeed _value) external;
/**
* @dev Updates collateral/USD chainlink price feed
* @param _collateralIndex collateral index
* @param _value new value
*/
function updateCollateralUsdPriceFeed(uint8 _collateralIndex, IChainlinkFeed _value) external;
/**
* @dev Updates collateral/GNS liquidity pool
* @param _collateralIndex collateral index
* @param _liquidityPoolInput new values
*/
function updateCollateralGnsLiquidityPool(
uint8 _collateralIndex,
LiquidityPoolInput calldata _liquidityPoolInput
) external;
/**
* @dev Updates TWAP interval
* @param _twapInterval new value (seconds)
*/
function updateTwapInterval(uint24 _twapInterval) external;
/**
* @dev Updates minimum answers count
* @param _value new value
*/
function updateMinAnswers(uint8 _value) external;
/**
* @dev Adds an oracle
* @param _a new value
*/
function addOracle(address _a) external;
/**
* @dev Replaces an oracle
* @param _index oracle index
* @param _a new value
*/
function replaceOracle(uint256 _index, address _a) external;
/**
* @dev Removes an oracle
* @param _index oracle index
*/
function removeOracle(uint256 _index) external;
/**
* @dev Updates market job id
* @param _jobId new value
*/
function setMarketJobId(bytes32 _jobId) external;
/**
* @dev Updates lookback job id
* @param _jobId new value
*/
function setLimitJobId(bytes32 _jobId) external;
/**
* @dev Updates lookback job count
* @param _limitJobCount new value
*/
function setLimitJobCount(uint8 _limitJobCount) external;
/**
* @dev Updates max market order deviation percentage
* @param _maxMarketDeviationP new value (1e3, %)
*/
function setMaxMarketDeviationP(uint24 _maxMarketDeviationP) external;
/**
* @dev Updates max lookback order deviation percentage
* @param _maxLookbackDeviationP new value (1e3, %)
*/
function setMaxLookbackDeviationP(uint24 _maxLookbackDeviationP) external;
/**
* @dev Stores pending order, requests price from oracles, and returns pending order id
* @param _input price request input (see IPriceAggregator)
*/
function getPrice(IPriceAggregator.GetPriceInput memory _input) external returns (ITradingStorage.Id memory);
/**
* @dev Fulfills price request, called by chainlink oracles
* @param _requestId request id
* @param _priceData price data
*/
function fulfill(bytes32 _requestId, uint256 _priceData) external;
/**
* @dev Claims back LINK tokens, called by gov fund
*/
function claimBackLink() external;
/**
* @dev Validates signed pair prices, stores them temporarily as well as the corresponding median price for each pair after filtering outliers and the list of corresponding pair indices (for cleanup)
* @param _signedPairPrices signed pair market prices (one entry per oracle)
* @param _isLookback whether the prices are for a lookback order
*/
function validateSignedPairPrices(
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices,
bool _isLookback
) external;
/**
* @dev Cleans up all temporary storage related to signed pair prices
*/
function cleanUpSignedPairPrices() external;
/**
* @dev Returns LINK fee for price request
* @param _collateralIndex collateral index
* @param _trader trader address
* @param _pairIndex pair index
* @param _positionSizeCollateral position size in collateral tokens (collateral precision)
* @param _isCounterTrade whether corresponding trade is counter trade
*/
function getLinkFee(
uint8 _collateralIndex,
address _trader,
uint16 _pairIndex,
uint256 _positionSizeCollateral,
bool _isCounterTrade
) external view returns (uint256);
/**
* @dev Returns collateral/USD price
* @param _collateralIndex index of collateral
*/
function getCollateralPriceUsd(uint8 _collateralIndex) external view returns (uint256);
/**
* @dev Returns USD normalized value from collateral value
* @param _collateralIndex index of collateral
* @param _collateralValue collateral value (collateral precision)
*/
function getUsdNormalizedValue(uint8 _collateralIndex, uint256 _collateralValue) external view returns (uint256);
/**
* @dev Returns collateral value (collateral precision) from USD normalized value
* @param _collateralIndex index of collateral
* @param _normalizedValue normalized value (1e18 USD)
*/
function getCollateralFromUsdNormalizedValue(
uint8 _collateralIndex,
uint256 _normalizedValue
) external view returns (uint256);
/**
* @dev Returns GNS/USD price based on GNS/collateral price
* @param _collateralIndex index of collateral
*/
function getGnsPriceUsd(uint8 _collateralIndex) external view returns (uint256);
/**
* @dev Returns GNS/USD price based on GNS/collateral price
* @param _collateralIndex index of collateral
* @param _gnsPriceCollateral GNS/collateral price (1e10)
*/
function getGnsPriceUsd(uint8 _collateralIndex, uint256 _gnsPriceCollateral) external view returns (uint256);
/**
* @dev Returns GNS/collateral price
* @param _collateralIndex index of collateral
*/
function getGnsPriceCollateralIndex(uint8 _collateralIndex) external view returns (uint256);
/**
* @dev Returns GNS/collateral price
* @param _collateral address of the collateral
*/
function getGnsPriceCollateralAddress(address _collateral) external view returns (uint256);
/**
* @dev Returns the link/usd price feed address
*/
function getLinkUsdPriceFeed() external view returns (IChainlinkFeed);
/**
* @dev Returns the twap interval in seconds
*/
function getTwapInterval() external view returns (uint24);
/**
* @dev Returns the minimum answers to execute an order and take the median
*/
function getMinAnswers() external view returns (uint8);
/**
* @dev Returns the market job id
*/
function getMarketJobId() external view returns (bytes32);
/**
* @dev Returns the limit job id
*/
function getLimitJobId() external view returns (bytes32);
/**
* @dev Returns a specific oracle
* @param _index index of the oracle
*/
function getOracle(uint256 _index) external view returns (address);
/**
* @dev Returns all oracles
*/
function getOracles() external view returns (address[] memory);
/**
* @dev Returns collateral/gns liquidity pool info
* @param _collateralIndex index of collateral
*/
function getCollateralGnsLiquidityPool(uint8 _collateralIndex) external view returns (LiquidityPoolInfo memory);
/**
* @dev Returns collateral/usd chainlink price feed
* @param _collateralIndex index of collateral
*/
function getCollateralUsdPriceFeed(uint8 _collateralIndex) external view returns (IChainlinkFeed);
/**
* @dev Returns order data
* @param _requestId index of collateral
*/
function getPriceAggregatorOrder(bytes32 _requestId) external view returns (Order memory);
/**
* @dev Returns order data
* @param _orderId order id
*/
function getPriceAggregatorOrderAnswers(
ITradingStorage.Id calldata _orderId
) external view returns (OrderAnswer[] memory);
/**
* @dev Returns chainlink token address
*/
function getChainlinkToken() external view returns (address);
/**
* @dev Returns requestCount (used by ChainlinkClientUtils)
*/
function getRequestCount() external view returns (uint256);
/**
* @dev Returns pendingRequests mapping entry (used by ChainlinkClientUtils)
*/
function getPendingRequest(bytes32 _id) external view returns (address);
/**
* @dev Returns lookback job count
*/
function getLimitJobCount() external view returns (uint8);
/**
* @dev Returns current lookback job index
*/
function getLimitJobIndex() external view returns (uint88);
/**
* @dev Returns max market order deviation percentage (1e3 %)
*/
function getMaxMarketDeviationP() external view returns (uint24);
/**
* @dev Returns max lookback order deviation percentage (1e3 %)
*/
function getMaxLookbackDeviationP() external view returns (uint24);
/**
* @dev Returns temporary signed order answers for a pair
* @param _pairIndex pair index
*/
function getPairSignedOrderAnswersTemporary(
uint16 _pairIndex
) external view returns (IPriceAggregator.OrderAnswer[] memory);
/**
* @dev Returns the median signed answer for a pair based on temporary storage
* @param _pairIndex pair index
*/
function getPairSignedMedianTemporary(
uint16 _pairIndex
) external view returns (ITradingCallbacks.AggregatorAnswer memory);
/**
* @dev Returns signed pair indices stored temporarily (for cleanup)
*/
function getSignedPairIndicesTemporary() external view returns (uint16[] memory);
/**
* @dev Emitted when LINK/USD price feed is updated
* @param value new value
*/
event LinkUsdPriceFeedUpdated(address value);
/**
* @dev Emitted when collateral/USD price feed is updated
* @param collateralIndex collateral index
* @param value new value
*/
event CollateralUsdPriceFeedUpdated(uint8 collateralIndex, address value);
/**
* @dev Emitted when collateral/GNS Uniswap V3 pool is updated
* @param collateralIndex collateral index
* @param newValue new value
*/
event CollateralGnsLiquidityPoolUpdated(uint8 collateralIndex, LiquidityPoolInfo newValue);
/**
* @dev Emitted when TWAP interval is updated
* @param newValue new value
*/
event TwapIntervalUpdated(uint32 newValue);
/**
* @dev Emitted when minimum answers count is updated
* @param value new value
*/
event MinAnswersUpdated(uint8 value);
/**
* @dev Emitted when an oracle is added
* @param index new oracle index
* @param value value
*/
event OracleAdded(uint256 index, address value);
/**
* @dev Emitted when an oracle is replaced
* @param index oracle index
* @param oldOracle old value
* @param newOracle new value
*/
event OracleReplaced(uint256 index, address oldOracle, address newOracle);
/**
* @dev Emitted when an oracle is removed
* @param index oracle index
* @param oldOracle old value
*/
event OracleRemoved(uint256 index, address oldOracle);
/**
* @dev Emitted when market job id is updated
* @param index index
* @param jobId new value
*/
event JobIdUpdated(uint256 index, bytes32 jobId);
/**
* @dev Emitted when a chainlink request is created
* @param request link request details
*/
event LinkRequestCreated(Chainlink.Request request);
/**
* @dev Emitted when a price is requested to the oracles
* @param collateralIndex collateral index
* @param pairIndex trading pair index
* @param pendingOrder corresponding pending order
* @param positionSizeCollateral position size in collateral tokens (collateral precision)
* @param fromBlock block number from which to start fetching prices (for lookbacks)
* @param isCounterTrade whether corresponding trade is counter trade
* @param isLookback true if lookback
* @param job chainlink job id (market/lookback)
* @param linkFeePerNode link fee distributed per node (1e18 precision)
* @param nodesCount amount of nodes to fetch prices from
*/
event PriceRequested(
uint8 indexed collateralIndex,
uint256 indexed pairIndex,
ITradingStorage.PendingOrder pendingOrder,
uint256 positionSizeCollateral,
uint256 fromBlock,
bool isCounterTrade,
bool isLookback,
bytes32 job,
uint256 linkFeePerNode,
uint256 nodesCount
);
/**
* @dev Emitted when a trading callback is called from the price aggregator
* @param a aggregator answer data
* @param orderType order type
*/
event TradingCallbackExecuted(ITradingCallbacks.AggregatorAnswer a, ITradingStorage.PendingOrderType orderType);
/**
* @dev Emitted when a price is received from the oracles
* @param orderId pending order id
* @param pairIndex trading pair index
* @param request chainlink request id
* @param priceData OrderAnswer compressed into uint256
* @param isLookback true if lookback
* @param usedInMedian false if order already fulfilled
* @param minAnswersReached true if min answers count was reached
* @param minFilteredAnswersReached true if min answers reached after filtering the ones above max deviation
* @param unfilteredAnswers unfiltered oracle answers (1e10)
* @param filteredAnswers filtered oracle answers (1e10)
*/
event PriceReceived(
ITradingStorage.Id orderId,
uint16 indexed pairIndex,
bytes32 request,
uint256 priceData,
bool isLookback,
bool usedInMedian,
bool minAnswersReached,
bool minFilteredAnswersReached,
IPriceAggregator.OrderAnswer[] unfilteredAnswers,
IPriceAggregator.OrderAnswer[] filteredAnswers
);
/**
* @dev Emitted when all signed prices have been processed for a pair
* @param pairIndex trading pair index
* @param isLookback true if lookback
* @param fromBlock corresponding from block (for lookbacks)
* @param minFilteredAnswersReached true if min answers reached after filtering the ones above max deviation
* @param unfilteredAnswers unfiltered oracle answers (1e10)
* @param filteredAnswers filtered oracle answers (1e10)
*/
event SignedPricesReceived(
uint16 indexed pairIndex,
bool isLookback,
uint32 fromBlock,
bool minFilteredAnswersReached,
IPriceAggregator.OrderAnswer[] unfilteredAnswers,
IPriceAggregator.OrderAnswer[] filteredAnswers
);
/**
* @dev Emitted when LINK tokens are claimed back by gov fund
* @param amountLink amount of LINK tokens claimed back
*/
event LinkClaimedBack(uint256 amountLink);
/**
* @dev Emitted when the count of limit jobs is updated
* @param limitJobCount the count of limit jobs
*/
event LimitJobCountUpdated(uint8 limitJobCount);
/**
* @dev Emitted when max market order deviation percentage is updated
* @param maxMarketDeviationP new value (1e3, %)
*/
event MaxMarketDeviationPUpdated(uint24 maxMarketDeviationP);
/**
* @dev Emitted when max lookback order deviation percentage is updated
* @param maxLookbackDeviationP new value (1e3, %)
*/
event MaxLookbackDeviationPUpdated(uint24 maxLookbackDeviationP);
error TransferAndCallToOracleFailed();
error SourceNotOracleOfRequest();
error RequestAlreadyPending();
error OracleAlreadyListed();
error InvalidCandle();
error WrongCollateralUsdDecimals();
error InvalidPoolType();
error WrongSignaturesCount();
error WrongSignerIdOrder();
error InvalidExpiryTimestamp();
error LookbackMismatch();
error FromBlockMismatch();
error InvalidSignature();
error PairAndCurrentPriceLengthMismatch();
error PairLengthMismatchBetweenSigners();
error PairIndexMismatchBetweenSigners();
error DuplicateOrUnsortedPairIndices();
error ExpiryTooFar();
error InvalidCurrentPrice();
error MinAnswersNotReached();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IPriceImpact.sol";
import "../types/ITradingStorage.sol";
/**
* @dev Interface for GNSPriceImpact facet (inherits types and also contains functions, events, and custom errors)
*/
interface IPriceImpactUtils is IPriceImpact {
/**
* @dev Initializes price impact facet
* @param _windowsDuration windows duration (seconds)
* @param _windowsCount windows count
*/
function initializePriceImpact(uint48 _windowsDuration, uint48 _windowsCount) external;
/**
* @dev Initializes negative pnl cumulative volume multiplier
* @param _negPnlCumulVolMultiplier new value (1e10)
*/
function initializeNegPnlCumulVolMultiplier(uint40 _negPnlCumulVolMultiplier) external;
/**
* @dev Initializes pair factors
* @param _pairIndices pair indices to initialize
* @param _protectionCloseFactors protection close factors (1e10)
* @param _protectionCloseFactorBlocks protection close factor blocks
* @param _cumulativeFactors cumulative factors (1e10)
*/
function initializePairFactors(
uint16[] calldata _pairIndices,
uint40[] calldata _protectionCloseFactors,
uint32[] calldata _protectionCloseFactorBlocks,
uint40[] calldata _cumulativeFactors
) external;
/**
* @dev Initializes depth bands mapping for price impact calculation
* @param _slot1 slot 1
* @param _slot2 slot 2
*/
function initializeDepthBandsMapping(uint256 _slot1, uint256 _slot2) external;
/**
* @dev Updates price impact windows count
* @param _newWindowsCount new windows count
*/
function setPriceImpactWindowsCount(uint48 _newWindowsCount) external;
/**
* @dev Updates price impact windows duration
* @param _newWindowsDuration new windows duration (seconds)
*/
function setPriceImpactWindowsDuration(uint48 _newWindowsDuration) external;
/**
* @dev Updates negative pnl cumulative volume multiplier
* @param _negPnlCumulVolMultiplier new value (1e10)
*/
function setNegPnlCumulVolMultiplier(uint40 _negPnlCumulVolMultiplier) external;
/**
* @dev Whitelists/unwhitelists traders from protection close factor
* @param _traders traders addresses
* @param _whitelisted values
*/
function setProtectionCloseFactorWhitelist(address[] calldata _traders, bool[] calldata _whitelisted) external;
/**
* @dev Updates traders price impact settings for pairs
* @param _traders traders addresses
* @param _pairIndices pair indices
* @param _cumulVolPriceImpactMultipliers cumulative volume price impact multipliers (1e3)
* @param _fixedSpreadPs fixed spreads (1e3 %)
*/
function setUserPriceImpact(
address[] calldata _traders,
uint16[] calldata _pairIndices,
uint16[] calldata _cumulVolPriceImpactMultipliers,
uint16[] calldata _fixedSpreadPs
) external;
/**
* @dev Sets encoded depth bands for price impact calculation per trading pair.
* See `PairDepthBands` struct for layout.
* @param _indices Array of pair indices.
* @param _depthBands Array of PairDepthBands structs with above/below slot encoding.
*/
function setPairDepthBands(
uint256[] calldata _indices,
IPriceImpact.PairDepthBands[] calldata _depthBands
) external;
/**
* @dev Sets the global depth band offset mapping.
* See `DepthBandsMapping` struct for layout.
* @param _slot1 Encoded offsets for bands 0–13 (first 32 bits unused).
* @param _slot2 Encoded offsets for bands 14–29.
*/
function setDepthBandsMapping(uint256 _slot1, uint256 _slot2) external;
/**
* @dev Updates pairs 1% depths above and below (for skew price impact)
* @dev Has to be manually updated from time to time based on desired speed of arbitrage (not too often to not cause imbalances)
* @param _collateralIndices indices of collaterals
* @param _pairIndices indices of pairs
* @param _depths depths in tokens (1e18, 0 = no price impact)
*/
function setPairSkewDepths(
uint8[] calldata _collateralIndices,
uint16[] calldata _pairIndices,
uint256[] calldata _depths
) external;
/**
* @dev Sets protection close factors for pairs
* @param _pairIndices pair indices to update
* @param _protectionCloseFactors new protection close factors (1e10)
*/
function setProtectionCloseFactors(
uint16[] calldata _pairIndices,
uint40[] calldata _protectionCloseFactors
) external;
/**
* @dev Sets protection close factor blocks duration for pairs
* @param _pairIndices pair indices to update
* @param _protectionCloseFactorBlocks new protection close factor blocks
*/
function setProtectionCloseFactorBlocks(
uint16[] calldata _pairIndices,
uint32[] calldata _protectionCloseFactorBlocks
) external;
/**
* @dev Sets cumulative factors for pairs
* @param _pairIndices pair indices to update
* @param _cumulativeFactors new cumulative factors (1e10)
*/
function setCumulativeFactors(uint16[] calldata _pairIndices, uint40[] calldata _cumulativeFactors) external;
/**
* @dev Sets whether pairs are exempt from price impact on open
* @param _pairIndices pair indices to update
* @param _exemptOnOpen new values
*/
function setExemptOnOpen(uint16[] calldata _pairIndices, bool[] calldata _exemptOnOpen) external;
/**
* @dev Sets whether pairs are exempt from price impact on close once protection close factor has expired
* @param _pairIndices pair indices to update
* @param _exemptAfterProtectionCloseFactor new values
*/
function setExemptAfterProtectionCloseFactor(
uint16[] calldata _pairIndices,
bool[] calldata _exemptAfterProtectionCloseFactor
) external;
/**
* @dev Adds open interest to current window
* @param _trader trader address
* @param _index trade index
* @param _oiDeltaCollateral open interest to add (collateral precision)
* @param _open whether it corresponds to opening or closing a trade
* @param _isPnlPositive whether it corresponds to a positive pnl trade (only relevant when _open = false)
*/
function addPriceImpactOpenInterest(
address _trader,
uint32 _index,
uint256 _oiDeltaCollateral,
bool _open,
bool _isPnlPositive
) external;
/**
* @dev Updates pair stored open interest after v10
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
* @param _oiDeltaCollateral open interest delta in collateral tokens (collateral precision)
* @param _oiDeltaToken open interest delta in tokens (1e18)
* @param _open whether it corresponds to opening or closing a trade
* @param _long true for long, false for short
*/
function updatePairOiAfterV10(
uint8 _collateralIndex,
uint16 _pairIndex,
uint256 _oiDeltaCollateral,
uint256 _oiDeltaToken,
bool _open,
bool _long
) external;
/**
* @dev Returns active open interest used in price impact calculation for a pair and side (long/short)
* @param _pairIndex index of pair
* @param _long true for long, false for short
*/
function getPriceImpactOi(uint256 _pairIndex, bool _long) external view returns (uint256 activeOi);
/**
* @dev Returns cumulative volume price impact % (1e10 precision) for a trade
* @param _trader trader address (to check if whitelisted from protection close factor)
* @param _pairIndex index of pair
* @param _long true for long, false for short
* @param _tradeOpenInterestUsd open interest of trade in USD (1e18 precision)
* @param _isPnlPositive true if positive pnl, false if negative pnl (only relevant when _open = false)
* @param _open true on open, false on close
* @param _lastPosIncreaseBlock block when trade position size was last increased (only relevant when _open = false)
*/
function getTradeCumulVolPriceImpactP(
address _trader,
uint16 _pairIndex,
bool _long,
uint256 _tradeOpenInterestUsd,
bool _isPnlPositive,
bool _open,
uint256 _lastPosIncreaseBlock
) external view returns (int256 priceImpactP);
/**
* @dev Returns skew price impact % (1e10 precision) for a trade
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
* @param _long true for long, false for short
* @param _positionSizeToken open interest of trade in tokens (1e18)
* @param _open true on open, false on close
*/
function getTradeSkewPriceImpactP(
uint8 _collateralIndex,
uint16 _pairIndex,
bool _long,
uint256 _positionSizeToken,
bool _open
) external view returns (int256 priceImpactP);
/**
* @dev Returns the encoded depth bands for a specific pair.
* See `PairDepthBands` struct for layout.
* @param _pairIndex Pair index.
* @return Encoded PairDepthBands struct (above and below).
*/
function getPairDepthBands(uint256 _pairIndex) external view returns (IPriceImpact.PairDepthBands memory);
/**
* @dev Returns encoded depth bands for multiple trading pairs.
* See `PairDepthBands` struct for layout.
* @param _indices Array of pair indices.
* @return Array of PairDepthBands structs.
*/
function getPairDepthBandsArray(
uint256[] calldata _indices
) external view returns (IPriceImpact.PairDepthBands[] memory);
/**
* @dev Returns the decoded depth bands for a specific pair.
* @param _pairIndex Pair index.
* @return totalDepthAboveUsd total depth above in USD (1e18).
* @return totalDepthBelowUsd total depth below in USD (1e18).
* @return bandsAbove above bands liquidity percentages bps (1e2 %).
* @return bandsBelow below bands liquidity percentages bps (1e2 %).
*/
function getPairDepthBandsDecoded(
uint256 _pairIndex
)
external
view
returns (
uint256 totalDepthAboveUsd,
uint256 totalDepthBelowUsd,
uint16[] memory bandsAbove,
uint16[] memory bandsBelow
);
/**
* @dev Returns the decoded depth bands for multiple pairs.
* @param _indices Array of pair indices.
* @return totalDepthAboveUsd Array of total depth above in USD (1e18).
* @return totalDepthBelowUsd Array of total depth below in USD (1e18).
* @return bandsAbove Array of above bands liquidity percentages bps (1e2 %).
* @return bandsBelow Array of below bands liquidity percentages bps (1e2 %).
*/
function getPairDepthBandsDecodedArray(
uint256[] calldata _indices
)
external
view
returns (
uint256[] memory totalDepthAboveUsd,
uint256[] memory totalDepthBelowUsd,
uint16[][] memory bandsAbove,
uint16[][] memory bandsBelow
);
/**
* @dev Returns the global depth band offset mapping.
* See `DepthBandsMapping` struct for layout.
* @return slot1 Encoded offsets for bands 0–13 (first 32 bits unused).
* @return slot2 Encoded offsets for bands 14–29.
*/
function getDepthBandsMapping() external view returns (uint256 slot1, uint256 slot2);
/**
* @dev Returns the decoded global depth band offset mapping.
* @return bands Array of offsets in parts per million (1e4 %).
*/
function getDepthBandsMappingDecoded() external view returns (uint16[] memory bands);
/**
* @dev Returns a pair's depths above and below the price (for skew price impact)
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
*/
function getPairSkewDepth(uint8 _collateralIndex, uint16 _pairIndex) external view returns (uint256);
/**
* @dev Returns current price impact windows settings
*/
function getOiWindowsSettings() external view returns (OiWindowsSettings memory);
/**
* @dev Returns OI window details (long/short OI)
* @param _windowsDuration windows duration (seconds)
* @param _pairIndex index of pair
* @param _windowId id of window
*/
function getOiWindow(
uint48 _windowsDuration,
uint256 _pairIndex,
uint256 _windowId
) external view returns (PairOi memory);
/**
* @dev Returns multiple OI windows details (long/short OI)
* @param _windowsDuration windows duration (seconds)
* @param _pairIndex index of pair
* @param _windowIds ids of windows
*/
function getOiWindows(
uint48 _windowsDuration,
uint256 _pairIndex,
uint256[] calldata _windowIds
) external view returns (PairOi[] memory);
/**
* @dev Returns depths above and below the price for multiple pairs for skew price impact (tokens, 1e18)
* @param _collateralIndices indices of collaterals
* @param _pairIndices indices of pairs
*/
function getPairSkewDepths(
uint8[] calldata _collateralIndices,
uint16[] calldata _pairIndices
) external view returns (uint256[] memory);
/**
* @dev Returns factors for a set of pairs (1e10)
* @param _indices indices of pairs
*/
function getPairFactors(uint256[] calldata _indices) external view returns (IPriceImpact.PairFactors[] memory);
/**
* @dev Returns negative pnl cumulative volume multiplier
*/
function getNegPnlCumulVolMultiplier() external view returns (uint48);
/**
* @dev Returns whether a trader is whitelisted from protection close factor
* @param _trader trader address
*/
function getProtectionCloseFactorWhitelist(address _trader) external view returns (bool);
/**
* @dev Returns a trader's price impact settings on a particular pair
* @param _trader trader address
* @param _pairIndex pair index
*/
function getUserPriceImpact(
address _trader,
uint256 _pairIndex
) external view returns (IPriceImpact.UserPriceImpact memory);
/**
* @dev Returns a trader's price impact settings for an array of pairs
* @param _trader trader address
* @param _pairIndices array of pair indices
*/
function getUserPriceImpactArray(
address _trader,
uint256[] calldata _pairIndices
) external view returns (IPriceImpact.UserPriceImpact[] memory);
/**
* @dev Returns a pair's open interest in collateral tokens after v10 (collateral precision)
* @param _collateralIndex collateral index
* @param _pairIndex pair index
*/
function getPairOiAfterV10Collateral(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (IPriceImpact.PairOiCollateral memory);
/**
* @dev Returns multiple pairs' open interests in collateral tokens after v10 (collateral precision)
* @param _collateralIndex collateral indices
* @param _pairIndex pair indices
*/
function getPairOisAfterV10Collateral(
uint8[] memory _collateralIndex,
uint16[] memory _pairIndex
) external view returns (IPriceImpact.PairOiCollateral[] memory);
/**
* @dev Returns a pair's open interest in tokens after v10 (1e18)
* @param _collateralIndex collateral index
* @param _pairIndex pair index
*/
function getPairOiAfterV10Token(
uint8 _collateralIndex,
uint16 _pairIndex
) external view returns (IPriceImpact.PairOiToken memory);
/**
* @dev Returns multiple pairs' open interests in tokens after v10 (collateral precision)
* @param _collateralIndex collateral indices
* @param _pairIndex pair indices
*/
function getPairOisAfterV10Token(
uint8[] memory _collateralIndex,
uint16[] memory _pairIndex
) external view returns (IPriceImpact.PairOiToken[] memory);
/**
* @dev Triggered when OiWindowsSettings is initialized (once)
* @param windowsDuration duration of each window (seconds)
* @param windowsCount number of windows
*/
event OiWindowsSettingsInitialized(uint48 indexed windowsDuration, uint48 indexed windowsCount);
/**
* @dev Triggered when OiWindowsSettings.windowsCount is updated
* @param windowsCount new number of windows
*/
event PriceImpactWindowsCountUpdated(uint48 indexed windowsCount);
/**
* @dev Triggered when OiWindowsSettings.windowsDuration is updated
* @param windowsDuration new duration of each window (seconds)
*/
event PriceImpactWindowsDurationUpdated(uint48 indexed windowsDuration);
/**
* @dev Triggered when negPnlCumulVolMultiplier is updated
* @param negPnlCumulVolMultiplier new value (1e10)
*/
event NegPnlCumulVolMultiplierUpdated(uint40 indexed negPnlCumulVolMultiplier);
/**
* @dev Triggered when a trader is whitelisted/unwhitelisted from protection close factor
* @param trader trader address
* @param whitelisted true if whitelisted, false if unwhitelisted
*/
event ProtectionCloseFactorWhitelistUpdated(address trader, bool whitelisted);
/**
* @dev Triggered when a trader's price impact data is updated
* @param trader trader address
* @param pairIndex pair index
* @param cumulVolPriceImpactMultiplier cumulative volume price impact multiplier (1e3)
* @param fixedSpreadP fixed spread (1e3 %)
*/
event UserPriceImpactUpdated(
address indexed trader,
uint16 indexed pairIndex,
uint16 cumulVolPriceImpactMultiplier,
uint16 fixedSpreadP
);
/**
* @dev Triggered when a pair's protection close factor is updated
* @param pairIndex index of the pair
* @param protectionCloseFactor new protection close factor (1e10)
*/
event ProtectionCloseFactorUpdated(uint256 indexed pairIndex, uint40 protectionCloseFactor);
/**
* @dev Triggered when a pair's protection close factor duration is updated
* @param pairIndex index of the pair
* @param protectionCloseFactorBlocks new protection close factor blocks
*/
event ProtectionCloseFactorBlocksUpdated(uint256 indexed pairIndex, uint32 protectionCloseFactorBlocks);
/**
* @dev Triggered when a pair's cumulative factor is updated
* @param pairIndex index of the pair
* @param cumulativeFactor new cumulative factor (1e10)
*/
event CumulativeFactorUpdated(uint256 indexed pairIndex, uint40 cumulativeFactor);
/**
* @dev Triggered when a pair's exemptOnOpen value is updated
* @param pairIndex index of the pair
* @param exemptOnOpen whether the pair is exempt of price impact on open
*/
event ExemptOnOpenUpdated(uint256 indexed pairIndex, bool exemptOnOpen);
/**
* @dev Triggered when a pair's exemptAfterProtectionCloseFactor value is updated
* @param pairIndex index of the pair
* @param exemptAfterProtectionCloseFactor whether the pair is exempt of price impact on close once protection close factor has expired
*/
event ExemptAfterProtectionCloseFactorUpdated(uint256 indexed pairIndex, bool exemptAfterProtectionCloseFactor);
/**
* @dev Triggered when OI is added to a window.
* @param oiWindowUpdate OI window update details (windowsDuration, pairIndex, windowId, etc.)
*/
event PriceImpactOpenInterestAdded(IPriceImpact.OiWindowUpdate oiWindowUpdate);
/**
* @dev Triggered when a pair's OI after v10 is updated.
* @param collateralIndex index of collateral
* @param pairIndex index of pair
* @param oiDeltaCollateral open interest delta in collateral tokens (collateral precision)
* @param oiDeltaToken open interest delta in tokens (1e18)
* @param open whether it corresponds to opening or closing a trade
* @param long true for long, false for short
* @param newOiCollateral new OI collateral after v10 (collateral precision)
* @param newOiToken new OI token after v10 (1e18)
*/
event PairOiAfterV10Updated(
uint8 indexed collateralIndex,
uint16 indexed pairIndex,
uint256 oiDeltaCollateral,
uint256 oiDeltaToken,
bool open,
bool long,
IPriceImpact.PairOiCollateral newOiCollateral,
IPriceImpact.PairOiToken newOiToken
);
/**
* @dev Triggered when multiple pairs' OI are transferred to a new window (when updating windows duration).
* @param pairsCount number of pairs
* @param prevCurrentWindowId previous current window ID corresponding to previous window duration
* @param prevEarliestWindowId previous earliest window ID corresponding to previous window duration
* @param newCurrentWindowId new current window ID corresponding to new window duration
*/
event PriceImpactOiTransferredPairs(
uint256 pairsCount,
uint256 prevCurrentWindowId,
uint256 prevEarliestWindowId,
uint256 newCurrentWindowId
);
/**
* @dev Triggered when a pair's OI is transferred to a new window.
* @param pairIndex index of the pair
* @param totalPairOi total USD long/short OI of the pair (1e18 precision)
*/
event PriceImpactOiTransferredPair(uint256 indexed pairIndex, IPriceImpact.PairOi totalPairOi);
/**
* @dev Triggered when a pair's 1% depth is updated (for cumulative volume price impact).
* @param pairIndex index of the pair
* @param valueAboveUsd new USD depth above the price
* @param valueBelowUsd new USD depth below the price
*/
event OnePercentDepthUpdated(uint256 indexed pairIndex, uint128 valueAboveUsd, uint128 valueBelowUsd);
/**
* @dev Triggered when a pair's 1% depth is updated (for skew price impact).
* @param pairIndex index of the pair
* @param newValue new depth above/below the price (1e18, token)
*/
event OnePercentSkewDepthUpdated(uint8 indexed collateralIndex, uint16 indexed pairIndex, uint256 newValue);
/**
* @dev Triggered when a pair's depth bands are updated.
* @param pairIndex index of the pair
* @param aboveSlot1 first slot for above bands (totalDepthUsd + 14 bands)
* @param aboveSlot2 second slot for above bands (16 bands)
* @param belowSlot1 first slot for below bands (totalDepthUsd + 14 bands)
* @param belowSlot2 second slot for below bands (16 bands)
*/
event PairDepthBandsUpdated(
uint256 indexed pairIndex,
uint256 aboveSlot1,
uint256 aboveSlot2,
uint256 belowSlot1,
uint256 belowSlot2
);
/**
* @dev Emitted when depth bands mapping is updated
* @param slot1 Slot 1 data containing the first 14 bands offsets
* @param slot2 Slot 2 data containing the last 16 bands offsets
*/
event DepthBandsMappingUpdated(uint256 slot1, uint256 slot2);
error WrongWindowsDuration();
error WrongWindowsCount();
error WrongDepthBandsOrder();
error DepthBandsAboveMax();
error DepthBandsIncomplete();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IReferrals.sol";
/**
* @dev Interface for GNSReferrals facet (inherits types and also contains functions, events, and custom errors)
*/
interface IReferralsUtils is IReferrals {
/**
*
* @param _allyFeeP % of total referral fee going to ally
* @param _startReferrerFeeP initial % of total referral fee earned when zero volume referred
* @param _targetVolumeUsd usd opening volume to refer to reach 100% of referral fee
*/
function initializeReferrals(uint256 _allyFeeP, uint256 _startReferrerFeeP, uint256 _targetVolumeUsd) external;
/**
* @dev Updates allyFeeP
* @param _value new ally fee %
*/
function updateAllyFeeP(uint256 _value) external;
/**
* @dev Updates startReferrerFeeP
* @param _value new start referrer fee %
*/
function updateStartReferrerFeeP(uint256 _value) external;
/**
* @dev Updates targetVolumeUsd
* @param _value new target volume in usd
*/
function updateReferralsTargetVolumeUsd(uint256 _value) external;
/**
* @dev Whitelists ally addresses
* @param _allies array of ally addresses
*/
function whitelistAllies(address[] calldata _allies) external;
/**
* @dev Unwhitelists ally addresses
* @param _allies array of ally addresses
*/
function unwhitelistAllies(address[] calldata _allies) external;
/**
* @dev Whitelists referrer addresses
* @param _referrers array of referrer addresses
* @param _allies array of corresponding ally addresses
*/
function whitelistReferrers(address[] calldata _referrers, address[] calldata _allies) external;
/**
* @dev Unwhitelists referrer addresses
* @param _referrers array of referrer addresses
*/
function unwhitelistReferrers(address[] calldata _referrers) external;
/**
* @dev Overrides referral fee % share of total trading fee for referrers
* @param _referrers array of referrer addresses
* @param _referralFeeP array of referral fee % (1e3)
*/
function overrideReferralFeeP(address[] calldata _referrers, uint24[] calldata _referralFeeP) external;
/**
* @dev Overrides ally fee % share of total trading fee for allies
* @param _allies array of ally addresses
* @param _allyFeeP array of ally fee % (1e3)
*/
function overrideAllyFeeP(address[] calldata _allies, uint24[] calldata _allyFeeP) external;
/**
* @dev Registers potential referrer for trader (only works if trader wasn't referred yet by someone else)
* @param _trader trader address
* @param _referral referrer address
*/
function registerPotentialReferrer(address _trader, address _referral) external;
/**
* @dev Distributes ally and referrer rewards
* @param _trader trader address
* @param _volumeUsd trading volume in usd (1e18 precision)
* @param _referrerFeeUsd referrer fee in USD (1e18 precision)
* @param _gnsPriceUsd token price in usd (1e10 precision)
*/
function distributeReferralReward(
address _trader,
uint256 _volumeUsd, // 1e18
uint256 _referrerFeeUsd,
uint256 _gnsPriceUsd // 1e10
) external;
/**
* @dev Claims pending GNS ally rewards of caller
*/
function claimAllyRewards() external;
/**
* @dev Claims pending GNS referrer rewards of caller
*/
function claimReferrerRewards() external;
/**
* @dev Returns referrer fee % progress towards earning 100% based on his volume referred (1e10)
* @param _referrer referrer address
*/
function getReferrerFeeProgressP(address _referrer) external view returns (uint256);
/**
* @dev Returns last referrer of trader (whether referrer active or not)
* @param _trader address of trader
*/
function getTraderLastReferrer(address _trader) external view returns (address);
/**
* @dev Returns active referrer of trader
* @param _trader address of trader
*/
function getTraderActiveReferrer(address _trader) external view returns (address);
/**
* @dev Returns referrers referred by ally
* @param _ally address of ally
*/
function getReferrersReferred(address _ally) external view returns (address[] memory);
/**
* @dev Returns traders referred by referrer
* @param _referrer address of referrer
*/
function getTradersReferred(address _referrer) external view returns (address[] memory);
/**
* @dev Returns ally fee % of total referral fee
*/
function getReferralsAllyFeeP() external view returns (uint256);
/**
* @dev Returns start referrer fee % of total referral fee when zero volume was referred
*/
function getReferralsStartReferrerFeeP() external view returns (uint256);
/**
* @dev Returns target volume in usd to reach 100% of referral fee
*/
function getReferralsTargetVolumeUsd() external view returns (uint256);
/**
* @dev Returns ally details
* @param _ally address of ally
*/
function getAllyDetails(address _ally) external view returns (AllyDetails memory);
/**
* @dev Returns referrer details
* @param _referrer address of referrer
*/
function getReferrerDetails(address _referrer) external view returns (ReferrerDetails memory);
/**
* @dev Returns referral settings overrides
* @param _address address of referrer/ally
*/
function getReferralSettingsOverrides(address _address) external view returns (ReferralSettingsOverrides memory);
/**
* @dev Emitted when allyFeeP is updated
* @param value new ally fee %
*/
event UpdatedAllyFeeP(uint256 value);
/**
* @dev Emitted when startReferrerFeeP is updated
* @param value new start referrer fee %
*/
event UpdatedStartReferrerFeeP(uint256 value);
/**
* @dev Emitted when openFeeP is updated
* @param value new open fee %
*/
event UpdatedOpenFeeP(uint256 value);
/**
* @dev Emitted when targetVolumeUsd is updated
* @param value new target volume in usd
*/
event UpdatedTargetVolumeUsd(uint256 value);
/**
* @dev Emitted when an ally is whitelisted
* @param ally ally address
*/
event AllyWhitelisted(address indexed ally);
/**
* @dev Emitted when an ally is unwhitelisted
* @param ally ally address
*/
event AllyUnwhitelisted(address indexed ally);
/**
* @dev Emitted when a referrer is whitelisted
* @param referrer referrer address
* @param ally ally address
*/
event ReferrerWhitelisted(address indexed referrer, address indexed ally);
/**
* @dev Emitted when a referrer is unwhitelisted
* @param referrer referrer address
*/
event ReferrerUnwhitelisted(address indexed referrer);
/**
* @dev Emitted when a trader has a new active referrer
*/
event ReferrerRegistered(address indexed trader, address indexed referrer);
/**
* @dev Emitted when ally rewards are distributed for a trade
* @param ally address of ally
* @param trader address of trader
* @param volumeUsd trade volume in usd (1e18 precision)
* @param amountGns amount of GNS reward (1e18 precision)
* @param amountValueUsd USD value of GNS reward (1e18 precision)
*/
event AllyRewardDistributed(
address indexed ally,
address indexed trader,
uint256 volumeUsd,
uint256 amountGns,
uint256 amountValueUsd
);
/**
* @dev Emitted when referrer rewards are distributed for a trade
* @param referrer address of referrer
* @param trader address of trader
* @param volumeUsd trade volume in usd (1e18 precision)
* @param amountGns amount of GNS reward (1e18 precision)
* @param amountValueUsd USD value of GNS reward (1e18 precision)
*/
event ReferrerRewardDistributed(
address indexed referrer,
address indexed trader,
uint256 volumeUsd,
uint256 amountGns,
uint256 amountValueUsd
);
/**
* @dev Emitted when an ally claims his pending rewards
* @param ally address of ally
* @param amountGns GNS pending rewards amount
*/
event AllyRewardsClaimed(address indexed ally, uint256 amountGns);
/**
* @dev Emitted when a referrer claims his pending rewards
* @param referrer address of referrer
* @param amountGns GNS pending rewards amount
*/
event ReferrerRewardsClaimed(address indexed referrer, uint256 amountGns);
/**
* @dev Emitted when a referrer fee % is overridden
* @param referrer referrer address
* @param referralFeeP new referral fee % (1e3)
*/
event OverrodeReferralFeeP(address indexed referrer, uint24 referralFeeP);
/**
* @dev Emitted when an ally fee % is overridden
* @param ally ally address
* @param allyFeeP new ally fee % (1e3)
*/
event OverrodeAllyFeeP(address indexed ally, uint24 allyFeeP);
error NoPendingRewards();
error AlreadyActive();
error AlreadyInactive();
error AllyNotActive();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/ITradingCallbacks.sol";
import "../libraries/IUpdateLeverageUtils.sol";
import "../libraries/IUpdatePositionSizeUtils.sol";
import "../libraries/ITradingCommonUtils.sol";
/**
* @dev Interface for GNSTradingCallbacks facet (inherits types and also contains functions, events, and custom errors)
*/
interface ITradingCallbacksUtils is
ITradingCallbacks,
IUpdateLeverageUtils,
IUpdatePositionSizeUtils,
ITradingCommonUtils
{
/**
*
* @param _vaultClosingFeeP the % of closing fee going to vault
*/
function initializeCallbacks(uint8 _vaultClosingFeeP) external;
/**
* @dev Initialize the treasury address
* @param _treasury the treasury address
*/
function initializeTreasuryAddress(address _treasury) external;
/**
* @dev Update the % of closing fee going to vault
* @param _valueP the % of closing fee going to vault
*/
function updateVaultClosingFeeP(uint8 _valueP) external;
/**
* @dev Updates the treasury address
* @param _treasury the new treasury address
*/
function updateTreasuryAddress(address _treasury) external;
/**
* @dev Claim the pending gov fees for all collaterals
*/
function claimPendingGovFees() external;
/**
* @dev Executes a pending open trade market order
* @param _a the price aggregator answer (order id, price, etc.)
*/
function openTradeMarketCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes a pending close trade market order
* @param _a the price aggregator answer (order id, price, etc.)
*/
function closeTradeMarketCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes a pending open trigger order (for limit/stop orders)
* @param _a the price aggregator answer (order id, price, etc.)
*/
function executeTriggerOpenOrderCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes open trigger directly (for limit/stop orders)
* @param _a the price aggregator answer (order id, price, etc.)
* @param _trade the trade object to execute the trigger for
* @param _orderType the pending order type
* @param _initiator the address that initiated the trigger execution
*/
function executeTriggerOpenOrderCallbackDirect(
ITradingCallbacks.AggregatorAnswer memory _a,
ITradingStorage.Trade memory _trade,
ITradingStorage.PendingOrderType _orderType,
address _initiator
) external;
/**
* @dev Executes a pending close trigger order (for tp/sl/liq orders)
* @param _a the price aggregator answer (order id, price, etc.)
*/
function executeTriggerCloseOrderCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes close trigger directly (for tp/sl/liq orders)
* @param _a the price aggregator answer (order id, price, etc.)
* @param _trade the trade object to execute the trigger for
* @param _orderType the pending order type
* @param _initiator the address that initiated the trigger execution
*/
function executeTriggerCloseOrderCallbackDirect(
ITradingCallbacks.AggregatorAnswer memory _a,
ITradingStorage.Trade memory _trade,
ITradingStorage.PendingOrderType _orderType,
address _initiator
) external;
/**
* @dev Executes a pending update leverage order
* @param _a the price aggregator answer (order id, price, etc.)
*/
function updateLeverageCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes a pending increase position size market order
* @param _a the price aggregator answer (order id, price, etc.)
*/
function increasePositionSizeMarketCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes a pending decrease position size market order
* @param _a the price aggregator answer (order id, price, etc.)
*/
function decreasePositionSizeMarketCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes a pending pnl withdrawal
* @param _a the price aggregator answer (order id, price, etc.)
*/
function pnlWithdrawalCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes a pending holding fees realization
* @param _a the price aggregator answer (order id, price, etc.)
*/
function manualHoldingFeesRealizationCallback(AggregatorAnswer memory _a) external;
/**
* @dev Executes a pending negative pnl realization
* @param _a the price aggregator answer (order id, price, etc.)
*/
function manualNegativePnlRealizationCallback(AggregatorAnswer memory _a) external;
/**
* @dev Returns the current vaultClosingFeeP value (%)
*/
function getVaultClosingFeeP() external view returns (uint8);
/**
* @dev Returns the current pending gov fees for a collateral index (collateral precision)
*/
function getPendingGovFeesCollateral(uint8 _collateralIndex) external view returns (uint256);
/**
* @dev Makes open trigger (STOP/LIMIT) checks like slippage, price impact, missed targets and returns cancellation reason if any
* @param _tradeId the id of the trade
* @param _orderType the pending order type
* @param _open the open value of the candle to check (1e10)
* @param _high the high value of the candle to check (1e10)
* @param _low the low value of the candle to check (1e10)
* @param _currentPairPrice the current pair price (1e10)
*/
function validateTriggerOpenOrderCallback(
ITradingStorage.Id memory _tradeId,
ITradingStorage.PendingOrderType _orderType,
uint64 _open,
uint64 _high,
uint64 _low,
uint64 _currentPairPrice
) external view returns (ITradingStorage.Trade memory t, ITradingCallbacks.Values memory v);
/**
* @dev Makes close trigger (SL/TP/LIQ) checks like slippage and price impact and returns cancellation reason if any
* @param _tradeId the id of the trade
* @param _orderType the pending order type
* @param _open the open value of the candle to check (1e10)
* @param _high the high value of the candle to check (1e10)
* @param _low the low value of the candle to check (1e10)
* @param _currentPairPrice the current pair price (1e10)
*/
function validateTriggerCloseOrderCallback(
ITradingStorage.Id memory _tradeId,
ITradingStorage.PendingOrderType _orderType,
uint64 _open,
uint64 _high,
uint64 _low,
uint64 _currentPairPrice
) external view returns (ITradingStorage.Trade memory t, ITradingCallbacks.Values memory v);
/**
* @dev Emitted when vaultClosingFeeP is updated
* @param valueP the % of closing fee going to vault
*/
event VaultClosingFeePUpdated(uint8 valueP);
/**
* @dev Emitted when gov fees are claimed for a collateral
* @param collateralIndex the collateral index
* @param amountCollateral the amount of fees claimed (collateral precision)
*/
event PendingGovFeesClaimed(uint8 collateralIndex, uint256 amountCollateral);
/**
* @dev Emitted when a market order is executed (open/close)
* @param orderId the id of the corresponding pending market order
* @param user trade user
* @param index trade index
* @param t the trade object
* @param open true for a market open order, false for a market close order
* @param oraclePrice the oracle price without spread/impact (1e10 precision)
* @param marketPrice the price at which the trade was executed (1e10 precision)
* @param liqPrice trade liquidation price (1e10 precision)
* @param priceImpact price impact details
* @param percentProfit the profit in percentage (1e10 precision)
* @param amountSentToTrader the final amount of collateral sent to the trader
* @param collateralPriceUsd the price of the collateral in USD (1e8 precision)
*/
event MarketExecuted(
ITradingStorage.Id orderId,
address indexed user,
uint32 indexed index,
ITradingStorage.Trade t,
bool open,
uint256 oraclePrice,
uint256 marketPrice,
uint256 liqPrice,
ITradingCommonUtils.TradePriceImpact priceImpact,
int256 percentProfit, // before fees
uint256 amountSentToTrader,
uint256 collateralPriceUsd // 1e8
);
/**
* @dev Emitted when a limit/stop order is executed
* @param orderId the id of the corresponding pending trigger order
* @param user trade user
* @param index trade index
* @param limitIndex limit index
* @param t the trade object
* @param triggerCaller the address that triggered the limit order
* @param orderType the type of the pending order
* @param oraclePrice the oracle price without spread/impact (1e10 precision)
* @param marketPrice the price at which the trade was executed (1e10 precision)
* @param liqPrice trade liquidation price (1e10 precision)
* @param priceImpact price impact details
* @param percentProfit the profit in percentage (1e10 precision)
* @param amountSentToTrader the final amount of collateral sent to the trader
* @param collateralPriceUsd the price of the collateral in USD (1e8 precision)
* @param exactExecution true if guaranteed execution was used
*/
event LimitExecuted(
ITradingStorage.Id orderId,
address indexed user,
uint32 indexed index,
uint32 indexed limitIndex,
ITradingStorage.Trade t,
address triggerCaller,
ITradingStorage.PendingOrderType orderType,
uint256 oraclePrice,
uint256 marketPrice,
uint256 liqPrice,
ITradingCommonUtils.TradePriceImpact priceImpact,
int256 percentProfit,
uint256 amountSentToTrader,
uint256 collateralPriceUsd, // 1e8
bool exactExecution
);
/**
* @dev Emitted when a pending market open order is canceled
* @param orderId order id of the pending market open order
* @param trader address of the trader
* @param pairIndex index of the trading pair
* @param cancelReason reason for the cancellation
* @param collateralReturned collateral returned to trader (collateral precision)
*/
event MarketOpenCanceled(
ITradingStorage.Id orderId,
address indexed trader,
uint256 indexed pairIndex,
CancelReason cancelReason,
uint256 collateralReturned
);
/**
* @dev Emitted when a pending market close order is canceled
* @param orderId order id of the pending market close order
* @param trader address of the trader
* @param pairIndex index of the trading pair
* @param index index of the trade for trader
* @param cancelReason reason for the cancellation
*/
event MarketCloseCanceled(
ITradingStorage.Id orderId,
address indexed trader,
uint256 indexed pairIndex,
uint256 indexed index,
CancelReason cancelReason
);
/**
* @dev Emitted when a pending trigger order is canceled
* @param orderId order id of the pending trigger order
* @param triggerCaller address of the trigger caller
* @param orderType type of the pending trigger order
* @param cancelReason reason for the cancellation
*/
event TriggerOrderCanceled(
ITradingStorage.Id orderId,
address indexed triggerCaller,
ITradingStorage.PendingOrderType orderType,
CancelReason cancelReason
);
/**
* @dev Emitted when a trade's holding fees are manually realized
* @param orderId corresponding order id
* @param collateralIndex collateral index
* @param trader address of the trader
* @param index index of the trade
* @param currentPairPrice current pair price (1e10 precision)
*/
event TradeHoldingFeesManuallyRealized(
ITradingStorage.Id orderId,
uint8 indexed collateralIndex,
address indexed trader,
uint32 indexed index,
uint256 currentPairPrice
);
/**
* @dev Emitted when positive pnl is claimed on an open trade
* @param orderId corresponding order id
* @param collateralIndex collateral index
* @param trader address of the trader
* @param index index of the trade
* @param priceImpact closing price impact details
* @param pnlPercent raw trade pnl percent (1e10 precision)
* @param withdrawablePositivePnlCollateral maximum withdrawable positive pnl in collateral tokens (collateral precision)
* @param currentPairPrice current pair price (1e10 precision)
* @param pnlInputCollateral input for amount of positive pnl to withdraw (collateral precision)
* @param pnlWithdrawnCollateral final positive pnl withdrawn in collateral tokens (collateral precision)
*/
event TradePositivePnlWithdrawn(
ITradingStorage.Id orderId,
uint8 indexed collateralIndex,
address indexed trader,
uint32 indexed index,
ITradingCommonUtils.TradePriceImpact priceImpact,
int256 pnlPercent,
int256 withdrawablePositivePnlCollateral,
uint256 currentPairPrice,
uint256 pnlInputCollateral,
uint256 pnlWithdrawnCollateral
);
/**
* @dev Emitted when a trade's negative pnl is manually realized
* @param orderId corresponding order id
* @param collateralIndex collateral index
* @param trader address of the trader
* @param index index of the trade
* @param negativePnlCollateral current negative pnl in collateral tokens (collateral precision)
* @param existingManuallyRealizedNegativePnlCollateral existing manually realized negative pnl in collateral tokens (collateral precision)
* @param newManuallyRealizedNegativePnlCollateral new manually realized negative pnl in collateral tokens (collateral precision)
* @param currentPairPrice current pair price (1e10 precision)
*/
event TradeNegativePnlManuallyRealized(
ITradingStorage.Id orderId,
uint8 indexed collateralIndex,
address indexed trader,
uint32 indexed index,
uint256 negativePnlCollateral,
uint256 existingManuallyRealizedNegativePnlCollateral,
uint256 newManuallyRealizedNegativePnlCollateral,
uint256 currentPairPrice
);
/**
* @dev Emitted when excess counter trade collateral is returned to trader
* @param orderId corresponding order id
* @param collateralIndex collateral index
* @param trader trader address
* @param collateralReturned collateral amount returned to trader (collateral precision)
*/
event CounterTradeCollateralReturned(
ITradingStorage.Id orderId,
uint8 indexed collateralIndex,
address indexed trader,
uint256 collateralReturned
);
error PendingOrderNotOpen();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/ITradingStorage.sol";
/**
* @dev Interface for TradingCommonUtils library
*/
interface ITradingCommonUtils {
struct TradePriceImpactInput {
ITradingStorage.Trade trade;
uint256 oraclePrice; // 1e10
uint256 positionSizeCollateral; // collateral precision
uint64 currentPairPrice; // 1e10
bool useCumulativeVolPriceImpact;
}
struct TradePriceImpact {
uint256 positionSizeToken; // 1e18
int256 fixedSpreadP; // 1e10 (%)
int256 cumulVolPriceImpactP; // 1e10 (%)
int256 skewPriceImpactP; // 1e10 (%)
int256 totalPriceImpactP; // 1e10 (%)
uint64 priceAfterImpact; // 1e10
}
/**
* @dev Emitted when gov fee is charged
* @param trader address of the trader
* @param collateralIndex index of the collateral
* @param amountCollateral amount charged (collateral precision)
*/
event GovFeeCharged(address indexed trader, uint8 indexed collateralIndex, uint256 amountCollateral);
/**
* @dev Emitted when referral fee is charged
* @param trader address of the trader
* @param collateralIndex index of the collateral
* @param amountCollateral amount charged (collateral precision)
*/
event ReferralFeeCharged(address indexed trader, uint8 indexed collateralIndex, uint256 amountCollateral);
/**
* @dev Emitted when GNS otc fee is charged
* @param trader address of the trader
* @param collateralIndex index of the collateral
* @param amountCollateral amount charged (collateral precision)
*/
event GnsOtcFeeCharged(address indexed trader, uint8 indexed collateralIndex, uint256 amountCollateral);
/**
* @dev Emitted when trigger fee is charged
* @param trader address of the trader
* @param collateralIndex index of the collateral
* @param amountCollateral amount charged (collateral precision)
*/
event TriggerFeeCharged(address indexed trader, uint8 indexed collateralIndex, uint256 amountCollateral);
/**
* @dev Emitted when gToken fee is charged
* @param trader address of the trader
* @param collateralIndex index of the collateral
* @param amountCollateral amount charged (collateral precision)
*/
event GTokenFeeCharged(address indexed trader, uint8 indexed collateralIndex, uint256 amountCollateral);
/**
* @dev Emitted when gToken OC fee is charged
* @param trader address of the trader
* @param collateralIndex index of the collateral
* @param amountCollateral amount charged (collateral precision)
*/
event GTokenOcFeeCharged(address indexed trader, uint8 indexed collateralIndex, uint256 amountCollateral);
/**
* @dev Emitted when a trade's fees are processed
* @param collateralIndex index of the collateral
* @param trader address of the trader
* @param positionSizeCollateral position size (collateral precision)
* @param orderType type of the order
* @param totalFeesCollateral amount of fees charged (collateral precision)
*/
event FeesProcessed(
uint8 indexed collateralIndex,
address indexed trader,
uint256 positionSizeCollateral,
ITradingStorage.PendingOrderType orderType,
uint256 totalFeesCollateral
);
/**
* @dev Emitted when a trade's value is transferred
* @param collateralIndex index of the collateral
* @param trader address of the trader
* @param index index of the trade
* @param collateralSentToTrader amount of collateral sent to the trader (collateral precision)
* @param availableCollateralInDiamond amount of available collateral in the diamond (collateral precision)
*/
event TradeValueTransferred(
uint8 indexed collateralIndex,
address indexed trader,
uint32 indexed index,
int256 collateralSentToTrader,
int256 availableCollateralInDiamond
);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/ITradingInteractions.sol";
import "../types/ITradingStorage.sol";
import "../types/IPriceAggregator.sol";
import "../libraries/IUpdateLeverageUtils.sol";
import "../libraries/IUpdatePositionSizeUtils.sol";
/**
* @dev Interface for GNSTradingInteractions facet (inherits types and also contains functions, events, and custom errors)
*/
interface ITradingInteractionsUtils is ITradingInteractions, IUpdateLeverageUtils, IUpdatePositionSizeUtils {
/**
* @dev Initializes the trading facet
* @param _marketOrdersTimeoutBlocks The number of blocks after which a market order is considered timed out
*/
function initializeTrading(uint16 _marketOrdersTimeoutBlocks, address[] memory _usersByPassTriggerLink) external;
/**
* @dev Updates marketOrdersTimeoutBlocks
* @param _valueBlocks blocks after which a market order times out
*/
function updateMarketOrdersTimeoutBlocks(uint16 _valueBlocks) external;
/**
* @dev Updates the users that can bypass the link cost of triggerOrder
* @param _users array of addresses that can bypass the link cost of triggerOrder
* @param _shouldByPass whether each user should bypass the link cost
*/
function updateByPassTriggerLink(address[] memory _users, bool[] memory _shouldByPass) external;
/**
* @dev Initiates manual holding fees / negative pnl realization for an open trade (sends negative pnl into vault to avoid temporary imbalances due to timing of trades closing)
* @param _trader trader address
* @param _index trade index
* @param _isHoldingFees true if holding fees realization, false if negative pnl realization
*/
function initiateManualNegativePnlRealization(address _trader, uint32 _index, bool _isHoldingFees) external;
/**
* @dev Sets _delegate as the new delegate of caller (can call delegatedAction)
* @param _delegate the new delegate address
*/
function setTradingDelegate(address _delegate) external;
/**
* @dev Removes the delegate of caller (can't call delegatedAction)
*/
function removeTradingDelegate() external;
/**
* @dev Caller executes a trading action on behalf of _trader using delegatecall
* @param _trader the trader address to execute the trading action for
* @param _callData the data to be executed (open trade/close trade, etc.)
*/
function delegatedTradingAction(address _trader, bytes calldata _callData) external returns (bytes memory);
/**
* @dev Opens a new trade/limit order/stop order
* @param _trade the trade to be opened
* @param _maxSlippageP the maximum allowed slippage % when open the trade (1e3 precision)
* @param _referrer the address of the referrer (can only be set once for a trader)
*/
function openTrade(ITradingStorage.Trade memory _trade, uint16 _maxSlippageP, address _referrer) external;
/**
* @dev Wraps native token and opens a new trade/limit order/stop order
* @param _trade the trade to be opened
* @param _maxSlippageP the maximum allowed slippage % when open the trade (1e3 precision)
* @param _referrer the address of the referrer (can only be set once for a trader)
*/
function openTradeNative(
ITradingStorage.Trade memory _trade,
uint16 _maxSlippageP,
address _referrer
) external payable;
/**
* @dev Updates existing trade's max closing slippage % for caller
* @param _index index of trade
* @param _maxSlippageP new max closing slippage % (1e3 precision)
*/
function updateMaxClosingSlippageP(uint32 _index, uint16 _maxSlippageP) external;
/**
* @dev Closes an open trade (market order) for caller
* @param _index the index of the trade of caller
* @param _expectedPrice expected closing price, used to check max slippage (1e10 precision)
*/
function closeTradeMarket(uint32 _index, uint64 _expectedPrice) external;
/**
* @dev Updates an existing limit/stop order for caller
* @param _index index of limit/stop order of caller
* @param _triggerPrice new trigger price of limit/stop order (1e10 precision)
* @param _tp new tp of limit/stop order (1e10 precision)
* @param _sl new sl of limit/stop order (1e10 precision)
* @param _maxSlippageP new max slippage % of limit/stop order (1e3 precision)
*/
function updateOpenOrder(
uint32 _index,
uint64 _triggerPrice,
uint64 _tp,
uint64 _sl,
uint16 _maxSlippageP
) external;
/**
* @dev Cancels an open limit/stop order for caller
* @param _index index of limit/stop order of caller
*/
function cancelOpenOrder(uint32 _index) external;
/**
* @dev Updates the tp of an open trade for caller
* @param _index index of open trade of caller
* @param _newTp new tp of open trade (1e10 precision)
*/
function updateTp(uint32 _index, uint64 _newTp) external;
/**
* @dev Updates the sl of an open trade for caller
* @param _index index of open trade of caller
* @param _newSl new sl of open trade (1e10 precision)
*/
function updateSl(uint32 _index, uint64 _newSl) external;
/**
* @dev Initiates a new trigger order (for tp/sl/liq/limit/stop orders)
* @param _packed the packed data of the trigger order (orderType, trader, index)
*/
function triggerOrder(uint256 _packed) external;
/**
* @dev Triggers an order using signed prices (for tp/sl/liq/limit/stop orders)
* @param _packed the packed data of the trigger order (orderType, trader, index)
* @param _signedPairPrices the signed lookback pair prices to be used for the trigger order
*/
function triggerOrderWithSignatures(
uint256 _packed,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external;
/**
* @dev Safety function in case oracles don't answer in time, allows caller to cancel a pending order and if relevant claim back any stuck collateral
* @dev Only allowed for MARKET_OPEN, MARKET_CLOSE, UPDATE_LEVERAGE, MARKET_PARTIAL_OPEN, and MARKET_PARTIAL_CLOSE orders
* @param _orderIndex the id of the pending order to cancel
*/
function cancelOrderAfterTimeout(uint32 _orderIndex) external;
/**
* @dev Update trade leverage
* @param _index index of trade
* @param _newLeverage new leverage (1e3)
*/
function updateLeverage(uint32 _index, uint24 _newLeverage) external;
/**
* @dev Wraps native tokens and updates trade leverage
* @param _index index of trade
* @param _newLeverage new leverage (1e3)
*/
function updateLeverageNative(uint32 _index, uint24 _newLeverage) external payable;
/**
* @dev Increase trade position size
* @param _index index of trade
* @param _collateralDelta collateral to add (collateral precision)
* @param _leverageDelta partial trade leverage (1e3)
* @param _expectedPrice expected price of execution (1e10 precision)
* @param _maxSlippageP max slippage % (1e3)
*/
function increasePositionSize(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice,
uint16 _maxSlippageP
) external;
/**
* @dev Wraps native token and increase trade position size
* @param _index index of trade
* @param _collateralDelta collateral to add (collateral precision)
* @param _leverageDelta partial trade leverage (1e3)
* @param _expectedPrice expected price of execution (1e10 precision)
* @param _maxSlippageP max slippage % (1e3)
*/
function increasePositionSizeNative(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice,
uint16 _maxSlippageP
) external payable;
/**
* @dev Decrease trade position size
* @param _index index of trade
* @param _collateralDelta collateral to remove (collateral precision)
* @param _leverageDelta leverage to reduce by (1e3)
* @param _expectedPrice expected closing price, used to check max slippage (1e10 precision)
*/
function decreasePositionSize(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice
) external;
/**
* @dev Initiates pnl withdrawal request for a trade
* @param _index index of the trade
* @param _amountCollateral amount of pnl to claim in collateral tokens (collateral precision)
*/
function withdrawPositivePnl(uint32 _index, uint120 _amountCollateral) external;
/**
* @dev Returns the wrapped native token or address(0) if the current chain, or the wrapped token, is not supported.
*/
function getWrappedNativeToken() external view returns (address);
/**
* @dev Returns true if the token is the wrapped native token for the current chain, where supported.
* @param _token token address
*/
function isWrappedNativeToken(address _token) external view returns (bool);
/**
* @dev Returns the address a trader delegates his trading actions to
* @param _trader address of the trader
*/
function getTradingDelegate(address _trader) external view returns (address);
/**
* @dev Returns the current marketOrdersTimeoutBlocks value
*/
function getMarketOrdersTimeoutBlocks() external view returns (uint16);
/**
* @dev Returns whether a user bypasses trigger link costs
* @param _user address of the user
*/
function getByPassTriggerLink(address _user) external view returns (bool);
/**
* @dev Emitted when marketOrdersTimeoutBlocks is updated
* @param newValueBlocks the new value of marketOrdersTimeoutBlocks
*/
event MarketOrdersTimeoutBlocksUpdated(uint256 newValueBlocks);
/**
* @dev Emitted when a user is allowed/disallowed to bypass the link cost of triggerOrder
* @param user address of the user
* @param bypass whether the user can bypass the link cost of triggerOrder
*/
event ByPassTriggerLinkUpdated(address indexed user, bool bypass);
/**
* @dev Emitted when a manual negative pnl realization is initiated
* @param orderId order id
* @param trader trader address
* @param index index of trade
* @param isHoldingFeesRealization whether holding fees or negative pnl realization
*/
event ManualNegativePnlRealizationInitiated(
ITradingStorage.Id orderId,
address indexed trader,
uint32 indexed index,
bool isHoldingFeesRealization
);
/**
* @dev Emitted when a market order is initiated
* @param orderId price aggregator order id of the pending market order
* @param trader address of the trader
* @param pairIndex index of the trading pair
* @param open whether the market order is for opening or closing a trade
*/
event MarketOrderInitiated(ITradingStorage.Id orderId, address indexed trader, uint16 indexed pairIndex, bool open);
/**
* @dev Emitted when a new limit/stop order is placed
* @param trader address of the trader
* @param pairIndex index of the trading pair
* @param index index of the open limit order for caller
*/
event OpenOrderPlaced(address indexed trader, uint16 indexed pairIndex, uint32 indexed index);
/**
*
* @param trader address of the trader
* @param pairIndex index of the trading pair
* @param index index of the open limit/stop order for caller
* @param newPrice new trigger price (1e10 precision)
* @param newTp new tp (1e10 precision)
* @param newSl new sl (1e10 precision)
* @param maxSlippageP new max slippage % (1e3 precision)
*/
event OpenLimitUpdated(
address indexed trader,
uint16 indexed pairIndex,
uint32 indexed index,
uint64 newPrice,
uint64 newTp,
uint64 newSl,
uint64 maxSlippageP
);
/**
* @dev Emitted when a limit/stop order is canceled (collateral sent back to trader)
* @param trader address of the trader
* @param pairIndex index of the trading pair
* @param index index of the open limit/stop order for caller
*/
event OpenLimitCanceled(address indexed trader, uint16 indexed pairIndex, uint32 indexed index);
/**
* @dev Emitted when a trader initiates a pnl withdrawal request
* @param orderId order id
* @param trader address of the trader
* @param index index of the trade
* @param amountCollateral amount of positive pnl to claim in collateral tokens (collateral precision)
*/
event PositivePnlWithdrawalInitiated(
ITradingStorage.Id orderId,
address indexed trader,
uint32 indexed index,
uint256 amountCollateral
);
/**
* @dev Emitted when a trigger order is initiated (tp/sl/liq/limit/stop orders)
* @param orderId price aggregator order id of the pending trigger order
* @param trader address of the trader
* @param pairIndex index of the trading pair
* @param byPassesLinkCost whether the caller bypasses the link cost
*/
event TriggerOrderInitiated(
ITradingStorage.Id orderId,
address indexed trader,
uint16 indexed pairIndex,
bool byPassesLinkCost
);
/**
* @dev Emitted when a pending market order is canceled due to timeout
* @param pendingOrderId id of the pending order
* @param pairIndex index of the trading pair
*/
event ChainlinkCallbackTimeout(ITradingStorage.Id pendingOrderId, uint256 indexed pairIndex);
/**
* @dev Emitted when a pending market order is canceled due to timeout and new closeTradeMarket() call failed
* @param trader address of the trader
* @param pairIndex index of the trading pair
* @param index index of the open trade for caller
*/
event CouldNotCloseTrade(address indexed trader, uint16 indexed pairIndex, uint32 indexed index);
/**
* @dev Emitted when a native token is wrapped
* @param trader address of the trader
* @param nativeTokenAmount amount of native token wrapped
*/
event NativeTokenWrapped(address indexed trader, uint256 nativeTokenAmount);
/**
* @dev Emitted when a trade's collateral is returned after timeout
* @param pendingOrderId corresponding order id
* @param collateralIndex index of collateral
* @param trader trader address
* @param collateralAmount collateral amount returned (collateral precision)
*/
event CollateralReturnedAfterTimeout(
ITradingStorage.Id pendingOrderId,
uint8 indexed collateralIndex,
address indexed trader,
uint256 collateralAmount
);
error NotWrappedNativeToken();
error DelegateNotApproved();
error PriceZero();
error CollateralNotActive();
error PriceImpactTooHigh();
error NoTrade();
error NoOrder();
error AlreadyBeingMarketClosed();
error ConflictingPendingOrder(ITradingStorage.PendingOrderType);
error WrongLeverage();
error WrongTp();
error WrongSl();
error WaitTimeout();
error PendingTrigger();
error NoSl();
error NoTp();
error NotYourOrder();
error DelegatedActionNotAllowed();
error InsufficientCollateral();
error WrongFromBlock();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/ITradingStorage.sol";
/**
* @dev Interface for GNSTradingStorage facet (inherits types and also contains functions, events, and custom errors)
*/
interface ITradingStorageUtils is ITradingStorage {
/**
* @dev Initializes the trading storage facet
* @param _gns address of the gns token
* @param _gnsStaking address of the gns staking contract
* @param _collaterals collateral addresses to add
* @param _gTokens corresponding gToken vault addresses
*/
function initializeTradingStorage(
address _gns,
address _gnsStaking,
address[] memory _collaterals,
address[] memory _gTokens
) external;
/**
* @dev Updates the trading activated state
* @param _activated the new trading activated state
*/
function updateTradingActivated(TradingActivated _activated) external;
/**
* @dev Lists a new supported collateral (disabled by default).
* @dev Important: updateCollateralGnsLiquidityPool, updateCollateralUsdPriceFeed, and toggleCollateralActiveState must be called after this function.
* @param _collateral the address of the collateral
* @param _gToken the gToken contract of the collateral
*/
function addCollateral(address _collateral, address _gToken) external;
/**
* @dev Toggles the active state of a supported collateral
* @param _collateralIndex index of the collateral
*/
function toggleCollateralActiveState(uint8 _collateralIndex) external;
/**
* @dev Updates the contracts of a supported collateral trading stack
* @param _collateral address of the collateral
* @param _gToken the gToken contract of the collateral
*/
function updateGToken(address _collateral, address _gToken) external;
/**
* @dev Stores a new trade (trade/limit/stop)
* @param _trade trade to be stored
* @param _tradeInfo trade info to be stored
* @param _currentPairPrice the current pair price (1e10)
*/
function storeTrade(
Trade memory _trade,
TradeInfo memory _tradeInfo,
uint64 _currentPairPrice
) external returns (Trade memory);
/**
* @dev Updates an existing trade max closing slippage %
* @param _tradeId id of the trade
* @param _maxSlippageP new max slippage % (1e3 precision)
*/
function updateTradeMaxClosingSlippageP(ITradingStorage.Id memory _tradeId, uint16 _maxSlippageP) external;
/**
* @dev Updates an open trade collateral
* @param _tradeId id of updated trade
* @param _collateralAmount new collateral amount value (collateral precision)
* @param _leverage new leverage value
* @param _openPrice new open price value
* @param _pendingOrderType corresponding pending order type (ignore dust OI delta for leverage updates, refresh trade liq params for partial adds)
* @param _positionSizeTokenDelta position size token delta (1e18)
* @param _isPnlPositive whether the pnl is positive (only relevant when closing)
* @param _currentPairPrice the current pair price (1e10)
*/
function updateTradePosition(
Id memory _tradeId,
uint120 _collateralAmount,
uint24 _leverage,
uint64 _openPrice,
ITradingStorage.PendingOrderType _pendingOrderType,
uint256 _positionSizeTokenDelta,
bool _isPnlPositive,
uint64 _currentPairPrice
) external;
/**
* @dev Updates an open order details (limit/stop)
* @param _tradeId id of updated trade
* @param _openPrice new open price (1e10)
* @param _tp new take profit price (1e10)
* @param _sl new stop loss price (1e10)
* @param _maxSlippageP new max slippage % value (1e3)
*/
function updateOpenOrderDetails(
Id memory _tradeId,
uint64 _openPrice,
uint64 _tp,
uint64 _sl,
uint16 _maxSlippageP
) external;
/**
* @dev Updates the take profit of an open trade
* @param _tradeId the trade id
* @param _newTp the new take profit (1e10 precision)
*/
function updateTradeTp(Id memory _tradeId, uint64 _newTp) external;
/**
* @dev Updates the stop loss of an open trade
* @param _tradeId the trade id
* @param _newSl the new sl (1e10 precision)
*/
function updateTradeSl(Id memory _tradeId, uint64 _newSl) external;
/**
* @dev Marks an open trade/limit/stop as closed
* @param _tradeId the trade id
* @param _isPnlPositive whether the pnl is positive
* @param _currentPairPrice the current pair price (1e10)
*/
function closeTrade(Id memory _tradeId, bool _isPnlPositive, uint64 _currentPairPrice) external;
/**
* @dev Stores a new pending order
* @param _pendingOrder the pending order to be stored
*/
function storePendingOrder(PendingOrder memory _pendingOrder) external returns (PendingOrder memory);
/**
* @dev Closes a pending order
* @param _orderId the id of the pending order to be closed
*/
function closePendingOrder(Id memory _orderId) external;
/**
* @dev Validates an open trade order (for all order types apart from MARKET_OPEN)
* @param _trade the trade to validate
* @param _orderType the pending order type to validate
*/
function validateOpenTradeOrder(Trade memory _trade, PendingOrderType _orderType) external view;
/**
* @dev Returns collateral data by index
* @param _index the index of the supported collateral
*/
function getCollateral(uint8 _index) external view returns (Collateral memory);
/**
* @dev Returns whether can open new trades with a collateral
* @param _index the index of the collateral to check
*/
function isCollateralActive(uint8 _index) external view returns (bool);
/**
* @dev Returns whether a collateral has been listed
* @param _index the index of the collateral to check
*/
function isCollateralListed(uint8 _index) external view returns (bool);
/**
* @dev Returns whether a collateral is the GNS token
* @param _index the index of the collateral to check
*/
function isCollateralGns(uint8 _index) external view returns (bool);
/**
* @dev Returns the number of supported collaterals
*/
function getCollateralsCount() external view returns (uint8);
/**
* @dev Returns the supported collaterals
*/
function getCollaterals() external view returns (Collateral[] memory);
/**
* @dev Returns the index of a supported collateral
* @param _collateral the address of the collateral
*/
function getCollateralIndex(address _collateral) external view returns (uint8);
/**
* @dev Returns the collateral index of the GNS token. If 0, GNS is not a collateral.
*/
function getGnsCollateralIndex() external view returns (uint8);
/**
* @dev Returns the trading activated state
*/
function getTradingActivated() external view returns (TradingActivated);
/**
* @dev Returns whether a trader is stored in the traders array
* @param _trader trader to check
*/
function getTraderStored(address _trader) external view returns (bool);
/**
* @dev Returns the length of the traders array
*/
function getTradersCount() external view returns (uint256);
/**
* @dev Returns all traders that have open trades using a pagination system
* @param _offset start index in the traders array
* @param _limit end index in the traders array
*/
function getTraders(uint32 _offset, uint32 _limit) external view returns (address[] memory);
/**
* @dev Returns open trade/limit/stop order
* @param _trader address of the trader
* @param _index index of the trade for trader
*/
function getTrade(address _trader, uint32 _index) external view returns (Trade memory);
/**
* @dev Returns all open trades/limit/stop orders for a trader
* @param _trader address of the trader
*/
function getTrades(address _trader) external view returns (Trade[] memory);
/**
* @dev Returns all trade/limit/stop orders using a pagination system
* @param _traders list of traders to return trades for
* @param _offset index of first trade to return
* @param _limit index of last trade to return
*/
function getAllTradesForTraders(
address[] memory _traders,
uint256 _offset,
uint256 _limit
) external view returns (Trade[] memory);
/**
* @dev Returns all trade/limit/stop orders using a pagination system.
* @dev Calls `getAllTradesForTraders` internally with all traders.
* @param _offset index of first trade to return
* @param _limit index of last trade to return
*/
function getAllTrades(uint256 _offset, uint256 _limit) external view returns (Trade[] memory);
/**
* @dev Returns trade info of an open trade/limit/stop order
* @param _trader address of the trader
* @param _index index of the trade for trader
*/
function getTradeInfo(address _trader, uint32 _index) external view returns (TradeInfo memory);
/**
* @dev Returns all trade infos of open trade/limit/stop orders for a trader
* @param _trader address of the trader
*/
function getTradeInfos(address _trader) external view returns (TradeInfo[] memory);
/**
* @dev Returns all trade infos of open trade/limit/stop orders using a pagination system
* @param _traders list of traders to return tradeInfo for
* @param _offset index of first tradeInfo to return
* @param _limit index of last tradeInfo to return
*/
function getAllTradeInfosForTraders(
address[] memory _traders,
uint256 _offset,
uint256 _limit
) external view returns (TradeInfo[] memory);
/**
* @dev Returns all trade infos of open trade/limit/stop orders using a pagination system.
* @dev Calls `getAllTradeInfosForTraders` internally with all traders.
* @param _offset index of first tradeInfo to return
* @param _limit index of last tradeInfo to return
*/
function getAllTradeInfos(uint256 _offset, uint256 _limit) external view returns (TradeInfo[] memory);
/**
* @dev Returns a pending ordeer
* @param _orderId id of the pending order
*/
function getPendingOrder(Id memory _orderId) external view returns (PendingOrder memory);
/**
* @dev Returns all pending orders for a trader
* @param _user address of the trader
*/
function getPendingOrders(address _user) external view returns (PendingOrder[] memory);
/**
* @dev Returns all pending orders using a pagination system
* @param _traders list of traders to return pendingOrder for
* @param _offset index of first pendingOrder to return
* @param _limit index of last pendingOrder to return
*/
function getAllPendingOrdersForTraders(
address[] memory _traders,
uint256 _offset,
uint256 _limit
) external view returns (PendingOrder[] memory);
/**
* @dev Returns all pending orders using a pagination system
* @dev Calls `getAllPendingOrdersForTraders` internally with all traders.
* @param _offset index of first pendingOrder to return
* @param _limit index of last pendingOrder to return
*/
function getAllPendingOrders(uint256 _offset, uint256 _limit) external view returns (PendingOrder[] memory);
/**
* @dev Returns the block number of the pending order for a trade (0 = doesn't exist)
* @param _tradeId id of the trade
* @param _orderType pending order type to check
*/
function getTradePendingOrderBlock(Id memory _tradeId, PendingOrderType _orderType) external view returns (uint256);
/**
* @dev Returns the counters of a trader (currentIndex / open count for trades/tradeInfos and pendingOrders mappings)
* @param _trader address of the trader
* @param _type the counter type (trade/pending order)
*/
function getCounters(address _trader, CounterType _type) external view returns (Counter memory);
/**
* @dev Returns the counters for a list of traders
* @param _traders the list of traders
* @param _type the counter type (trade/pending order)
*/
function getCountersForTraders(
address[] calldata _traders,
CounterType _type
) external view returns (Counter[] memory);
/**
* @dev Returns the address of the gToken for a collateral stack
* @param _collateralIndex the index of the supported collateral
*/
function getGToken(uint8 _collateralIndex) external view returns (address);
/**
* @dev Returns the liquidation params for a trade
* @param _trader address of the trader
* @param _index index of the trade for trader
*/
function getTradeLiquidationParams(
address _trader,
uint32 _index
) external view returns (IPairsStorage.GroupLiquidationParams memory);
/**
* @dev Returns all trade liquidation params of open trade/limit/stop orders for a trader
* @param _trader address of the trader
*/
function getTradesLiquidationParams(
address _trader
) external view returns (IPairsStorage.GroupLiquidationParams[] memory);
/**
* @dev Returns all trade liquidation params of open trade/limit/stop orders using a pagination system
* @param _traders list of traders to return liq params for
* @param _offset index of first liq param to return
* @param _limit index of last liq param to return
*/
function getAllTradesLiquidationParamsForTraders(
address[] memory _traders,
uint256 _offset,
uint256 _limit
) external view returns (IPairsStorage.GroupLiquidationParams[] memory);
/**
* @dev Returns all trade liquidation params of open trade/limit/stop orders using a pagination system
* @dev Calls `getAllTradesLiquidationParamsForTraders` internally with all traders.
* @param _offset index of first liq param to return
* @param _limit index of last liq param to return
*/
function getAllTradesLiquidationParams(
uint256 _offset,
uint256 _limit
) external view returns (IPairsStorage.GroupLiquidationParams[] memory);
/**
* @dev Returns the current contracts version
*/
function getCurrentContractsVersion() external view returns (ITradingStorage.ContractsVersion);
/**
* @dev Returns the contracts version for a trade
* @param _trader address of the trader
* @param _index index of the trade
*/
function getTradeContractsVersion(
address _trader,
uint32 _index
) external view returns (ITradingStorage.ContractsVersion);
/**
* @dev Returns the corresponding lookback fromBlock for a trigger
* @param _trader address of the trader
* @param _index index of the trade
* @param _orderType trigger order type
*/
function getLookbackFromBlock(
address _trader,
uint32 _index,
ITradingStorage.PendingOrderType _orderType
) external view returns (uint32);
/**
* @dev Emitted when the trading activated state is updated
* @param activated the new trading activated state
*/
event TradingActivatedUpdated(TradingActivated activated);
/**
* @dev Emitted when a new supported collateral is added
* @param collateral the address of the collateral
* @param index the index of the supported collateral
* @param gToken the gToken contract of the collateral
*/
event CollateralAdded(address collateral, uint8 index, address gToken);
/**
* @dev Emitted when an existing supported collateral active state is updated
* @param index the index of the supported collateral
* @param isActive the new active state
*/
event CollateralUpdated(uint8 indexed index, bool isActive);
/**
* @dev Emitted when an existing supported collateral is disabled (can still close trades but not open new ones)
* @param index the index of the supported collateral
*/
event CollateralDisabled(uint8 index);
/**
* @dev Emitted when the contracts of a supported collateral trading stack are updated
* @param collateral the address of the collateral
* @param index the index of the supported collateral
* @param gToken the gToken contract of the collateral
*/
event GTokenUpdated(address collateral, uint8 index, address gToken);
/**
* @dev Emitted when a new trade is stored
* @param user trade user
* @param index trade index
* @param trade the trade stored
* @param tradeInfo the trade info stored
* @param liquidationParams the trade liquidation params stored
*/
event TradeStored(
address indexed user,
uint32 indexed index,
Trade trade,
TradeInfo tradeInfo,
IPairsStorage.GroupLiquidationParams liquidationParams
);
/**
* @dev Emitted when the max closing slippage % of an open trade is updated
* @param user trade user
* @param index trade index
* @param maxClosingSlippageP new max closing slippage % value (1e3 precision)
*/
event TradeMaxClosingSlippagePUpdated(address indexed user, uint32 indexed index, uint16 maxClosingSlippageP);
/**
* @dev Emitted when an open trade collateral is updated
* @param user trade user
* @param index trade index
* @param collateralAmount new collateral value (collateral precision)
*/
event TradeCollateralUpdated(address indexed user, uint32 indexed index, uint120 collateralAmount);
/**
* @dev Emitted when an open trade collateral is updated
* @param user trade user
* @param index trade index
* @param collateralAmount new collateral value (collateral precision)
* @param leverage new leverage value (1e3)
* @param openPrice new open price value (1e10)
* @param newSl new sl price (1e10)
* @param newPositionSizeToken new position size in token amount (1e18)
* @param pendingOrderType corresponding pending order type
* @param isPnlPositive true if trade pnl is positive (only relevant when closing)
*/
event TradePositionUpdated(
address indexed user,
uint32 indexed index,
uint120 collateralAmount,
uint24 leverage,
uint64 openPrice,
uint64 newSl,
uint160 newPositionSizeToken,
ITradingStorage.PendingOrderType pendingOrderType,
bool isPnlPositive
);
/**
* @dev Emitted when an existing trade/limit order/stop order is updated
* @param user trade user
* @param index trade index
* @param openPrice new open price value (1e10)
* @param tp new take profit value (1e10)
* @param sl new stop loss value (1e10)
* @param maxSlippageP new max slippage % value (1e3)
*/
event OpenOrderDetailsUpdated(
address indexed user,
uint32 indexed index,
uint64 openPrice,
uint64 tp,
uint64 sl,
uint16 maxSlippageP
);
/**
* @dev Emitted when the take profit of an open trade is updated
* @param user trade user
* @param index trade index
* @param newTp the new take profit (1e10 precision)
*/
event TradeTpUpdated(address indexed user, uint32 indexed index, uint64 newTp);
/**
* @dev Emitted when the stop loss of an open trade is updated
* @param user trade user
* @param index trade index
* @param newSl the new sl (1e10 precision)
*/
event TradeSlUpdated(address indexed user, uint32 indexed index, uint64 newSl);
/**
* @dev Emitted when an open trade is closed
* @param user trade user
* @param index trade index
* @param isPnlPositive true if trade pnl is positive
*/
event TradeClosed(address indexed user, uint32 indexed index, bool isPnlPositive);
/**
* @dev Emitted when a new pending order is stored
* @param pendingOrder the pending order stored
*/
event PendingOrderStored(PendingOrder pendingOrder);
/**
* @dev Emitted when a pending order is closed
* @param orderId the id of the pending order closed
*/
event PendingOrderClosed(Id orderId);
error MissingCollaterals();
error CollateralAlreadyActive();
error CollateralAlreadyDisabled();
error TradePositionSizeZero();
error TradeOpenPriceZero();
error TradePairNotListed();
error TradeTpInvalid();
error TradeSlInvalid();
error MaxSlippageZero();
error TradeInfoCollateralPriceUsdZero();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/ITriggerRewards.sol";
/**
* @dev Interface for GNSTriggerRewards facet (inherits types and also contains functions, events, and custom errors)
*/
interface ITriggerRewardsUtils is ITriggerRewards {
/**
*
* @dev Initializes parameters for trigger rewards facet
* @param _timeoutBlocks blocks after which a trigger times out
*/
function initializeTriggerRewards(uint16 _timeoutBlocks) external;
/**
*
* @dev Updates the blocks after which a trigger times out
* @param _timeoutBlocks blocks after which a trigger times out
*/
function updateTriggerTimeoutBlocks(uint16 _timeoutBlocks) external;
/**
*
* @dev Distributes GNS rewards to oracles for a specific trigger
* @param _rewardGns total GNS reward to be distributed among oracles
*/
function distributeTriggerReward(uint256 _rewardGns) external;
/**
* @dev Claims pending GNS trigger rewards for the caller
* @param _oracle address of the oracle
*/
function claimPendingTriggerRewards(address _oracle) external;
/**
*
* @dev Returns current triggerTimeoutBlocks value
*/
function getTriggerTimeoutBlocks() external view returns (uint16);
/**
*
* @dev Checks if an order is active (exists and has not timed out)
* @param _orderBlock block number of the order
*/
function hasActiveOrder(uint256 _orderBlock) external view returns (bool);
/**
*
* @dev Returns the pending GNS trigger rewards for an oracle
* @param _oracle address of the oracle
*/
function getTriggerPendingRewardsGns(address _oracle) external view returns (uint256);
/**
*
* @dev Emitted when timeoutBlocks is updated
* @param timeoutBlocks blocks after which a trigger times out
*/
event TriggerTimeoutBlocksUpdated(uint16 timeoutBlocks);
/**
*
* @dev Emitted when trigger rewards are distributed for a specific order
* @param rewardsPerOracleGns reward in GNS distributed per oracle
* @param oraclesCount number of oracles rewarded
*/
event TriggerRewarded(uint256 rewardsPerOracleGns, uint256 oraclesCount);
/**
*
* @dev Emitted when pending GNS trigger rewards are claimed by an oracle
* @param oracle address of the oracle
* @param rewardsGns GNS rewards claimed
*/
event TriggerRewardsClaimed(address oracle, uint256 rewardsGns);
error TimeoutBlocksZero();
error NoPendingTriggerRewards();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IUpdateLeverage.sol";
import "../types/ITradingStorage.sol";
import "../types/ITradingCallbacks.sol";
/**
* @dev Interface for leverage updates
*/
interface IUpdateLeverageUtils is IUpdateLeverage {
/**
* @param orderId request order id
* @param trader address of trader
* @param pairIndex index of pair
* @param index index of trade
* @param isIncrease true if increase leverage, false if decrease
* @param newLeverage new leverage value (1e3)
*/
event LeverageUpdateInitiated(
ITradingStorage.Id orderId,
address indexed trader,
uint256 indexed pairIndex,
uint256 indexed index,
bool isIncrease,
uint256 newLeverage
);
/**
* @param orderId request order id
* @param isIncrease true if leverage increased, false if decreased
* @param cancelReason cancel reason (executed if none)
* @param collateralIndex collateral index
* @param trader address of trader
* @param pairIndex index of pair
* @param index index of trade
* @param oraclePrice current oracle price (1e10)
* @param collateralDelta collateral delta (collateral precision)
* @param values useful values (new collateral, new leverage, liq price, gov fee collateral)
*/
event LeverageUpdateExecuted(
ITradingStorage.Id orderId,
bool isIncrease,
ITradingCallbacks.CancelReason cancelReason,
uint8 indexed collateralIndex,
address indexed trader,
uint256 pairIndex,
uint256 indexed index,
uint256 oraclePrice,
uint256 collateralDelta,
IUpdateLeverage.UpdateLeverageValues values
);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/IUpdatePositionSize.sol";
import "../types/ITradingStorage.sol";
import "../types/ITradingCallbacks.sol";
/**
* @dev Interface for position size updates
*/
interface IUpdatePositionSizeUtils is IUpdatePositionSize {
/**
* @param orderId request order id
* @param trader address of the trader
* @param pairIndex index of the pair
* @param index index of user trades
* @param isIncrease true if increase position size, false if decrease
* @param collateralDelta collateral delta (collateral precision)
* @param leverageDelta leverage delta (1e3)
*/
event PositionSizeUpdateInitiated(
ITradingStorage.Id orderId,
address indexed trader,
uint256 indexed pairIndex,
uint256 indexed index,
bool isIncrease,
uint256 collateralDelta,
uint256 leverageDelta
);
/**
* @param orderId request order id
* @param cancelReason cancel reason if canceled or none if executed
* @param collateralIndex collateral index
* @param trader address of trader
* @param pairIndex index of pair
* @param index index of trade
* @param long true for long, false for short
* @param oraclePrice oracle price (1e10)
* @param collateralPriceUsd collateral price in USD (1e8)
* @param collateralDelta collateral delta (collateral precision)
* @param leverageDelta leverage delta (1e3)
* @param values important values (new open price, new leverage, new collateral, etc.)
*/
event PositionSizeIncreaseExecuted(
ITradingStorage.Id orderId,
ITradingCallbacks.CancelReason cancelReason,
uint8 indexed collateralIndex,
address indexed trader,
uint256 pairIndex,
uint256 indexed index,
bool long,
uint256 oraclePrice,
uint256 collateralPriceUsd,
uint256 collateralDelta,
uint256 leverageDelta,
IUpdatePositionSize.IncreasePositionSizeValues values
);
/**
* @param orderId request order id
* @param cancelReason cancel reason if canceled or none if executed
* @param collateralIndex collateral index
* @param trader address of trader
* @param pairIndex index of pair
* @param index index of trade
* @param long true for long, false for short
* @param oraclePrice oracle price (1e10)
* @param collateralPriceUsd collateral price in USD (1e8)
* @param collateralDelta collateral delta (collateral precision)
* @param leverageDelta leverage delta (1e3)
* @param values important values (pnl, new leverage, new collateral, etc.)
*/
event PositionSizeDecreaseExecuted(
ITradingStorage.Id orderId,
ITradingCallbacks.CancelReason cancelReason,
uint8 indexed collateralIndex,
address indexed trader,
uint256 pairIndex,
uint256 indexed index,
bool long,
uint256 oraclePrice,
uint256 collateralPriceUsd,
uint256 collateralDelta,
uint256 leverageDelta,
IUpdatePositionSize.DecreasePositionSizeValues values
);
error InvalidIncreasePositionSizeInput();
error InvalidDecreasePositionSizeInput();
error NewPositionSizeSmaller();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Interface for BlockManager_Mock contract (test helper)
*/
interface IBlockManager_Mock {
function getBlockNumber() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSAddressStore facet
*/
interface IAddressStore {
enum Role {
GOV_TIMELOCK,
GOV,
MANAGER,
GOV_EMERGENCY,
GOV_EMERGENCY_TIMELOCK
}
struct Addresses {
address gns;
address gnsStaking;
address treasury;
}
struct AddressStore {
uint256 __deprecated; // previously globalAddresses (gns token only, 1 slot)
mapping(address => mapping(Role => bool)) accessControl;
Addresses globalAddresses;
uint256[7] __gap1; // gap for global addresses
// insert new storage here
uint256[38] __gap2; // gap for rest of diamond storage
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./IPairsStorage.sol";
/**
* @dev Contains the types for the GNSBorrowingFees facet
*/
interface IBorrowingFees {
struct BorrowingFeesStorage {
mapping(uint8 => mapping(uint16 => BorrowingData)) pairs;
mapping(uint8 => mapping(uint16 => BorrowingPairGroup[])) pairGroups;
mapping(uint8 => mapping(uint16 => OpenInterest)) pairOisBeforeV10;
mapping(uint8 => mapping(uint16 => BorrowingData)) groups;
mapping(uint8 => mapping(uint16 => OpenInterest)) groupOis;
mapping(uint8 => mapping(address => mapping(uint32 => BorrowingInitialAccFees))) initialAccFees;
mapping(uint8 => mapping(uint16 => BorrowingFeePerBlockCap)) pairFeePerBlockCaps;
BorrowingFeePerBlockCap feePerBlockCap;
uint256[42] __gap;
}
struct BorrowingData {
uint32 feePerBlock; // 1e10 (%)
uint64 accFeeLong; // 1e10 (%)
uint64 accFeeShort; // 1e10 (%)
uint48 accLastUpdatedBlock;
uint48 feeExponent;
}
struct BorrowingPairGroup {
uint16 groupIndex;
uint48 block;
uint64 initialAccFeeLong; // 1e10 (%)
uint64 initialAccFeeShort; // 1e10 (%)
uint64 prevGroupAccFeeLong; // 1e10 (%)
uint64 prevGroupAccFeeShort; // 1e10 (%)
uint64 pairAccFeeLong; // 1e10 (%)
uint64 pairAccFeeShort; // 1e10 (%)
uint64 __placeholder; // might be useful later
}
struct OpenInterest {
uint72 long; // 1e10 (collateral)
uint72 short; // 1e10 (collateral)
uint72 max; // 1e10 (collateral)
uint40 __placeholder; // might be useful later
}
struct BorrowingInitialAccFees {
uint64 accPairFee; // 1e10 (%)
uint64 accGroupFee; // 1e10 (%)
uint48 block;
uint80 __placeholder; // might be useful later
}
struct BorrowingPairParams {
uint16 groupIndex;
uint32 feePerBlock; // 1e10 (%)
uint48 feeExponent;
uint72 maxOi;
}
struct BorrowingGroupParams {
uint32 feePerBlock; // 1e10 (%)
uint72 maxOi; // 1e10
uint48 feeExponent;
}
struct BorrowingFeePerBlockCap {
uint32 minP; // 1e3 (%)
uint32 maxP; // 1e3 (%)
}
struct BorrowingFeeInput {
uint8 collateralIndex;
address trader;
uint16 pairIndex;
uint32 index;
bool long;
uint256 collateral; // 1e18 | 1e6 (collateral)
uint256 leverage; // 1e3
uint256 currentPairPrice; // 1e10
}
struct LiqPriceInput {
uint8 collateralIndex;
address trader;
uint16 pairIndex;
uint32 index;
uint64 openPrice; // 1e10
bool long;
uint256 collateral; // 1e18 | 1e6 (collateral)
uint256 leverage; // 1e3
int256 additionalFeeCollateral; // 1e18 | 1e6 (collateral)
IPairsStorage.GroupLiquidationParams liquidationParams;
uint64 currentPairPrice; // 1e10
bool isCounterTrade;
uint256 partialCloseMultiplier; // 1e18
bool beforeOpened;
}
struct PendingBorrowingAccFeesInput {
uint64 accFeeLong; // 1e10 (%)
uint64 accFeeShort; // 1e10 (%)
uint256 oiLong; // 1e18 | 1e6
uint256 oiShort; // 1e18 | 1e6
uint32 feePerBlock; // 1e10
uint256 currentBlock;
uint256 accLastUpdatedBlock;
uint72 maxOi; // 1e10
uint48 feeExponent;
uint128 collateralPrecision;
BorrowingFeePerBlockCap feePerBlockCap;
}
// Useful to avoid stack too deep errors
struct AccFeesDeltaValues {
bool moreShorts;
uint256 blockDistance;
uint256 maxOiScaled; // 1e18 | 1e6
uint256 netOi; // 1e18 | 1e6
uint256 minNetOi; // 1e18 | 1e6
uint256 maxNetOi; // 1e18 | 1e6
uint256 cappedNetOi; // 1e18 | 1e6
}
struct GetTradeBorrowingFeeValues {
BorrowingInitialAccFees initialFees;
BorrowingPairGroup firstPairGroup;
uint256 borrowingFeeP; // 1e10 (%)
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSChainConfig facet
*/
interface IChainConfig {
struct ChainConfigStorage {
uint256 reentrancyLock; // HAS TO BE FIRST AND TAKE A FULL SLOT (GNSReentrancyGuard expects it)
uint16 nativeTransferGasLimit; // 16 bits. 64,535 max value
bool nativeTransferEnabled; // When true, the diamond is allowed to unwrap native tokens on transfer out
uint232 __placeholder;
uint256[48] __gap;
}
}// SPDX-License-Identifier: MIT pragma solidity 0.8.23; /** * @author Nick Mudge <[email protected]> (https://twitter.com/mudgen) * @author Gains Network * @dev Based on EIP-2535: Diamonds (https://eips.ethereum.org/EIPS/eip-2535) * @dev Follows diamond-3 implementation (https://github.com/mudgen/diamond-3-hardhat/) * @dev Contains the types used in the diamond management contracts. */ interface IDiamondStorage { struct DiamondStorage { // maps function selector to the facet address and // the position of the selector in the facetFunctionSelectors.selectors array mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition; // maps facet addresses to function selectors mapping(address => FacetFunctionSelectors) facetFunctionSelectors; // facet addresses address[] facetAddresses; address[47] __gap; } struct FacetAddressAndPosition { address facetAddress; uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array } struct FacetFunctionSelectors { bytes4[] functionSelectors; uint256 facetAddressPosition; // position of facetAddress in facetAddresses array } enum FacetCutAction { ADD, REPLACE, REMOVE, NOP } struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } }
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSFeeTiers facet
*/
interface IFeeTiers {
struct FeeTiersStorage {
// Volume Fee Tiers
FeeTier[8] feeTiers;
mapping(uint256 => uint256) groupVolumeMultipliers; // groupIndex (pairs storage) => multiplier (1e3)
mapping(address => TraderInfo) traderInfos; // trader => TraderInfo
mapping(address => mapping(uint32 => TraderDailyInfo)) traderDailyInfos; // trader => day => TraderDailyInfo
mapping(address => TraderEnrollment) traderEnrollments; // trader => TraderEnrollment
mapping(address => uint224) unclaimedPoints; // trader => points (1e18)
// Staking Tiers
FeeTier[8] gnsStakingTiers;
mapping(address => GnsStakingInfo) gnsStakingInfos; // trader => staking info
address gnsVaultAddress; // gGNS or any other GNS ERC4626 vault
bool useGnsVaultBalance; // if true, gnsVaultAddress balance is used for tier calculation
uint88 __placeholder;
// Gap
uint256[27] __gap;
}
enum TraderEnrollmentStatus {
ENROLLED,
EXCLUDED
}
enum CreditType {
IMMEDIATE,
CLAIMABLE
}
struct FeeTier {
uint32 feeMultiplier; // 1e3
uint32 pointsThreshold; // 0 precision; GNS amount or volume points
}
struct TraderInfo {
uint32 lastDayUpdated;
uint224 trailingPoints; // 1e18
}
struct TraderDailyInfo {
uint32 feeMultiplierCache; // 1e3
uint224 points; // 1e18
}
struct TraderEnrollment {
TraderEnrollmentStatus status;
uint248 __placeholder;
}
struct GnsStakingInfo {
uint88 stakedGns; // 1e18
uint88 stakedVaultGns; // 1e18
uint24 bonusAmount; // 0 precision
uint32 stakeTimestamp;
uint32 feeMultiplierCache; // 1e3
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./ITradingStorage.sol";
import "./IPriceImpact.sol";
/**
* @dev Contains the types for the GNSFundingFees facet
*/
interface IFundingFees {
struct FundingFeesStorage {
mapping(uint8 => mapping(uint16 => PairGlobalParams)) pairGlobalParams;
mapping(uint8 => mapping(uint16 => FundingFeeParams)) pairFundingFeeParams;
mapping(uint8 => mapping(uint16 => BorrowingFeeParams)) pairBorrowingFeeParams;
mapping(uint8 => mapping(uint16 => PairFundingFeeData)) pairFundingFeeData;
mapping(uint8 => mapping(uint16 => PairBorrowingFeeData)) pairBorrowingFeeData;
mapping(address => mapping(uint32 => TradeFeesData)) tradeFeesData;
mapping(address => mapping(uint32 => UiRealizedPnlData)) tradeUiRealizedPnlData;
mapping(uint32 => PendingParamUpdate) pendingParamUpdates;
uint256[42] __gap;
}
enum ParamUpdateType {
SKEW_COEFFICIENT_PER_YEAR,
ABSOLUTE_VELOCITY_PER_YEAR_CAP,
ABSOLUTE_RATE_PER_SECOND_CAP,
THETA_THRESHOLD_USD,
FUNDING_FEES_ENABLED,
APR_MULTIPLIER_ENABLED,
BORROWING_RATE_PER_SECOND_P,
BORROWING_PAIR
}
struct PairGlobalParams {
/**
* @dev Max skew in collateral tokens allowed when opening a trade / partial adding
* @dev Min: 0 (= disabled)
* @dev Precision: 1e-10
* @dev Max: 120,892,581,961,462 (= 120m USD if collateral/usd = 1e-6)
* @dev If left uninitialized (0), max skew is disabled
*/
uint80 maxSkewCollateral;
uint176 __placeholder;
}
struct FundingFeeParams {
/**
* @dev Yearly velocity coefficient of the funding rate % / second, it is multiplied by the net exposure in tokens.
* @dev Precision: 1e-26 => 10^20 token exposure (= 100m USD, 1 collateral = 1e-6 USD, current pair price = 1e-6), funding APR += 31.536% each year (+= 0.0864% each day)
* @dev Max: 5.19e7 => 1e-8 collateral exposure (= 10k USD, 1 collateral = 1e6 USD, current pair price = 1e6), funding APR += 16,374,427% each year (+= 44,861% each day, += 1,869% each hour)
* @dev If left uninitialized (0), the funding rate velocity will stay at 0
*/
uint112 skewCoefficientPerYear;
/**
* @dev Absolute cap on the yearly velocity of the funding rate % / second (same when funding positive or negative).
* @dev Precision: 1e-7 => 3.15% APR absolute increase per year (+= 0.00864% each day)
* @dev Theoretical max: 1.67 => max 52,908,625% APR absolute increase per year (+= 144,955% each day, += 6,039% each hour)
* @dev If left uninitialized (0), the funding rate velocity will stay at 0
*/
uint24 absoluteVelocityPerYearCap;
/**
* @dev Cap on the absolute value of the funding rate % / second.
* @dev Precision: 1e-10 => 0.0031536% APR
* @dev Theoretical max: 0.0016 => 52,908% APR
* @dev True cap: 0.0003170979 => 10,000% APR
* @dev If left uninitialized (0), the funding rate will stay at 0
*/
uint24 absoluteRatePerSecondCap;
/**
* @dev Minimum net exposure in USD required for funding rate velocity to be != 0.
* @dev Precision: 1 USD (no decimals)
* @dev Max: 4,294,967,295 USD
* @dev If left uninitialized (0), there will be no minimum exposure threshold
*/
uint32 thetaThresholdUsd;
/**
* @dev When set to true, enables funding fees on the pair (funding rate always resumes from last rate after funding fees were disabled)
* @dev When set to false, disables funding fees on the pair
* @dev If left uninitialized (false), funding fees are disabled by default on the pair
*/
bool fundingFeesEnabled;
/**
* @dev When set to true, enables earning side APR multiplier (earning APR = paying APR * paying OI / earning OI)
* @dev When set to false, disables earning side APR multiplier (paying APR = earning APR)
* @dev If left uninitialized (false), earning side APR multiplier is disabled by default on the pair
*/
bool aprMultiplierEnabled;
uint48 __placeholder;
}
struct BorrowingFeeParams {
/**
* @dev Borrowing rate % / second for a given pair.
* @dev Precision: 1e-10 => 0.0031536% APR
* @dev Theoretical max: 0.0016 => 52,908% APR
* @dev True cap: 0.0000317097 => 1,000% APR
* @dev If left uninitialized (0), the borrowing rate is 0
*/
uint24 borrowingRatePerSecondP;
uint232 __placeholder;
}
struct PairFundingFeeData {
/**
* @dev Accumulated funding fees % weighted by pair price (unit: fee collateral per 100 units of OI in pair amount)
* @dev To know the funding fee in collateral tokens for a trade, do (current acc fee - initial acc fee) * position size collateral / entry price / 1e10 / 100.
* @dev Step: 1e-20%; min step = lowest funding rate % per second * 1 second * lowest pair price = 1e-20% (no issues with rounding down)
* @dev Max: 1.7e18; max possible value = max funding % / second * max timestamp * max pair price * 100 (smaller side cap) = 0.0016 * 4,294,967,295 * 1e6 * 100 = 6.87e14
* @dev Acc funding fees use funding rate % / second precision of 1e-10 => min/step APR = 0.0031536%
*/
int128 accFundingFeeLongP;
int128 accFundingFeeShortP;
/**
* @dev Funding rate % / second when the net exposure or funding fee parameters last changed.
* @dev The true current rate is different depending on the current funding rate velocity and the timestamp of the last update.
* @dev Precision: 1e-18% => 3.15e-11% APR; min step = lowest velocity per year * 1 second / 1 year = 1e-10 * 1 / (86400*365) = 3.17e-18 (no issues with rounding down)
* @dev Theoretical max: 0.036 => 1,136,204% APR
* @dev True cap: 0.0003171 => 10,000% APR
*/
int56 lastFundingRatePerSecondP;
/**
* @dev Timestamp of last accumulated funding fees update
* @dev Max: 4,294,967,295 (Feb 07 2106)
*/
uint32 lastFundingUpdateTs;
uint168 __placeholder;
}
struct PairBorrowingFeeData {
/**
* @dev Accumulated borrowing fees % weighted by pair price (unit: fee collateral per 100 units of OI in pair amount)
* @dev To know the borrowing fee in collateral tokens for a trade, do (current acc fee - initial acc fee) * position size collateral / entry price / 1e10 / 100.
* @dev Precision: 1e-20%; min step = lowest borrowing rate % per second * 1 second * lowest pair price = 1e-20% (no issues with rounding down)
* @dev Max: 3.4e18; max possible value = max borrowing % / second * max timestamp * max pair price = 0.0016 * 4,294,967,295 * 1e6 = 6.87e12
*/
uint128 accBorrowingFeeP;
/**
* @dev Timestamp of last accumulated borrowing fees update
* @dev Max: 4,294,967,295 (Feb 07 2106)
*/
uint32 lastBorrowingUpdateTs;
uint96 __placeholder;
}
struct TradeFeesData {
/**
* @dev Realized trading fees impact trade value and collateral in diamond (already sent) and realized pnl only impacts trade value.
* @dev Trading fees/Holding fees/Realized negative/positive pnl don't impact trade collateral/position size, they are stored separately here.
* @dev They bring the liquidation price closer or further away depending on the sign.
* @dev Precision: collateral precision
*/
uint128 realizedTradingFeesCollateral;
int128 realizedPnlCollateral;
/**
* @dev The following variables reduce the collateral available in diamond without impacting the trade value.
* @dev It's cleaner than to increase realized trading fees and compensate by realizing positive pnl.
* @dev Precision: collateral precision
*/
uint128 manuallyRealizedNegativePnlCollateral;
uint128 alreadyTransferredNegativePnlCollateral;
/**
* @dev The following variable increases the collateral available in diamond without impacting the trade value.
* @dev Virtual available collateral in diamond (collateral precision) is important for leverage increases (= collateral withdrawals)
* @dev It allows maintaining the same trade value and having no impact on other vars used to calculate available in diamond
* @dev While never allowing a trade's available collateral in diamond to go below zero.
* @dev If the user deposits collateral, available in diamond should always increase instead of going from negative to zero.
* @dev Precision: collateral precision
*/
uint128 virtualAvailableCollateralInDiamond;
uint128 __placeholder;
/**
* @dev Used to know how much pending funding/borrowing fees a trade has to pay.
* @dev When trade position size changes, updated to new acc funding/borrowing fees, and pending existing holding fees are stored separately.
* @dev Precision: same as PairFundingFeeData.accFundingFeeLongP/accFundingFeeShortP and PairBorrowingFeeData.accBorrowingFeeP
*/
int128 initialAccFundingFeeP;
uint128 initialAccBorrowingFeeP;
}
struct UiRealizedPnlData {
/**
* @dev Trading action fees charged on the open trade (open, partial add, partial close, etc.)
* @dev Precision: collateral precision
*/
uint128 realizedTradingFeesCollateral;
/**
* @dev Old borrowing fees realized on the open trade
* @dev Precision: collateral precision
*/
uint128 realizedOldBorrowingFeesCollateral;
/**
* @dev New borrowing fees realized on the open trade
* @dev Precision: collateral precision
*/
uint128 realizedNewBorrowingFeesCollateral;
/**
* @dev Funding fees realized on the open trade
* @dev Precision: collateral precision
*/
int128 realizedFundingFeesCollateral;
/**
* @dev Realized pnl of all previous partial closes on the open trade
* @dev Precision: collateral precision
*/
int128 realizedPnlPartialCloseCollateral;
/**
* @dev All pnl withdrawn on the open trade
* @dev Precision: collateral precision
*/
uint128 pnlWithdrawnCollateral;
}
struct PendingParamUpdate {
uint8 collateralIndex;
uint16 pairIndex;
ParamUpdateType updateType;
uint224 newValue; /// @custom:dev uint224 is bigger than any of the funding/borrowing fee params, so no overflow possible
}
struct FundingFeeValues {
IPriceImpact.PairOiToken pairOiToken; // 1e18
int256 netExposureToken; // 1e18
int256 netExposureUsd; // 1e10
uint256 secondsSinceLastUpdate;
int40 currentVelocityPerYear; // 1e10
int256 avgFundingRatePerSecondP; // 1e18 (%)
int256 currentPairPriceInt; // 1e10
uint256 secondsToReachZeroRate;
int256 fundingFeesDeltaP; // 1e20 (%)
uint256 longAprMultiplier; // 1e20
uint256 shortAprMultiplier; // 1e20
}
struct FundingParamCallbackInput {
uint8 collateralIndex;
uint16 pairIndex;
uint224 newValue; // precision depends on parameter
}
struct TradeHoldingFees {
int256 fundingFeeCollateral; // collateral precision
uint256 borrowingFeeCollateral; // collateral precision
uint256 borrowingFeeCollateral_old; // collateral precision
int256 totalFeeCollateral; // collateral precision
}
struct RealizeTradingFeesValues {
ITradingStorage.Trade trade;
uint256 liqPrice; // 1e10
uint128 newRealizedFeesCollateral; // collateral precision
int128 newRealizedPnlCollateral; // collateral precision
uint256 amountSentFromVaultCollateral; // collateral precision
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSPairsStorage facet
*/
interface IOtc {
struct OtcStorage {
mapping(uint8 => uint256) collateralBalances; // collateralIndex => available OTC value (collateral precision)
OtcConfig otcConfig;
uint256[47] __gap;
}
struct OtcConfig {
address gnsTreasury; /// @custom:deprecated Use `AddressStore.globalAddresses.treasury` instead
uint64 treasuryShareP; // %, 1e10 precision
uint64 stakingShareP; // %, 1e10 precision
uint64 burnShareP; // %, 1e10 precision
uint64 premiumP; // %, 1e10 precision
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSPairsStorage facet
*/
interface IPairsStorage {
struct PairsStorage {
mapping(uint256 => Pair) pairs;
mapping(uint256 => Group) groups;
mapping(uint256 => Fee) fees; /// @custom:deprecated
mapping(string => mapping(string => bool)) isPairListed;
mapping(uint256 => uint256) pairCustomMaxLeverage; // 1e3 precision
uint256 currentOrderId; /// @custom:deprecated
uint256 pairsCount;
uint256 groupsCount;
uint256 feesCount;
mapping(uint256 => GroupLiquidationParams) groupLiquidationParams;
mapping(uint256 => FeeGroup) feeGroups;
GlobalTradeFeeParams globalTradeFeeParams;
mapping(uint16 => CounterTradeSettings) pairCounterTradeSettings;
mapping(uint8 => mapping(uint16 => GlobalTradeFeeParams)) pairTradeFeeParamOverrides;
mapping(uint8 => mapping(uint16 => PairFlags)) pairFlags;
uint256[35] __gap;
}
struct Pair {
string from;
string to;
Feed feed; /// @custom:deprecated
uint256 spreadP; // 1e10
uint256 groupIndex;
uint256 feeIndex;
}
struct Group {
string name;
bytes32 job; /// @custom:deprecated
uint256 minLeverage; // 1e3 precision
uint256 maxLeverage; // 1e3 precision
}
struct GlobalTradeFeeParams {
uint24 referralFeeP; // 1e3 (%)
uint24 govFeeP; // 1e3 (%)
uint24 triggerOrderFeeP; // 1e3 (%)
uint24 gnsOtcFeeP; // 1e3 (%)
uint24 gTokenFeeP; // 1e3 (%)
uint24 gTokenOcFeeP; // 1e3 (%)
uint112 __placeholder;
}
struct FeeGroup {
uint40 totalPositionSizeFeeP; // 1e10 (%)
uint40 totalLiqCollateralFeeP; // 1e10 (%)
uint40 oraclePositionSizeFeeP; // 1e10 (%)
uint32 minPositionSizeUsd; // 1e3
uint104 __placeholder;
}
struct TradeFees {
uint256 totalFeeCollateral; // collateral precision
uint256 referralFeeCollateral; // collateral precision
uint256 govFeeCollateral; // collateral precision
uint256 triggerOrderFeeCollateral; // collateral precision
uint256 gnsOtcFeeCollateral; // collateral precision
uint256 gTokenFeeCollateral; // collateral precision
uint256 gTokenOcFeeCollateral; // collateral precision
}
struct GroupLiquidationParams {
uint40 maxLiqSpreadP; // 1e10 (%)
uint40 startLiqThresholdP; // 1e10 (%)
uint40 endLiqThresholdP; // 1e10 (%)
uint24 startLeverage; // 1e3
uint24 endLeverage; // 1e3
}
struct CounterTradeSettings {
uint24 maxLeverage; // 1e3
uint16 feeRateMultiplier; // 1e3
uint216 __placeholder;
}
struct PairFlags {
bool skipBurn; // When true, skips gToken burns from losses
uint248 __placeholder;
}
// Deprecated structs
enum FeedCalculation {
DEFAULT,
INVERT,
COMBINE
} /// @custom:deprecated
struct Feed {
address feed1;
address feed2;
FeedCalculation feedCalculation;
uint256 maxDeviationP;
} /// @custom:deprecated
struct Fee {
string name;
uint256 openFeeP; // 1e10 (% of position size)
uint256 closeFeeP; // 1e10 (% of position size)
uint256 oracleFeeP; // 1e10 (% of position size)
uint256 triggerOrderFeeP; // 1e10 (% of position size)
uint256 minPositionSizeUsd; // 1e18 (collateral x leverage, useful for min fee)
} /// @custom:deprecated
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
import "./ITradingStorage.sol";
import "./ITradingCallbacks.sol";
import "../IChainlinkFeed.sol";
import "../ILiquidityPool.sol";
/**
* @dev Contains the types for the GNSPriceAggregator facet
*/
interface IPriceAggregator {
struct PriceAggregatorStorage {
IChainlinkFeed linkUsdPriceFeed;
uint24 twapInterval; // seconds
uint8 minAnswers;
uint24 maxMarketDeviationP; // 1e3 %
uint24 maxLookbackDeviationP; // 1e3 %
uint16 __placeholder;
bytes32[2] jobIds;
address[] oracles;
mapping(uint8 => LiquidityPoolInfo) collateralGnsLiquidityPools;
mapping(uint8 => IChainlinkFeed) collateralUsdPriceFeed;
mapping(bytes32 => Order) orders;
mapping(address => mapping(uint32 => uint256[])) orderAnswersLegacy; /// @custom:deprecated
// Chainlink Client state start
LinkTokenInterface linkErc677;
uint8 limitJobCount; // max value 255
uint88 limitJobIndex; // max value 3e26 runs
uint256 requestCount;
mapping(bytes32 => address) pendingRequests;
// Chainlink Client state end
bytes32 paramUpdateJobId; /// @custom:deprecated
mapping(address => mapping(uint32 => OrderAnswer[])) orderAnswers;
mapping(uint16 => OrderAnswer[]) signedOrderAnswersTemporary;
mapping(uint16 => ITradingCallbacks.AggregatorAnswer) signedMediansTemporary;
uint16[] signedPairIndicesTemporary;
uint256[34] __gap;
}
struct LiquidityPoolInfo {
ILiquidityPool pool;
bool isGnsToken0InLp;
PoolType poolType;
uint80 __placeholder;
}
struct Order {
address user;
uint32 index;
ITradingStorage.PendingOrderType orderType;
uint16 pairIndex;
bool isLookback;
uint32 __placeholder;
}
struct OrderAnswer {
/// @custom:dev max price = type(uint56.max) / 1e10 = 7,205,759
uint56 open;
uint56 high;
uint56 low;
uint56 current;
uint32 ts;
}
struct LiquidityPoolInput {
ILiquidityPool pool;
PoolType poolType;
}
struct GetPriceInput {
uint8 collateralIndex;
uint16 pairIndex;
ITradingStorage.PendingOrder pendingOrder;
uint256 positionSizeCollateral; // collateral precision
uint256 fromBlock;
bool isCounterTrade;
}
struct SignedPairPrices {
uint8 signerId; // index in oracles array of price aggregator facet
uint32 expiryTs;
bool isLookback;
uint32 fromBlock;
bytes signature;
uint16[] pairIndices;
OrderAnswer[] prices;
}
enum PoolType {
UNISWAP_V3,
ALGEBRA_v1_9,
CONSTANT_VALUE
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSPriceImpact facet
*/
interface IPriceImpact {
struct PriceImpactStorage {
OiWindowsSettings oiWindowsSettings;
mapping(uint48 => mapping(uint256 => mapping(uint256 => PairOi))) windows; // duration => pairIndex => windowId => Oi
mapping(uint256 => PairDepth) pairDepths; // pairIndex => depth (USD)
mapping(address => mapping(uint32 => TradePriceImpactInfo)) tradePriceImpactInfos; // deprecated
mapping(uint256 => PairFactors) pairFactors;
uint40 negPnlCumulVolMultiplier;
uint216 __placeholder;
mapping(address => bool) protectionCloseFactorWhitelist;
mapping(address => mapping(uint256 => UserPriceImpact)) userPriceImpact; // address => pair => UserPriceImpact
mapping(uint8 => mapping(uint16 => uint256)) pairSkewDepths; // collateral index => pairIndex => depth (tokens, 1e18)
mapping(uint8 => mapping(uint16 => PairOiCollateral)) pairOiAfterV10Collateral; // collateral index => pairIndex => open interest (collateral precision)
mapping(uint8 => mapping(uint16 => PairOiToken)) pairOiAfterV10Token; // collateral index => pairIndex => open interest (1e18)
mapping(uint256 => PairDepthBands) pairDepthBands; // pairIndex => depth bands
DepthBandsMapping depthBandsMapping; // global mapping from band indices to band percentage offsets
uint256[36] __gap;
}
struct OiWindowsSettings {
uint48 startTs;
uint48 windowsDuration;
uint48 windowsCount;
}
struct PairOi {
uint128 oiLongUsd; // 1e18 USD
uint128 oiShortUsd; // 1e18 USD
}
struct PairOiCollateral {
uint128 oiLongCollateral; // collateral precision
uint128 oiShortCollateral; // collateral precision
}
struct PairOiToken {
uint128 oiLongToken; // 1e18
uint128 oiShortToken; // 1e18
}
struct OiWindowUpdate {
address trader;
uint32 index;
uint48 windowsDuration;
uint256 pairIndex;
uint256 windowId;
bool long;
bool open;
bool isPnlPositive;
uint128 openInterestUsd; // 1e18 USD
}
struct PairDepth {
uint128 onePercentDepthAboveUsd; // USD
uint128 onePercentDepthBelowUsd; // USD
}
struct PairFactors {
uint40 protectionCloseFactor; // 1e10; max 109.95x
uint32 protectionCloseFactorBlocks;
uint40 cumulativeFactor; // 1e10; max 109.95x
bool exemptOnOpen;
bool exemptAfterProtectionCloseFactor;
uint128 __placeholder;
}
struct UserPriceImpact {
uint16 cumulVolPriceImpactMultiplier; // 1e3
uint16 fixedSpreadP; // 1e3 %
uint224 __placeholder;
}
struct PriceImpactValues {
PairFactors pairFactors;
bool protectionCloseFactorWhitelist;
UserPriceImpact userPriceImpact;
bool protectionCloseFactorActive;
uint256 depth; // USD
bool tradePositiveSkew;
int256 tradeSkewMultiplier;
int256 priceImpactDivider;
}
/**
* @dev Each slot encodes cumulative liquidity percentages for specific bands.
* Percentages are in basis points (eg. 10,000 = 100% => percentages with 2 decimals).
* Max value: 65535/1e2 = 655.35%
*/
struct PairDepthBands {
uint256 aboveSlot1; // totalDepthUsd (uint32, no decimals) + 14 x uint16 band cumulative liquidity percentages
uint256 aboveSlot2; // 16 x uint16 band cumulative liquidity percentages
uint256 belowSlot1; // totalDepthUsd (uint32, no decimals) + 14 x uint16 band cumulative liquidity percentages
uint256 belowSlot2; // 16 x uint16 band cumulative liquidity percentages
}
/**
* @dev Each slot encodes cumulative offset percentages from mid price for specific bands.
* Same encoding as PairDepthBands, but totalDepthUsd is zeroed.
* Percentages are in parts per million (eg. 1,000,000 = 100% => percentages with 4 decimals).
* Max value: 65535/1e4 = 6.5535%
*/
struct DepthBandsMapping {
uint256 slot1; // first 32 bits empty, 14 x uint16 band offset percentages
uint256 slot2; // 16 x uint16 band offset percentages
}
// Working struct for convenience
struct DepthBandParameters {
uint256 pairSlot1;
uint256 pairSlot2;
uint256 mappingSlot1;
uint256 mappingSlot2;
}
// Deprecated
struct TradePriceImpactInfo {
uint128 lastWindowOiUsd;
uint128 __placeholder;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSReferrals facet
*/
interface IReferrals {
struct ReferralsStorage {
mapping(address => AllyDetails) allyDetails;
mapping(address => ReferrerDetails) referrerDetails;
mapping(address => address) referrerByTrader;
uint256 allyFeeP; // % (of referrer fees going to allies, eg. 10)
uint256 startReferrerFeeP; // % (of referrer fee when 0 volume referred, eg. 75)
uint256 openFeeP; /// @custom:deprecated
uint256 targetVolumeUsd; // USD (to reach maximum referral system fee, eg. 1e8)
mapping(address => ReferralSettingsOverrides) referralSettingsOverrides;
uint256[42] __gap;
}
struct AllyDetails {
address[] referrersReferred;
uint256 volumeReferredUsd; // 1e18
uint256 pendingRewardsGns; // 1e18
uint256 totalRewardsGns; // 1e18
uint256 totalRewardsValueUsd; // 1e18
bool active;
}
struct ReferrerDetails {
address ally;
address[] tradersReferred;
uint256 volumeReferredUsd; // 1e18
uint256 pendingRewardsGns; // 1e18
uint256 totalRewardsGns; // 1e18
uint256 totalRewardsValueUsd; // 1e18
bool active;
}
struct ReferralSettingsOverrides {
uint24 referralFeeOverrideP; // % of total trading fee (1e3), 0 means it uses globalTradeFeeParams value
uint24 allyFeeOverrideP; // % of total trading fee (1e3), 0 means it uses default allyFeeP value
uint208 __placeholder;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../types/ITradingStorage.sol";
import "../libraries/ITradingCommonUtils.sol";
/**
* @dev Contains the types for the GNSTradingCallbacks facet
*/
interface ITradingCallbacks {
struct TradingCallbacksStorage {
uint8 vaultClosingFeeP;
uint248 __placeholder;
mapping(uint8 => uint256) pendingGovFees; // collateralIndex => pending gov fee (collateral)
uint256[48] __gap;
}
enum CancelReason {
NONE,
PAUSED, // deprecated
MARKET_CLOSED, // deprecated
SLIPPAGE,
TP_REACHED,
SL_REACHED,
EXPOSURE_LIMITS,
PRICE_IMPACT,
MAX_LEVERAGE,
NO_TRADE,
WRONG_TRADE,
NOT_HIT,
LIQ_REACHED,
COUNTER_TRADE_CANCELED
}
struct AggregatorAnswer {
ITradingStorage.Id orderId;
uint64 open;
uint64 high;
uint64 low;
uint64 current;
}
// Useful to avoid stack too deep errors
struct Values {
int256 profitP;
uint256 executionPrice;
uint256 executionPriceRaw;
uint256 liqPrice;
uint256 amountSentToTrader;
uint256 collateralPriceUsd;
bool exactExecution;
uint32 limitIndex;
ITradingCommonUtils.TradePriceImpact priceImpact;
CancelReason cancelReason;
uint256 collateralToReturn;
uint120 newCollateralAmount;
uint256 newEffectiveLeverage;
uint256 pnlWithdrawnCollateral;
uint256 openingFeeCollateral;
}
struct PnlWithdrawalValues {
ITradingStorage.Trade trade;
uint64 currentPairPrice;
uint256 positionSizeCollateral;
ITradingCommonUtils.TradePriceImpact priceImpact;
int256 pnlPercent;
int256 withdrawablePositivePnlCollateral;
uint256 pnlInputCollateral;
uint256 pnlWithdrawnCollateral;
uint256 withdrawablePositivePnlCollateralUint;
uint256 finalGovFeeCollateral;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSTradingInteractions facet
*/
interface ITradingInteractions {
struct TradingInteractionsStorage {
address senderOverride; // 160 bits
uint16 marketOrdersTimeoutBlocks; // 16 bits
uint80 __placeholder;
mapping(address => address) delegations;
mapping(address => bool) byPassTriggerLink;
uint256[47] __gap;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./IPairsStorage.sol";
/**
* @dev Contains the types for the GNSTradingStorage facet
*/
interface ITradingStorage {
struct TradingStorage {
TradingActivated tradingActivated; // 8 bits
uint8 lastCollateralIndex; // 8 bits
uint8 gnsCollateralIndex; // 8 bits
uint232 __placeholder; // 232 bits
mapping(uint8 => Collateral) collaterals;
mapping(uint8 => address) gTokens;
mapping(address => uint8) collateralIndex;
mapping(address => mapping(uint32 => Trade)) trades;
mapping(address => mapping(uint32 => TradeInfo)) tradeInfos;
mapping(address => mapping(uint32 => mapping(PendingOrderType => uint256))) tradePendingOrderBlock;
mapping(address => mapping(uint32 => PendingOrder)) pendingOrders;
mapping(address => mapping(CounterType => Counter)) userCounters;
address[] traders;
mapping(address => bool) traderStored;
mapping(address => mapping(uint32 => IPairsStorage.GroupLiquidationParams)) tradeLiquidationParams;
uint256[38] __gap;
}
enum PendingOrderType {
MARKET_OPEN,
MARKET_CLOSE,
LIMIT_OPEN,
STOP_OPEN,
TP_CLOSE,
SL_CLOSE,
LIQ_CLOSE,
UPDATE_LEVERAGE,
MARKET_PARTIAL_OPEN,
MARKET_PARTIAL_CLOSE,
PARAM_UPDATE, // deprecated
PNL_WITHDRAWAL,
MANUAL_HOLDING_FEES_REALIZATION,
MANUAL_NEGATIVE_PNL_REALIZATION
}
enum CounterType {
TRADE,
PENDING_ORDER
}
enum TradeType {
TRADE,
LIMIT,
STOP
}
enum TradingActivated {
ACTIVATED,
CLOSE_ONLY,
PAUSED
}
enum ContractsVersion {
BEFORE_V9_2,
V9_2,
V10
}
struct Collateral {
// slot 1
address collateral; // 160 bits
bool isActive; // 8 bits
uint88 __placeholder; // 88 bits
// slot 2
uint128 precision;
uint128 precisionDelta;
}
struct Id {
address user; // 160 bits
uint32 index; // max: 4,294,967,295
}
struct Trade {
// slot 1
address user; // 160 bits
uint32 index; // max: 4,294,967,295
uint16 pairIndex; // max: 65,535
uint24 leverage; // 1e3; max: 16,777.215
bool long; // 8 bits
bool isOpen; // 8 bits
uint8 collateralIndex; // max: 255
// slot 2
TradeType tradeType; // 8 bits
uint120 collateralAmount; // collateral precision; max: 3.402e+38
uint64 openPrice; // 1e10; max: 1.8e19
uint64 tp; // 1e10; max: 1.8e19
// slot 3
uint64 sl; // 1e10; max: 1.8e19
bool isCounterTrade;
/// @dev Warning this position size in token amount is calculated using position size collateral / entry price, without taking into account skew price impact
/// At the moment it is only used to calculate skew price impact, once we refactor formulas to use it for pnl for example we'll need to re-calculate it based on real entry price
/// And once we refactor to USD position sizes we'll have to convert it from collateral to USD
uint160 positionSizeToken; // 1e18, assuming 1e-9 collateral price, 1e-9 pair price => max value is 1.46 trillion USD (1e12)
uint24 __placeholder;
}
struct TradeInfo {
uint32 createdBlock; // for lookbacks
uint32 tpLastUpdatedBlock; // for lookbacks
uint32 slLastUpdatedBlock; // for lookbacks
uint16 maxSlippageP; // 1e3 (%)
uint48 lastOiUpdateTs; // deprecated
uint48 collateralPriceUsd; // 1e8 collateral price at trade open
ContractsVersion contractsVersion;
uint32 lastPosIncreaseBlock; // for protection close factor
uint8 __placeholder;
}
struct PendingOrder {
// slots 1-3
Trade trade;
// slot 4
address user; // 160 bits
uint32 index; // max: 4,294,967,295
bool isOpen; // 8 bits
PendingOrderType orderType; // 8 bits
uint32 createdBlock; // max: 4,294,967,295
uint16 maxSlippageP; // 1e3 (%), max: 65.535%
}
struct Counter {
uint32 currentIndex;
uint32 openCount;
uint192 __placeholder;
}
struct UpdateTradePositionValues {
uint32 blockNumber;
uint64 newSl;
uint160 newPositionSizeToken;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev Contains the types for the GNSTriggerRewards facet
*/
interface ITriggerRewards {
struct TriggerRewardsStorage {
uint16 triggerTimeoutBlocks; // 16 bits
uint240 __placeholder; // 240 bits
mapping(address => uint256) pendingRewardsGns;
uint256[48] __gap;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./IDiamondStorage.sol";
import "./IPairsStorage.sol";
import "./IReferrals.sol";
import "./IFeeTiers.sol";
import "./IPriceImpact.sol";
import "./ITradingStorage.sol";
import "./ITriggerRewards.sol";
import "./ITradingInteractions.sol";
import "./ITradingCallbacks.sol";
import "./IBorrowingFees.sol";
import "./IPriceAggregator.sol";
import "./IOtc.sol";
import "./IChainConfig.sol";
import "./IFundingFees.sol";
/**
* @dev Contains the types of all diamond facets
*/
interface ITypes is
IDiamondStorage,
IPairsStorage,
IReferrals,
IFeeTiers,
IPriceImpact,
ITradingStorage,
ITriggerRewards,
ITradingInteractions,
ITradingCallbacks,
IBorrowingFees,
IPriceAggregator,
IOtc,
IChainConfig,
IFundingFees
{
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
*
* @dev Interface for leverage updates types
*/
interface IUpdateLeverage {
/// @dev Update leverage input values
struct UpdateLeverageInput {
address user;
uint32 index;
uint24 newLeverage; // 1e3
}
/// @dev Useful values for increase leverage callback
struct UpdateLeverageValues {
uint256 newLeverage; // 1e3
uint256 newCollateralAmount; // collateral precision
uint256 existingLiqPrice; // 1e10
uint256 newLiqPrice; // 1e10
uint256 govFeeCollateral; // collateral precision
uint256 newEffectiveLeverage; // 1e3
uint256 totalTradeAvailableCollateralInDiamond; // collateral precision
uint256 availableCollateralInDiamond; // collateral precision
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../libraries/ITradingCommonUtils.sol";
/**
*
* @dev Interface for position size updates types
*/
interface IUpdatePositionSize {
/// @dev Request decrease position input values
struct DecreasePositionSizeInput {
address user;
uint32 index;
uint120 collateralDelta; // collateral precision
uint24 leverageDelta; // 1e3
uint64 expectedPrice; // 1e10
}
/// @dev Request increase position input values
struct IncreasePositionSizeInput {
address user;
uint32 index;
uint120 collateralDelta; // collateral precision
uint24 leverageDelta; // 1e3
uint64 expectedPrice; // 1e10
uint16 maxSlippageP; // 1e3 (%)
}
/// @dev Useful values for decrease position size callback
struct DecreasePositionSizeValues {
bool isLeverageUpdate;
uint256 positionSizeCollateralDelta; // collateral precision
uint256 existingPositionSizeCollateral; // collateral precision
uint256 existingLiqPrice; // 1e10
uint256 newLiqPrice; // 1e10
ITradingCommonUtils.TradePriceImpact priceImpact;
int256 existingPnlPercent; // 1e10 (%)
int256 partialRawPnlCollateral; // collateral precision
int256 partialNetPnlCollateral; // collateral precision
int256 pnlToRealizeCollateral; // collateral precision
uint256 closingFeeCollateral; // collateral precision
uint256 totalAvailableCollateralInDiamond; // collateral precision
int256 availableCollateralInDiamond; // collateral precision
int256 collateralSentToTrader; // collateral precision
uint120 newCollateralAmount; // collateral precision
uint24 newLeverage; // 1e3
}
/// @dev Useful values for increase position size callback
struct IncreasePositionSizeValues {
uint256 positionSizeCollateralDelta; // collateral precision
uint256 existingPositionSizeCollateral; // collateral precision
uint256 newPositionSizeCollateral; // collateral precision
uint256 newCollateralAmount; // collateral precision
uint256 newLeverage; // 1e3
ITradingCommonUtils.TradePriceImpact priceImpact;
int256 existingPnlCollateral; // collateral precision
uint256 oldPosSizePlusPnlCollateral; // collateral precision
uint256 newOpenPrice; // 1e10
uint256 openingFeesCollateral; // collateral precision
uint256 existingLiqPrice; // 1e10
uint256 newLiqPrice; // 1e10
bool isCounterTradeValidated;
uint256 exceedingPositionSizeCollateral; // collateral precision
uint256 counterTradeCollateralToReturn; // collateral precision
uint256 newEffectiveLeverage; // 1e3
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../interfaces/types/IAddressStore.sol";
import "./StorageUtils.sol";
/**
*
* @dev GNSAddressStore facet internal library
*/
library AddressStoreUtils {
/**
* @dev Returns storage slot to use when fetching addresses
*/
function _getSlot() internal pure returns (uint256) {
return StorageUtils.GLOBAL_ADDRESSES_SLOT;
}
/**
* @dev Returns storage pointer for Addresses struct in global diamond contract, at defined slot
*/
function getAddresses() internal pure returns (IAddressStore.Addresses storage s) {
uint256 storageSlot = _getSlot();
assembly {
s.slot := storageSlot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../interfaces/IGeneralErrors.sol";
import "../interfaces/libraries/IChainConfigUtils.sol";
import "./StorageUtils.sol";
/**
* @dev ChainConfig facet internal library
*/
library ChainConfigUtils {
uint16 internal constant MIN_NATIVE_TRANSFER_GAS_LIMIT = 21_000;
/**
* @dev Check IChainConfig interface for documentation
*/
function initializeChainConfig(uint16 _nativeTransferGasLimit, bool _nativeTransferEnabled) internal {
updateNativeTransferGasLimit(_nativeTransferGasLimit);
updateNativeTransferEnabled(_nativeTransferEnabled);
}
/**
* @dev Check IChainConfigUtils interface for documentation
*/
function updateNativeTransferGasLimit(uint16 _nativeTransferGasLimit) internal {
if (_nativeTransferGasLimit < MIN_NATIVE_TRANSFER_GAS_LIMIT) revert IGeneralErrors.BelowMin();
_getStorage().nativeTransferGasLimit = _nativeTransferGasLimit;
emit IChainConfigUtils.NativeTransferGasLimitUpdated(_nativeTransferGasLimit);
}
/**
* @dev Check IChainConfigUtils interface for documentation
*/
function updateNativeTransferEnabled(bool _nativeTransferEnabled) internal {
_getStorage().nativeTransferEnabled = _nativeTransferEnabled;
emit IChainConfigUtils.NativeTransferEnabledUpdated(_nativeTransferEnabled);
}
/**
* @dev Check IChainConfigUtils interface for documentation
*/
function getNativeTransferGasLimit() internal view returns (uint16) {
uint16 gasLimit = _getStorage().nativeTransferGasLimit;
// If `nativeTransferGasLimit` is 0 (not yet initialized) then return `MIN_NATIVE_TRANSFER_GAS_LIMIT
return gasLimit == 0 ? MIN_NATIVE_TRANSFER_GAS_LIMIT : gasLimit;
}
/**
* @dev Check IChainConfigUtils interface for documentation
*/
function getNativeTransferEnabled() internal view returns (bool) {
return _getStorage().nativeTransferEnabled;
}
/**
* @dev Check IChainConfigUtils interface for documentation
*/
function getReentrancyLock() internal view returns (uint256) {
return _getStorage().reentrancyLock;
}
/**
* @dev Returns storage slot to use when fetching storage relevant to library
*/
function _getSlot() internal pure returns (uint256) {
return StorageUtils.GLOBAL_CHAIN_CONFIG_SLOT;
}
/**
* @dev Returns storage pointer for storage struct in diamond contract, at defined slot
*/
function _getStorage() internal pure returns (IChainConfig.ChainConfigStorage storage s) {
uint256 storageSlot = _getSlot();
assembly {
s.slot := storageSlot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import "../interfaces/IArbSys.sol";
import "../interfaces/IGeneralErrors.sol";
import "../interfaces/mock/IBlockManager_Mock.sol";
/**
* @dev Chain helpers internal library
*/
library ChainUtils {
// Supported chains
uint256 internal constant ARBITRUM_MAINNET = 42161;
uint256 internal constant ARBITRUM_SEPOLIA = 421614;
uint256 internal constant POLYGON_MAINNET = 137;
uint256 internal constant BASE_MAINNET = 8453;
uint256 internal constant APECHAIN_MAINNET = 33139;
uint256 internal constant TESTNET = 31337;
// Wrapped native tokens
address private constant ARBITRUM_MAINNET_WETH = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
address private constant ARBITRUM_SEPOLIA_WETH = 0x980B62Da83eFf3D4576C647993b0c1D7faf17c73;
address private constant POLYGON_MAINNET_WMATIC = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;
address private constant BASE_MAINNET_WETH = 0x4200000000000000000000000000000000000006;
address private constant APECHAIN_MAINNET_WAPE = 0x00000000000f7e000644657dC9417b185962645a; // Custom non-rebasing WAPE
IArbSys private constant ARB_SYS = IArbSys(address(100));
/**
* @dev Returns the current block number (l2 block for arbitrum)
*/
function getBlockNumber() internal view returns (uint256) {
if (
block.chainid == ARBITRUM_MAINNET || block.chainid == APECHAIN_MAINNET || block.chainid == ARBITRUM_SEPOLIA
) {
return ARB_SYS.arbBlockNumber();
}
if (block.chainid == TESTNET) {
return IBlockManager_Mock(address(420)).getBlockNumber();
}
return block.number;
}
/**
* @dev Returns blockNumber converted to uint48
* @param blockNumber block number to convert
*/
function getUint48BlockNumber(uint256 blockNumber) internal pure returns (uint48) {
if (blockNumber > type(uint48).max) revert IGeneralErrors.Overflow();
return uint48(blockNumber);
}
/**
* @dev Returns the wrapped native token address for the current chain
*/
function getWrappedNativeToken() internal view returns (address) {
if (block.chainid == ARBITRUM_MAINNET) {
return ARBITRUM_MAINNET_WETH;
}
if (block.chainid == BASE_MAINNET) {
return BASE_MAINNET_WETH;
}
if (block.chainid == APECHAIN_MAINNET) {
return APECHAIN_MAINNET_WAPE;
}
if (block.chainid == POLYGON_MAINNET) {
return POLYGON_MAINNET_WMATIC;
}
if (block.chainid == ARBITRUM_SEPOLIA) {
return ARBITRUM_SEPOLIA_WETH;
}
if (block.chainid == TESTNET) {
return address(421);
}
return address(0);
}
/**
* @dev Returns whether a token is the wrapped native token for the current chain
* @param _token token address to check
*/
function isWrappedNativeToken(address _token) internal view returns (bool) {
return _token != address(0) && _token == getWrappedNativeToken();
}
/**
* @dev Converts blocks to seconds for the current chain.
* @dev Important: the result is an estimation and may not be accurate. Use with caution.
* @param _blocks block count to convert to seconds
*/
function convertBlocksToSeconds(uint256 _blocks) internal view returns (uint256) {
uint256 millisecondsPerBlock;
if (block.chainid == ARBITRUM_MAINNET || block.chainid == ARBITRUM_SEPOLIA) {
millisecondsPerBlock = 300; // 0.3 seconds per block
} else if (block.chainid == BASE_MAINNET) {
millisecondsPerBlock = 2000; // 2 seconds per block
} else if (block.chainid == POLYGON_MAINNET) {
millisecondsPerBlock = 2200; // 2.2 seconds per block
} else if (block.chainid == APECHAIN_MAINNET) {
millisecondsPerBlock = 1000; // apechain blocktime is variable; this will be updated over time as averages change
} else if (block.chainid == TESTNET) {
millisecondsPerBlock = 1000; // 1 second per block
} else {
revert IGeneralErrors.UnsupportedChain();
}
return Math.mulDiv(_blocks, millisecondsPerBlock, 1000, Math.Rounding.Up);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../interfaces/types/ITradingStorage.sol";
/**
*
* @dev Internal library for important constants commonly used in many places
*/
library ConstantsUtils {
uint256 internal constant P_10 = 1e10; // 10 decimals (DO NOT UPDATE)
uint256 internal constant SL_LIQ_BUFFER_P = 5 * P_10; // SL has to be 5% closer than liq price
uint256 internal constant LEGACY_LIQ_THRESHOLD_P = 90 * P_10; // -90% pnl
uint256 internal constant MIN_LIQ_THRESHOLD_P = 50 * P_10; // -50% pnl
uint256 internal constant MAX_OPEN_NEGATIVE_PNL_P = 40 * P_10; // -40% pnl
uint256 internal constant MAX_LIQ_SPREAD_P = (5 * P_10) / 100; // 0.05%
uint16 internal constant DEFAULT_MAX_CLOSING_SLIPPAGE_P = 1 * 1e3; // 1%
uint24 internal constant MAX_REFERRAL_FEE_P = 50e3;
uint24 internal constant MIN_LEVERAGE = 0.1e3; // 0.1x
/**
* @dev Returns market order types
*/
function getMarketOrderTypes() internal pure returns (ITradingStorage.PendingOrderType[9] memory) {
return [
ITradingStorage.PendingOrderType.MARKET_OPEN,
ITradingStorage.PendingOrderType.MARKET_CLOSE,
ITradingStorage.PendingOrderType.UPDATE_LEVERAGE,
ITradingStorage.PendingOrderType.MARKET_PARTIAL_OPEN,
ITradingStorage.PendingOrderType.MARKET_PARTIAL_CLOSE,
ITradingStorage.PendingOrderType.PARAM_UPDATE,
ITradingStorage.PendingOrderType.PNL_WITHDRAWAL,
ITradingStorage.PendingOrderType.MANUAL_HOLDING_FEES_REALIZATION,
ITradingStorage.PendingOrderType.MANUAL_NEGATIVE_PNL_REALIZATION
];
}
/**
* @dev Returns pending order type (market open/limit open/stop open) for a trade type (trade/limit/stop)
* @param _tradeType the trade type
*/
function getPendingOpenOrderType(
ITradingStorage.TradeType _tradeType
) internal pure returns (ITradingStorage.PendingOrderType) {
return
_tradeType == ITradingStorage.TradeType.TRADE
? ITradingStorage.PendingOrderType.MARKET_OPEN
: _tradeType == ITradingStorage.TradeType.LIMIT
? ITradingStorage.PendingOrderType.LIMIT_OPEN
: ITradingStorage.PendingOrderType.STOP_OPEN;
}
/**
* @dev Returns true if order type is market
* @param _orderType order type
*/
function isOrderTypeMarket(ITradingStorage.PendingOrderType _orderType) internal pure returns (bool) {
ITradingStorage.PendingOrderType[9] memory marketOrderTypes = ConstantsUtils.getMarketOrderTypes();
for (uint256 i; i < marketOrderTypes.length; ++i) {
if (_orderType == marketOrderTypes[i]) return true;
}
return false;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
* @dev External library used to pack and unpack values
*/
library PackingUtils {
/**
* @dev Packs values array into a single uint256
* @param _values values to pack
* @param _bitLengths corresponding bit lengths for each value
*/
function pack(uint256[] memory _values, uint256[] memory _bitLengths) external pure returns (uint256 packed) {
require(_values.length == _bitLengths.length, "Mismatch in the lengths of values and bitLengths arrays");
uint256 currentShift;
for (uint256 i; i < _values.length; ++i) {
require(currentShift + _bitLengths[i] <= 256, "Packed value exceeds 256 bits");
uint256 maxValue = (1 << _bitLengths[i]) - 1;
require(_values[i] <= maxValue, "Value too large for specified bit length");
uint256 maskedValue = _values[i] & maxValue;
packed |= maskedValue << currentShift;
currentShift += _bitLengths[i];
}
}
/**
* @dev Unpacks a single uint256 into an array of values
* @param _packed packed value
* @param _bitLengths corresponding bit lengths for each value
*/
function unpack(uint256 _packed, uint256[] memory _bitLengths) external pure returns (uint256[] memory values) {
values = new uint256[](_bitLengths.length);
uint256 currentShift;
for (uint256 i; i < _bitLengths.length; ++i) {
require(currentShift + _bitLengths[i] <= 256, "Unpacked value exceeds 256 bits");
uint256 maxValue = (1 << _bitLengths[i]) - 1;
uint256 mask = maxValue << currentShift;
values[i] = (_packed & mask) >> currentShift;
currentShift += _bitLengths[i];
}
}
/**
* @dev Unpacks aggregator answer
* @param _packed packed value
* @return current current price (1e10)
* @return open open price (1e10)
* @return high high price (1e10)
* @return low low price (1e10)
* @return ts timestamp
*/
function unpackAggregatorAnswer(
uint256 _packed
) external pure returns (uint56 current, uint56 open, uint56 high, uint56 low, uint32 ts) {
current = uint56(_packed);
open = uint56(_packed >> 56);
high = uint56(_packed >> 112);
low = uint56(_packed >> 168);
ts = uint32(_packed >> 224);
}
/**
* @dev Unpacks trigger order calldata into 3 values
* @param _packed packed value
* @return orderType order type
* @return trader trader address
* @return index trade index
*/
function unpackTriggerOrder(uint256 _packed) external pure returns (uint8 orderType, address trader, uint32 index) {
orderType = uint8(_packed & 0xFF); // 8 bits
trader = address(uint160(_packed >> 8)); // 160 bits
index = uint32((_packed >> 168)); // 32 bits
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/**
*
* @dev Internal library to manage storage slots of GNSMultiCollatDiamond contract diamond storage structs.
*
* BE EXTREMELY CAREFUL, DO NOT EDIT THIS WITHOUT A GOOD REASON
*
*/
library StorageUtils {
uint256 internal constant GLOBAL_ADDRESSES_SLOT = 3;
uint256 internal constant GLOBAL_PAIRS_STORAGE_SLOT = 51;
uint256 internal constant GLOBAL_REFERRALS_SLOT = 101;
uint256 internal constant GLOBAL_FEE_TIERS_SLOT = 151;
uint256 internal constant GLOBAL_PRICE_IMPACT_SLOT = 201;
uint256 internal constant GLOBAL_DIAMOND_SLOT = 251;
uint256 internal constant GLOBAL_TRADING_STORAGE_SLOT = 301;
uint256 internal constant GLOBAL_TRIGGER_REWARDS_SLOT = 351;
uint256 internal constant GLOBAL_TRADING_SLOT = 401;
uint256 internal constant GLOBAL_TRADING_CALLBACKS_SLOT = 451;
uint256 internal constant GLOBAL_BORROWING_FEES_SLOT = 501;
uint256 internal constant GLOBAL_PRICE_AGGREGATOR_SLOT = 551;
uint256 internal constant GLOBAL_OTC_SLOT = 601;
uint256 internal constant GLOBAL_CHAIN_CONFIG_SLOT = 651;
uint256 internal constant GLOBAL_FUNDING_FEES_SLOT = 701;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IWETH9.sol";
import "../interfaces/IERC20.sol";
/**
* @dev Library to handle transfers of tokens, including native tokens.
*/
library TokenTransferUtils {
using SafeERC20 for IERC20;
/**
* @dev Unwraps and transfers `_amount` of native tokens to a recipient, `_to`.
*
* IMPORTANT:
* If the recipient does not accept the native transfer then the tokens are re-wrapped and transferred as ERC20.
* Always ensure CEI pattern is followed or reentrancy guards are in place before performing native transfers.
*
* @param _token the wrapped native token address
* @param _to the recipient
* @param _amount the amount of tokens to transfer
* @param _gasLimit how much gas to forward.
*/
function unwrapAndTransferNative(address _token, address _to, uint256 _amount, uint256 _gasLimit) internal {
// 1. Unwrap `_amount` of `_token`
IWETH9(_token).withdraw(_amount);
// 2. Attempt to transfer native tokens
// Uses low-level call and loads no return data into memory to prevent `returnbomb` attacks
// See https://gist.github.com/pcaversaccio/3b487a24922c839df22f925babd3c809 for an example
bool success;
assembly {
// call(gas, address, value, argsOffset, argsSize, retOffset, retSize)
success := call(_gasLimit, _to, _amount, 0, 0, 0, 0)
}
// 3. If the native transfer was successful, return
if (success) return;
// 4. Otherwise re-wrap `_amount` of `_token`
IWETH9(_token).deposit{value: _amount}();
// 5. Send with an ERC20 transfer
transfer(_token, _to, _amount);
}
/**
* @dev Transfers `_amount` of `_token` to a recipient, `to`
* @param _token the token address
* @param _to the recipient
* @param _amount amount of tokens to transfer
*/
function transfer(address _token, address _to, uint256 _amount) internal {
IERC20(_token).safeTransfer(_to, _amount);
}
/**
* @dev Transfers `_amount` of `_token` from a sender, `_from`, to a recipient, `to`.
* @param _token the token address
* @param _from the sender
* @param _to the recipient
* @param _amount amount of tokens to transfer
*/
function transferFrom(address _token, address _from, address _to, uint256 _amount) internal {
IERC20(_token).safeTransferFrom(_from, _to, _amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../interfaces/IGNSMultiCollatDiamond.sol";
import "./StorageUtils.sol";
import "./AddressStoreUtils.sol";
import "./TradingCommonUtils.sol";
/**
* @dev GNSTradingCallbacks facet external library #2
*/
library TradeManagementCallbacksUtils {
/**
* @dev Executes pending order callback for pnl withdrawal
* @param _order Pending order
* @param _a Aggregator answer
*/
function executePnlWithdrawalCallback(
ITradingStorage.PendingOrder memory _order,
ITradingCallbacks.AggregatorAnswer memory _a
) external {
ITradingCallbacks.PnlWithdrawalValues memory v;
v.trade = _getTrade(_order.trade.user, _order.trade.index);
v.currentPairPrice = _a.current;
if (!v.trade.isOpen) return;
TradingCommonUtils.updateFeeTierPoints(v.trade.collateralIndex, v.trade.user, v.trade.pairIndex, 0);
v.positionSizeCollateral = TradingCommonUtils.getPositionSizeCollateral(
v.trade.collateralAmount,
v.trade.leverage
);
(v.priceImpact, ) = TradingCommonUtils.getTradeClosingPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(
v.trade,
v.currentPairPrice,
v.positionSizeCollateral,
v.currentPairPrice,
true
)
);
v.pnlPercent = TradingCommonUtils.getPnlPercent(
v.trade.openPrice,
v.priceImpact.priceAfterImpact,
v.trade.long,
v.trade.leverage
);
v.withdrawablePositivePnlCollateral =
int256(
TradingCommonUtils.getTradeValueCollateral(
v.trade,
v.pnlPercent,
TradingCommonUtils.getTotalTradeFeesCollateral(
v.trade.collateralIndex,
v.trade.user,
v.trade.pairIndex,
v.positionSizeCollateral,
v.trade.isCounterTrade
),
v.currentPairPrice
)
) -
int256(uint256(v.trade.collateralAmount));
v.pnlInputCollateral = _order.trade.collateralAmount;
v.withdrawablePositivePnlCollateralUint = v.withdrawablePositivePnlCollateral > 0
? uint256(v.withdrawablePositivePnlCollateral)
: 0;
v.pnlWithdrawnCollateral = v.pnlInputCollateral > v.withdrawablePositivePnlCollateralUint
? v.withdrawablePositivePnlCollateralUint
: v.pnlInputCollateral;
if (v.pnlWithdrawnCollateral > 0) {
_getMultiCollatDiamond().realizePnlOnOpenTrade(
v.trade.user,
v.trade.index,
-int256(v.pnlWithdrawnCollateral)
);
TradingCommonUtils.receiveCollateralFromVault(v.trade.collateralIndex, v.pnlWithdrawnCollateral);
TradingCommonUtils.transferCollateralTo(v.trade.collateralIndex, v.trade.user, v.pnlWithdrawnCollateral);
_getMultiCollatDiamond().storeUiPnlWithdrawnCollateral(
v.trade.user,
v.trade.index,
v.pnlWithdrawnCollateral
);
}
v.finalGovFeeCollateral = _getMultiCollatDiamond().realizeTradingFeesOnOpenTrade(
v.trade.user,
v.trade.index,
TradingCommonUtils.getMinGovFeeCollateral(v.trade.collateralIndex, v.trade.user, v.trade.pairIndex),
v.currentPairPrice
);
TradingCommonUtils.distributeExactGovFeeCollateral(
v.trade.collateralIndex,
v.trade.user,
v.finalGovFeeCollateral
);
emit ITradingCallbacksUtils.TradePositivePnlWithdrawn(
_a.orderId,
v.trade.collateralIndex,
v.trade.user,
v.trade.index,
v.priceImpact,
v.pnlPercent,
v.withdrawablePositivePnlCollateral,
v.currentPairPrice,
v.pnlInputCollateral,
v.pnlWithdrawnCollateral
);
}
/**
* @dev Executes pending order callback for holding fees realization
* @param _order Pending order
* @param _a Aggregator answer
*/
function executeManualHoldingFeesRealizationCallback(
ITradingStorage.PendingOrder memory _order,
ITradingCallbacks.AggregatorAnswer memory _a
) external {
ITradingStorage.Trade memory trade = _getTrade(_order.trade.user, _order.trade.index);
if (!trade.isOpen) return;
_getMultiCollatDiamond().realizeHoldingFeesOnOpenTrade(trade.user, trade.index, _a.current);
_getMultiCollatDiamond().storeTradeInitialAccFees(
trade.user,
trade.index,
trade.collateralIndex,
trade.pairIndex,
trade.long,
_a.current
);
_getMultiCollatDiamond().handleTradeBorrowingCallback(
trade.collateralIndex,
trade.user,
trade.pairIndex,
trade.index,
0, // no OI change
false, // doesn't matter since pos size = 0
trade.long,
_a.current
);
emit ITradingCallbacksUtils.TradeHoldingFeesManuallyRealized(
_a.orderId,
trade.collateralIndex,
trade.user,
trade.index,
_a.current
);
}
/**
* @dev Executes pending order callback for negative pnl realization
* @param _order Pending order
* @param _a Aggregator answer
*/
function executeManualNegativePnlRealizationCallback(
ITradingStorage.PendingOrder memory _order,
ITradingCallbacks.AggregatorAnswer memory _a
) external {
ITradingStorage.Trade memory trade = _getTrade(_order.trade.user, _order.trade.index);
if (!trade.isOpen) return;
(ITradingCommonUtils.TradePriceImpact memory priceImpact, ) = TradingCommonUtils.getTradeClosingPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(
trade,
_a.current,
TradingCommonUtils.getPositionSizeCollateral(trade.collateralAmount, trade.leverage),
_a.current,
false // don't use cumulative volume price impact
)
);
IFundingFees.TradeFeesData memory tradeFeesData = _getMultiCollatDiamond().getTradeFeesData(
trade.user,
trade.index
);
int256 totalPnlCollateral = TradingCommonUtils.getTradeUnrealizedRawPnlCollateral(
trade,
priceImpact.priceAfterImpact
) +
tradeFeesData.realizedPnlCollateral +
int256(uint256(tradeFeesData.alreadyTransferredNegativePnlCollateral));
uint256 totalNegativePnlCollateral = totalPnlCollateral < 0 ? uint256(-totalPnlCollateral) : uint256(0);
uint128 existingManuallyRealizedNegativePnlCollateral = tradeFeesData.manuallyRealizedNegativePnlCollateral;
uint256 newManuallyRealizedNegativePnlCollateral = existingManuallyRealizedNegativePnlCollateral;
if (totalNegativePnlCollateral > existingManuallyRealizedNegativePnlCollateral) {
uint256 maxRealizableNegativePnlCollateral = TradingCommonUtils.getTradeAvailableCollateralInDiamond(
trade.user,
trade.index,
trade.collateralAmount
);
uint256 negativePnlToRealizeCollateral = totalNegativePnlCollateral -
existingManuallyRealizedNegativePnlCollateral;
negativePnlToRealizeCollateral = negativePnlToRealizeCollateral > maxRealizableNegativePnlCollateral
? maxRealizableNegativePnlCollateral
: negativePnlToRealizeCollateral;
TradingCommonUtils.transferCollateralToVault(
trade.collateralIndex,
negativePnlToRealizeCollateral,
trade.user,
false // don't burn unrealized pnl since it's not final
);
newManuallyRealizedNegativePnlCollateral += negativePnlToRealizeCollateral;
} else {
uint256 realizedNegativePnlToCancelCollateral = existingManuallyRealizedNegativePnlCollateral -
totalNegativePnlCollateral;
TradingCommonUtils.receiveCollateralFromVault(trade.collateralIndex, realizedNegativePnlToCancelCollateral);
newManuallyRealizedNegativePnlCollateral -= realizedNegativePnlToCancelCollateral;
}
_getMultiCollatDiamond().storeManuallyRealizedNegativePnlCollateral(
trade.user,
trade.index,
newManuallyRealizedNegativePnlCollateral
);
emit ITradingCallbacksUtils.TradeNegativePnlManuallyRealized(
_a.orderId,
trade.collateralIndex,
trade.user,
trade.index,
totalNegativePnlCollateral,
existingManuallyRealizedNegativePnlCollateral,
newManuallyRealizedNegativePnlCollateral,
_a.current
);
}
/**
* @dev Returns current address as multi-collateral diamond interface to call other facets functions.
*/
function _getMultiCollatDiamond() internal view returns (IGNSMultiCollatDiamond) {
return IGNSMultiCollatDiamond(address(this));
}
/**
* @dev Returns trade from storage
* @param _trader Trader address
* @param _index Trade index
* @return Trade
*/
function _getTrade(address _trader, uint32 _index) internal view returns (ITradingStorage.Trade memory) {
return TradingCommonUtils.getTrade(_trader, _index);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import "../interfaces/IGNSMultiCollatDiamond.sol";
import "../interfaces/IGToken.sol";
import "../interfaces/IGNSStaking.sol";
import "../interfaces/IERC20.sol";
import "./StorageUtils.sol";
import "./AddressStoreUtils.sol";
import "./TradingCommonUtils.sol";
import "./updateLeverage/UpdateLeverageLifecycles.sol";
import "./updatePositionSize/UpdatePositionSizeLifecycles.sol";
import "./TradeManagementCallbacksUtils.sol";
/**
* @dev GNSTradingCallbacks facet external library
*/
library TradingCallbacksUtils {
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function initializeCallbacks(uint8 _vaultClosingFeeP) external {
updateVaultClosingFeeP(_vaultClosingFeeP);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function updateVaultClosingFeeP(uint8 _valueP) public {
if (_valueP > 100) revert IGeneralErrors.AboveMax();
_getStorage().vaultClosingFeeP = _valueP;
emit ITradingCallbacksUtils.VaultClosingFeePUpdated(_valueP);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function updateTreasuryAddress(address _treasury) external {
if (_treasury == address(0)) revert IGeneralErrors.ZeroAddress();
// Set treasury address
IGNSAddressStore.Addresses storage addresses = AddressStoreUtils.getAddresses();
addresses.treasury = _treasury;
emit IGNSAddressStore.AddressesUpdated(addresses);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function claimPendingGovFees() external {
address treasury = AddressStoreUtils.getAddresses().treasury;
if (treasury == address(0)) revert IGeneralErrors.ZeroAddress();
uint8 collateralsCount = _getMultiCollatDiamond().getCollateralsCount();
for (uint8 i = 1; i <= collateralsCount; ++i) {
uint256 feesAmountCollateral = _getStorage().pendingGovFees[i];
if (feesAmountCollateral > 0) {
_getStorage().pendingGovFees[i] = 0;
TradingCommonUtils.transferCollateralTo(i, treasury, feesAmountCollateral, false);
emit ITradingCallbacksUtils.PendingGovFeesClaimed(i, feesAmountCollateral);
}
}
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function openTradeMarketCallback(ITradingCallbacks.AggregatorAnswer memory _a) external tradingActivated {
ITradingStorage.PendingOrder memory o = _getPendingOrder(_a.orderId);
_validatePendingOrderOpen(o);
ITradingStorage.Trade memory t = o.trade;
TradingCommonUtils.updateFeeTierPoints(t.collateralIndex, t.user, t.pairIndex, 0);
ITradingCallbacks.Values memory v = _openTradePrep(t, _a.current, o.maxSlippageP, _a.current);
t.openPrice = uint64(v.priceImpact.priceAfterImpact);
if (v.cancelReason == ITradingCallbacks.CancelReason.NONE) {
// Return excess counter trade collateral
if (v.collateralToReturn > 0) {
TradingCommonUtils.transferCollateralTo(t.collateralIndex, t.user, v.collateralToReturn);
t.collateralAmount = v.newCollateralAmount;
emit ITradingCallbacksUtils.CounterTradeCollateralReturned(
_a.orderId,
t.collateralIndex,
t.user,
v.collateralToReturn
);
}
t = _registerTrade(t, v.openingFeeCollateral, v.priceImpact.positionSizeToken, o.orderType, _a.current);
emit ITradingCallbacksUtils.MarketExecuted(
_a.orderId,
t.user,
t.index,
t,
true,
_a.current,
t.openPrice,
v.liqPrice,
v.priceImpact,
0,
0,
_getCollateralPriceUsd(t.collateralIndex)
);
} else {
// Gov fee to pay for oracle cost
uint256 govFeeCollateral = TradingCommonUtils.getMinGovFeeCollateral(
t.collateralIndex,
t.user,
t.pairIndex
);
TradingCommonUtils.distributeExactGovFeeCollateral(t.collateralIndex, t.user, govFeeCollateral);
uint256 collateralReturned = t.collateralAmount - govFeeCollateral;
TradingCommonUtils.transferCollateralTo(t.collateralIndex, t.user, collateralReturned);
emit ITradingCallbacksUtils.MarketOpenCanceled(
_a.orderId,
t.user,
t.pairIndex,
v.cancelReason,
collateralReturned
);
}
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function closeTradeMarketCallback(
ITradingCallbacks.AggregatorAnswer memory _a
) external tradingActivatedOrCloseOnly {
ITradingStorage.PendingOrder memory o = _getPendingOrder(_a.orderId);
_validatePendingOrderOpen(o);
ITradingStorage.Trade memory t = _getTrade(o.trade.user, o.trade.index);
ITradingStorage.TradeInfo memory i = _getTradeInfo(o.trade.user, o.trade.index);
ITradingCommonUtils.TradePriceImpact memory priceImpact = _getTradeClosingPriceImpact(
t,
_a.current,
_a.current,
true
);
ITradingCallbacks.CancelReason cancelReason;
{
uint256 expectedPrice = o.trade.openPrice;
uint256 maxSlippage = (expectedPrice *
(i.maxSlippageP > 0 ? i.maxSlippageP : ConstantsUtils.DEFAULT_MAX_CLOSING_SLIPPAGE_P)) /
100 /
1e3;
cancelReason = !t.isOpen
? ITradingCallbacks.CancelReason.NO_TRADE
: (
t.long
? priceImpact.priceAfterImpact < expectedPrice - maxSlippage
: priceImpact.priceAfterImpact > expectedPrice + maxSlippage
)
? ITradingCallbacks.CancelReason.SLIPPAGE
: ITradingCallbacks.CancelReason.NONE;
}
if (cancelReason != ITradingCallbacks.CancelReason.NO_TRADE) {
ITradingCallbacks.Values memory v;
if (cancelReason == ITradingCallbacks.CancelReason.NONE) {
v.profitP = TradingCommonUtils.getPnlPercent(
t.openPrice,
priceImpact.priceAfterImpact,
t.long,
t.leverage
);
v.liqPrice = TradingCommonUtils.getTradeLiquidationPrice(t, _a.current);
v.amountSentToTrader = _unregisterTrade(t, v.profitP, o.orderType, _a.current, v.liqPrice, _a.current);
v.collateralPriceUsd = _getCollateralPriceUsd(t.collateralIndex);
emit ITradingCallbacksUtils.MarketExecuted(
_a.orderId,
t.user,
t.index,
t,
false,
_a.current,
priceImpact.priceAfterImpact,
v.liqPrice,
priceImpact,
v.profitP,
v.amountSentToTrader,
v.collateralPriceUsd
);
} else {
// Charge gov fee
TradingCommonUtils.updateFeeTierPoints(t.collateralIndex, t.user, t.pairIndex, 0);
uint256 govFeeCollateral = TradingCommonUtils.getMinGovFeeCollateral(
t.collateralIndex,
t.user,
t.pairIndex
);
uint256 finalGovFeeCollateral = _getMultiCollatDiamond().realizeTradingFeesOnOpenTrade(
t.user,
t.index,
govFeeCollateral,
_a.current
);
TradingCommonUtils.distributeExactGovFeeCollateral(t.collateralIndex, t.user, finalGovFeeCollateral);
}
}
if (cancelReason != ITradingCallbacks.CancelReason.NONE) {
emit ITradingCallbacksUtils.MarketCloseCanceled(_a.orderId, t.user, t.pairIndex, t.index, cancelReason);
}
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function executeTriggerOpenOrderCallback(ITradingCallbacks.AggregatorAnswer memory _a) external tradingActivated {
ITradingStorage.PendingOrder memory o = _getPendingOrder(_a.orderId);
_validatePendingOrderOpen(o);
executeTriggerOpenOrderCallbackDirect(_a, _getTrade(o.trade.user, o.trade.index), o.orderType, o.user);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function executeTriggerOpenOrderCallbackDirect(
ITradingCallbacks.AggregatorAnswer memory _a,
ITradingStorage.Trade memory _trade,
ITradingStorage.PendingOrderType _orderType,
address _initiator
) public tradingActivated {
TradingCommonUtils.updateFeeTierPoints(_trade.collateralIndex, _trade.user, _trade.pairIndex, 0);
// Ensure state conditions for executing open order trigger are met
ITradingCallbacks.Values memory v = _validateTriggerOpenOrderCallback(
_trade,
_orderType,
_a.open,
_a.high,
_a.low,
_a.current
);
if (v.cancelReason == ITradingCallbacks.CancelReason.NONE) {
// Return excess counter trade collateral
if (v.collateralToReturn > 0) {
TradingCommonUtils.transferCollateralTo(_trade.collateralIndex, _trade.user, v.collateralToReturn);
_trade.collateralAmount = v.newCollateralAmount;
emit ITradingCallbacksUtils.CounterTradeCollateralReturned(
_a.orderId,
_trade.collateralIndex,
_trade.user,
v.collateralToReturn
);
}
// Unregister open order
v.limitIndex = _trade.index;
_getMultiCollatDiamond().closeTrade(ITradingStorage.Id({user: _trade.user, index: v.limitIndex}), false, 0);
// Store trade
_trade.openPrice = uint64(v.executionPrice);
_trade.tradeType = ITradingStorage.TradeType.TRADE;
_trade = _registerTrade(
_trade,
v.openingFeeCollateral,
v.priceImpact.positionSizeToken,
_orderType,
_a.current
);
v.collateralPriceUsd = _getCollateralPriceUsd(_trade.collateralIndex);
emit ITradingCallbacksUtils.LimitExecuted(
_a.orderId,
_trade.user,
_trade.index,
v.limitIndex,
_trade,
_initiator,
_orderType,
v.executionPriceRaw,
_trade.openPrice,
v.liqPrice,
v.priceImpact,
0,
0,
v.collateralPriceUsd,
v.exactExecution
);
} else {
emit ITradingCallbacksUtils.TriggerOrderCanceled(_a.orderId, _initiator, _orderType, v.cancelReason);
}
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function executeTriggerCloseOrderCallback(
ITradingCallbacks.AggregatorAnswer memory _a
) external tradingActivatedOrCloseOnly {
ITradingStorage.PendingOrder memory o = _getPendingOrder(_a.orderId);
_validatePendingOrderOpen(o);
executeTriggerCloseOrderCallbackDirect(_a, _getTrade(o.trade.user, o.trade.index), o.orderType, o.user);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function executeTriggerCloseOrderCallbackDirect(
ITradingCallbacks.AggregatorAnswer memory _a,
ITradingStorage.Trade memory _trade,
ITradingStorage.PendingOrderType _orderType,
address _initiator
) public tradingActivatedOrCloseOnly {
// Ensure state conditions for executing close order trigger are met
ITradingCallbacks.Values memory v = _validateTriggerCloseOrderCallback(
_trade,
_orderType,
_a.open,
_a.high,
_a.low,
_a.current
);
if (v.cancelReason == ITradingCallbacks.CancelReason.NONE) {
v.profitP = TradingCommonUtils.getPnlPercent(
_trade.openPrice,
uint64(v.executionPrice),
_trade.long,
_trade.leverage
);
v.amountSentToTrader = _unregisterTrade(
_trade,
v.profitP,
_orderType,
v.executionPriceRaw,
v.liqPrice,
_a.current
);
v.collateralPriceUsd = _getCollateralPriceUsd(_trade.collateralIndex);
emit ITradingCallbacksUtils.LimitExecuted(
_a.orderId,
_trade.user,
_trade.index,
0,
_trade,
_initiator,
_orderType,
v.executionPriceRaw,
v.executionPrice,
v.liqPrice,
v.priceImpact,
v.profitP,
v.amountSentToTrader,
v.collateralPriceUsd,
v.exactExecution
);
} else {
emit ITradingCallbacksUtils.TriggerOrderCanceled(_a.orderId, _initiator, _orderType, v.cancelReason);
}
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function updateLeverageCallback(ITradingCallbacks.AggregatorAnswer memory _a) external tradingActivated {
ITradingStorage.PendingOrder memory order = _getMultiCollatDiamond().getPendingOrder(_a.orderId);
_validatePendingOrderOpen(order);
UpdateLeverageLifecycles.executeUpdateLeverage(order, _a);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function increasePositionSizeMarketCallback(
ITradingCallbacks.AggregatorAnswer memory _a
) external tradingActivated {
ITradingStorage.PendingOrder memory order = _getMultiCollatDiamond().getPendingOrder(_a.orderId);
_validatePendingOrderOpen(order);
UpdatePositionSizeLifecycles.executeIncreasePositionSizeMarket(order, _a);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function decreasePositionSizeMarketCallback(
ITradingCallbacks.AggregatorAnswer memory _a
) external tradingActivatedOrCloseOnly {
ITradingStorage.PendingOrder memory order = _getMultiCollatDiamond().getPendingOrder(_a.orderId);
_validatePendingOrderOpen(order);
UpdatePositionSizeLifecycles.executeDecreasePositionSizeMarket(order, _a);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function pnlWithdrawalCallback(ITradingCallbacks.AggregatorAnswer memory _a) external tradingActivatedOrCloseOnly {
ITradingStorage.PendingOrder memory order = _getMultiCollatDiamond().getPendingOrder(_a.orderId);
_validatePendingOrderOpen(order);
TradeManagementCallbacksUtils.executePnlWithdrawalCallback(order, _a);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function manualHoldingFeesRealizationCallback(
ITradingCallbacks.AggregatorAnswer memory _a
) external tradingActivatedOrCloseOnly {
ITradingStorage.PendingOrder memory order = _getMultiCollatDiamond().getPendingOrder(_a.orderId);
_validatePendingOrderOpen(order);
TradeManagementCallbacksUtils.executeManualHoldingFeesRealizationCallback(order, _a);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function manualNegativePnlRealizationCallback(
ITradingCallbacks.AggregatorAnswer memory _a
) external tradingActivatedOrCloseOnly {
ITradingStorage.PendingOrder memory order = _getMultiCollatDiamond().getPendingOrder(_a.orderId);
_validatePendingOrderOpen(order);
TradeManagementCallbacksUtils.executeManualNegativePnlRealizationCallback(order, _a);
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function getVaultClosingFeeP() external view returns (uint8) {
return _getStorage().vaultClosingFeeP;
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function getPendingGovFeesCollateral(uint8 _collateralIndex) external view returns (uint256) {
return _getStorage().pendingGovFees[_collateralIndex];
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function validateTriggerOpenOrderCallback(
ITradingStorage.Id memory _tradeId,
ITradingStorage.PendingOrderType _orderType,
uint64 _open,
uint64 _high,
uint64 _low,
uint64 _currentPairPrice
) public view returns (ITradingStorage.Trade memory t, ITradingCallbacks.Values memory v) {
t = _getTrade(_tradeId.user, _tradeId.index);
v = _validateTriggerOpenOrderCallback(t, _orderType, _open, _high, _low, _currentPairPrice);
}
/**
* @dev Internal function for validateTriggerOpenOrderCallback that accepts `_trade`
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function _validateTriggerOpenOrderCallback(
ITradingStorage.Trade memory _trade,
ITradingStorage.PendingOrderType _orderType,
uint64 _open,
uint64 _high,
uint64 _low,
uint64 _currentPairPrice
) internal view returns (ITradingCallbacks.Values memory v) {
if (
_orderType != ITradingStorage.PendingOrderType.LIMIT_OPEN &&
_orderType != ITradingStorage.PendingOrderType.STOP_OPEN
) revert IGeneralErrors.WrongOrderType();
// Return early if trade is not open
if (!_trade.isOpen) {
v.cancelReason = ITradingCallbacks.CancelReason.NO_TRADE;
return v;
}
// Check exact execution using market prices
uint64 highPrice = TradingCommonUtils.getMarketPrice(_trade.collateralIndex, _trade.pairIndex, _high);
uint64 lowPrice = TradingCommonUtils.getMarketPrice(_trade.collateralIndex, _trade.pairIndex, _low);
bool exactExecution = (highPrice >= _trade.openPrice && lowPrice <= _trade.openPrice);
uint256 executionPriceRaw = exactExecution
? TradingCommonUtils.deriveOraclePrice(_trade.collateralIndex, _trade.pairIndex, uint64(_trade.openPrice))
: _open;
v = _openTradePrep(
_trade,
executionPriceRaw,
_getTradeInfo(_trade.user, _trade.index).maxSlippageP,
_currentPairPrice
);
v.exactExecution = exactExecution;
v.executionPriceRaw = executionPriceRaw;
v.executionPrice = v.priceImpact.priceAfterImpact;
if (!v.exactExecution) {
// Use market price for trigger validation for non-exact executions
uint64 openPrice = TradingCommonUtils.getMarketPrice(_trade.collateralIndex, _trade.pairIndex, _open);
if (
_trade.tradeType == ITradingStorage.TradeType.STOP
? (_trade.long ? openPrice < _trade.openPrice : openPrice > _trade.openPrice)
: (_trade.long ? openPrice > _trade.openPrice : openPrice < _trade.openPrice)
) v.cancelReason = ITradingCallbacks.CancelReason.NOT_HIT;
}
}
/**
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function validateTriggerCloseOrderCallback(
ITradingStorage.Id memory _tradeId,
ITradingStorage.PendingOrderType _orderType,
uint64 _open,
uint64 _high,
uint64 _low,
uint64 _currentPairPrice
) public view returns (ITradingStorage.Trade memory t, ITradingCallbacks.Values memory v) {
t = _getTrade(_tradeId.user, _tradeId.index);
v = _validateTriggerCloseOrderCallback(t, _orderType, _open, _high, _low, _currentPairPrice);
}
/**
* @dev Internal function for validateTriggerCloseOrderCallback that accepts `_trade`
* @dev Check ITradingCallbacksUtils interface for documentation
*/
function _validateTriggerCloseOrderCallback(
ITradingStorage.Trade memory _trade,
ITradingStorage.PendingOrderType _orderType,
uint64 _open,
uint64 _high,
uint64 _low,
uint64 _currentPairPrice
) public view returns (ITradingCallbacks.Values memory v) {
if (
_orderType != ITradingStorage.PendingOrderType.TP_CLOSE &&
_orderType != ITradingStorage.PendingOrderType.SL_CLOSE &&
_orderType != ITradingStorage.PendingOrderType.LIQ_CLOSE
) revert IGeneralErrors.WrongOrderType();
ITradingStorage.TradeInfo memory i = _getTradeInfo(_trade.user, _trade.index);
v.cancelReason = !_trade.isOpen ? ITradingCallbacks.CancelReason.NO_TRADE : ITradingCallbacks.CancelReason.NONE;
// Return early if trade is not open
if (v.cancelReason != ITradingCallbacks.CancelReason.NONE) return v;
v.liqPrice = TradingCommonUtils.getTradeLiquidationPrice(_trade, _currentPairPrice);
uint256 triggerPrice = _orderType == ITradingStorage.PendingOrderType.TP_CLOSE
? _trade.tp
: (_orderType == ITradingStorage.PendingOrderType.SL_CLOSE ? _trade.sl : v.liqPrice);
// Convert prices to market prices for TP/SL orders (not liquidations)
uint64 openPrice = _open;
uint64 highPrice = _high;
uint64 lowPrice = _low;
if (_orderType != ITradingStorage.PendingOrderType.LIQ_CLOSE) {
openPrice = TradingCommonUtils.getMarketPrice(_trade.collateralIndex, _trade.pairIndex, _open);
highPrice = TradingCommonUtils.getMarketPrice(_trade.collateralIndex, _trade.pairIndex, _high);
lowPrice = TradingCommonUtils.getMarketPrice(_trade.collateralIndex, _trade.pairIndex, _low);
}
v.exactExecution = triggerPrice > 0 && lowPrice <= triggerPrice && highPrice >= triggerPrice;
v.executionPriceRaw = v.exactExecution
? (
_orderType != ITradingStorage.PendingOrderType.LIQ_CLOSE
? TradingCommonUtils.deriveOraclePrice(
_trade.collateralIndex,
_trade.pairIndex,
uint64(triggerPrice)
)
: triggerPrice
)
: _open;
// Apply closing spread and price impact for TPs and SLs, not liquidations (because trade value is 0 already)
if (_orderType != ITradingStorage.PendingOrderType.LIQ_CLOSE) {
v.priceImpact = _getTradeClosingPriceImpact(_trade, v.executionPriceRaw, _currentPairPrice, true);
v.executionPrice = v.priceImpact.priceAfterImpact;
} else {
v.executionPrice = v.executionPriceRaw;
}
uint256 maxSlippage = (triggerPrice *
(i.maxSlippageP > 0 ? i.maxSlippageP : ConstantsUtils.DEFAULT_MAX_CLOSING_SLIPPAGE_P)) /
100 /
1e3;
v.cancelReason = (v.exactExecution ||
(_orderType == ITradingStorage.PendingOrderType.LIQ_CLOSE &&
(_trade.long ? _open <= v.liqPrice : _open >= v.liqPrice)) ||
(_orderType == ITradingStorage.PendingOrderType.TP_CLOSE &&
_trade.tp > 0 &&
(_trade.long ? openPrice >= _trade.tp : openPrice <= _trade.tp)) ||
(_orderType == ITradingStorage.PendingOrderType.SL_CLOSE &&
_trade.sl > 0 &&
(_trade.long ? openPrice <= _trade.sl : openPrice >= _trade.sl)))
? (
_orderType != ITradingStorage.PendingOrderType.LIQ_CLOSE &&
(
_trade.long
? v.executionPrice < triggerPrice - maxSlippage
: v.executionPrice > triggerPrice + maxSlippage
)
? ITradingCallbacks.CancelReason.SLIPPAGE
: ITradingCallbacks.CancelReason.NONE
)
: ITradingCallbacks.CancelReason.NOT_HIT;
}
/**
* @dev Returns storage slot to use when fetching storage relevant to library
*/
function _getSlot() internal pure returns (uint256) {
return StorageUtils.GLOBAL_TRADING_CALLBACKS_SLOT;
}
/**
* @dev Returns storage pointer for storage struct in diamond contract, at defined slot
*/
function _getStorage() internal pure returns (ITradingCallbacks.TradingCallbacksStorage storage s) {
uint256 storageSlot = _getSlot();
assembly {
s.slot := storageSlot
}
}
/**
* @dev Returns current address as multi-collateral diamond interface to call other facets functions.
*/
function _getMultiCollatDiamond() internal view returns (IGNSMultiCollatDiamond) {
return IGNSMultiCollatDiamond(address(this));
}
/**
* @dev Modifier to only allow trading action when trading is activated (= revert if not activated)
*/
modifier tradingActivated() {
_tradingActivated();
_;
}
/**
* @dev Modifier to only allow trading action when trading is activated or close only (= revert if paused)
*/
modifier tradingActivatedOrCloseOnly() {
_tradingActivatedOrCloseOnly();
_;
}
/**
* @dev Checks if trading is fully activated (not paused or close-only)
*/
function _tradingActivated() private view {
if (_getMultiCollatDiamond().getTradingActivated() != ITradingStorage.TradingActivated.ACTIVATED)
revert IGeneralErrors.Paused();
}
/**
* @dev Checks if trading is activated or close-only (not paused)
*/
function _tradingActivatedOrCloseOnly() private view {
if (_getMultiCollatDiamond().getTradingActivated() == ITradingStorage.TradingActivated.PAUSED)
revert IGeneralErrors.Paused();
}
/**
* @dev Registers a trade in storage, and handles all fees and rewards
* @param _trade Trade to register
* @param _openingFeeCollateral opening fee in collateral token (collateral precision)
* @param _positionSizeToken position size in tokens (1e18)
* @param _orderType Corresponding pending order type
* @param _currentPairPrice current pair price (1e10)
* @return Final registered trade
*/
function _registerTrade(
ITradingStorage.Trade memory _trade,
uint256 _openingFeeCollateral,
uint256 _positionSizeToken,
ITradingStorage.PendingOrderType _orderType,
uint64 _currentPairPrice
) internal returns (ITradingStorage.Trade memory) {
// 1. Store final trade in storage contract
ITradingStorage.TradeInfo memory tradeInfo;
_trade.positionSizeToken = uint160(_positionSizeToken);
_trade = _getMultiCollatDiamond().storeTrade(_trade, tradeInfo, _currentPairPrice);
// 2.1 Charge opening fee
_getMultiCollatDiamond().realizeTradingFeesOnOpenTrade(
_trade.user,
_trade.index,
_openingFeeCollateral,
_currentPairPrice
);
// 2.2 Distribute opening fee to recipients
TradingCommonUtils.processFees(
_trade,
TradingCommonUtils.getPositionSizeCollateral(_trade.collateralAmount, _trade.leverage),
_orderType,
_openingFeeCollateral
);
return _trade;
}
/**
* @dev Unregisters a trade from storage, and handles all fees and rewards
* @param _trade Trade to unregister
* @param _profitP Profit percentage (1e10)
* @param _orderType pending order type
* @param _executionPriceRaw execution price without closing spread/impact (1e10)
* @param _liqPrice trade liquidation price (1e10)
* @param _currentPairPrice current pair price (1e10)
* @return tradeValueCollateral Amount of collateral sent to trader, collateral + pnl (collateral precision)
*/
function _unregisterTrade(
ITradingStorage.Trade memory _trade,
int256 _profitP,
ITradingStorage.PendingOrderType _orderType,
uint256 _executionPriceRaw,
uint256 _liqPrice,
uint64 _currentPairPrice
) internal returns (uint256 tradeValueCollateral) {
// 1. Mark trade as closed, realize pending holding fees, handle OI deltas
// Has to be called first for funding fees to be stored as trading fees, so available collateral in diamond calculation is accurate
_getMultiCollatDiamond().closeTrade(
ITradingStorage.Id({user: _trade.user, index: _trade.index}),
_profitP > 0,
_currentPairPrice
);
// 2. Calculate closing fees (refresh fee tier first)
TradingCommonUtils.updateFeeTierPoints(_trade.collateralIndex, _trade.user, _trade.pairIndex, 0);
uint256 positionSizeCollateral = TradingCommonUtils.getPositionSizeCollateral(
_trade.collateralAmount,
_trade.leverage
);
uint256 closingFeesCollateral = _orderType == ITradingStorage.PendingOrderType.LIQ_CLOSE
? TradingCommonUtils.getTotalTradeLiqFeesCollateral(_trade.pairIndex, _trade.collateralAmount)
: TradingCommonUtils.getTotalTradeFeesCollateral(
_trade.collateralIndex,
_trade.user,
_trade.pairIndex,
positionSizeCollateral,
_trade.isCounterTrade
);
// 3.1 Calculate net trade value (with pnl, closing fees, realized fees, realized pnl)
// Note: there's no pending holding fees at that point since they were realized with closeTrade()
tradeValueCollateral = TradingCommonUtils.getTradeValueCollateral(
_trade,
_profitP,
closingFeesCollateral,
_currentPairPrice
);
// 3.2 If trade is liquidated, set trade value to 0
tradeValueCollateral = (_trade.long ? _executionPriceRaw <= _liqPrice : _executionPriceRaw >= _liqPrice)
? 0
: tradeValueCollateral; /// @dev Only check with execution price not current price otherwise SL lookbacks wouldn't work (would be liquidated)
// 4. Handle collateral transfers between diamond, vault, and trader
int256 availableCollateralInDiamond = int256(
TradingCommonUtils.getTradeAvailableCollateralInDiamond(_trade.user, _trade.index, _trade.collateralAmount)
) - int256(closingFeesCollateral);
TradingCommonUtils.handleTradeValueTransfer(_trade, int256(tradeValueCollateral), availableCollateralInDiamond);
// 5. Process closing fees
TradingCommonUtils.processFees(_trade, positionSizeCollateral, _orderType, closingFeesCollateral);
// 6. Store realized trading fees for UI (since we don't call realizeTradingFeesOnOpenTrade, closing fee is taken from amount sent to trader)
_getMultiCollatDiamond().storeUiRealizedTradingFeesCollateral(_trade.user, _trade.index, closingFeesCollateral);
}
/**
* @dev Makes pre-trade checks: price impact, if trade should be cancelled based on parameters like: PnL, leverage, slippage, etc.
* @param _trade trade input
* @param _executionPriceRaw execution price without closing spread/impact (1e10 precision)
* @param _maxSlippageP max slippage % (1e3 precision)
* @param _currentPairPrice current pair price (1e10)
*/
function _openTradePrep(
ITradingStorage.Trade memory _trade,
uint256 _executionPriceRaw,
uint256 _maxSlippageP,
uint64 _currentPairPrice
) internal view returns (ITradingCallbacks.Values memory v) {
uint256 positionSizeCollateral = TradingCommonUtils.getPositionSizeCollateral(
_trade.collateralAmount,
_trade.leverage
);
if (_trade.isCounterTrade) {
(v.cancelReason, v.collateralToReturn, v.newCollateralAmount) = _validateCounterTrade(
_trade,
positionSizeCollateral,
_currentPairPrice
);
positionSizeCollateral = TradingCommonUtils.getPositionSizeCollateral(
v.newCollateralAmount,
_trade.leverage
);
}
v.priceImpact = TradingCommonUtils.getTradeOpeningPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(_trade, _executionPriceRaw, positionSizeCollateral, 0, true)
);
v.openingFeeCollateral = TradingCommonUtils.getTotalTradeFeesCollateral(
_trade.collateralIndex,
_trade.user,
_trade.pairIndex,
positionSizeCollateral,
_trade.isCounterTrade
);
v.liqPrice = TradingCommonUtils.getTradeLiquidationPriceBeforeOpened(
_trade,
int256(v.openingFeeCollateral),
v.newCollateralAmount > 0 ? v.newCollateralAmount : _trade.collateralAmount,
v.priceImpact.priceAfterImpact
);
uint256 maxSlippage = (uint256(_trade.openPrice) * _maxSlippageP) / 100 / 1e3;
v.cancelReason = (
_trade.long
? v.priceImpact.priceAfterImpact > _trade.openPrice + maxSlippage
: v.priceImpact.priceAfterImpact < _trade.openPrice - maxSlippage
)
? ITradingCallbacks.CancelReason.SLIPPAGE
: (_trade.tp > 0 &&
(
_trade.long
? v.priceImpact.priceAfterImpact >= _trade.tp
: v.priceImpact.priceAfterImpact <= _trade.tp
))
? ITradingCallbacks.CancelReason.TP_REACHED
: (_trade.sl > 0 && (_trade.long ? _executionPriceRaw <= _trade.sl : _executionPriceRaw >= _trade.sl))
? ITradingCallbacks.CancelReason.SL_REACHED
: (_trade.long ? _currentPairPrice <= v.liqPrice : _currentPairPrice >= v.liqPrice)
? ITradingCallbacks.CancelReason.LIQ_REACHED
: !TradingCommonUtils.isWithinExposureLimits(
_trade.collateralIndex,
_trade.pairIndex,
_trade.long,
positionSizeCollateral,
_currentPairPrice
)
? ITradingCallbacks.CancelReason.EXPOSURE_LIMITS
: TradingCommonUtils.getNegativePnlFromOpeningPriceImpactP(
v.priceImpact.fixedSpreadP,
v.priceImpact.cumulVolPriceImpactP,
_trade.leverage,
_trade.long
) > int256(ConstantsUtils.MAX_OPEN_NEGATIVE_PNL_P)
? ITradingCallbacks.CancelReason.PRICE_IMPACT
: _trade.leverage > _getMultiCollatDiamond().pairMaxLeverage(_trade.pairIndex)
? ITradingCallbacks.CancelReason.MAX_LEVERAGE
: v.cancelReason;
}
/**
* @dev Reverts if pending order is not open
* @param _order Pending order
*/
function _validatePendingOrderOpen(ITradingStorage.PendingOrder memory _order) internal pure {
if (!_order.isOpen) revert ITradingCallbacksUtils.PendingOrderNotOpen();
}
/**
* @dev Validates counter trade
* @param _trade counter trade
* @param _positionSizeCollateral position size in collateral tokens (collateral precision)
* @param _currentPairPrice current pair price (1e10)
*/
function _validateCounterTrade(
ITradingStorage.Trade memory _trade,
uint256 _positionSizeCollateral,
uint64 _currentPairPrice
)
internal
view
returns (ITradingCallbacks.CancelReason cancelReason, uint256 collateralToReturn, uint120 newCollateralAmount)
{
(bool isValidated, uint256 exceedingPositionSizeCollateral) = TradingCommonUtils.validateCounterTrade(
_trade,
_positionSizeCollateral,
_currentPairPrice
);
newCollateralAmount = _trade.collateralAmount;
if (isValidated) {
collateralToReturn = Math.mulDiv(exceedingPositionSizeCollateral, 1e3, _trade.leverage, Math.Rounding.Up);
newCollateralAmount -= uint120(collateralToReturn);
// Make sure new trade collateral is still > 5x min fee otherwise cancel whole trade
if (
newCollateralAmount <
TradingCommonUtils.getPairMinOpeningCollateral(_trade.collateralIndex, _trade.pairIndex)
) {
isValidated = false;
}
}
cancelReason = isValidated
? ITradingCallbacks.CancelReason.NONE
: ITradingCallbacks.CancelReason.COUNTER_TRADE_CANCELED;
}
/**
* @dev Returns pending order from storage
* @param _orderId Order ID
* @return Pending order
*/
function _getPendingOrder(
ITradingStorage.Id memory _orderId
) internal view returns (ITradingStorage.PendingOrder memory) {
return _getMultiCollatDiamond().getPendingOrder(_orderId);
}
/**
* @dev Returns collateral price in USD
* @param _collateralIndex Collateral index
* @return Collateral price in USD
*/
function _getCollateralPriceUsd(uint8 _collateralIndex) internal view returns (uint256) {
return _getMultiCollatDiamond().getCollateralPriceUsd(_collateralIndex);
}
/**
* @dev Returns trade from storage
* @param _trader Trader address
* @param _index Trade index
* @return Trade
*/
function _getTrade(address _trader, uint32 _index) internal view returns (ITradingStorage.Trade memory) {
return TradingCommonUtils.getTrade(_trader, _index);
}
/**
* @dev Returns trade info from storage
* @param _trader Trader address
* @param _index Trade index
* @return TradeInfo
*/
function _getTradeInfo(address _trader, uint32 _index) internal view returns (ITradingStorage.TradeInfo memory) {
return TradingCommonUtils.getTradeInfo(_trader, _index);
}
/**
* @dev Internal wrapper for `TradingCommonUtils.getTradeOpeningPriceImpact`. Avoids stack too deep.
* @param _trade Trade
* @param _oraclePrice the oracle price (1e10)
* @param _currentPairPrice the current pair price (1e10)
* @param _useCumulativeVolPriceImpact whether to use cumulative volume price impact
* @return output
*/
function _getTradeClosingPriceImpact(
ITradingStorage.Trade memory _trade,
uint256 _oraclePrice,
uint64 _currentPairPrice,
bool _useCumulativeVolPriceImpact
) internal view returns (ITradingCommonUtils.TradePriceImpact memory output) {
(output, ) = TradingCommonUtils.getTradeClosingPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(
_trade,
_oraclePrice,
TradingCommonUtils.getPositionSizeCollateral(_trade.collateralAmount, _trade.leverage),
_currentPairPrice,
_useCumulativeVolPriceImpact
)
);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import "../interfaces/IGToken.sol";
import "../interfaces/IGNSMultiCollatDiamond.sol";
import "../interfaces/IGNSStaking.sol";
import "../interfaces/IERC20.sol";
import "./ConstantsUtils.sol";
import "./AddressStoreUtils.sol";
import "./ChainUtils.sol";
import "./ChainConfigUtils.sol";
import "./TradingCallbacksUtils.sol";
import "./TradingStorageGetters.sol";
import "./TokenTransferUtils.sol";
/**
* @dev External library for helper functions commonly used in many places
*/
library TradingCommonUtils {
using TokenTransferUtils for address;
// Pure functions
/**
* @dev Returns the current percent profit of a trade (1e10 precision)
* @param _openPrice trade open price (1e10 precision)
* @param _currentPrice trade current price (1e10 precision)
* @param _long true for long, false for short
* @param _leverage trade leverage (1e3 precision)
*/
function getPnlPercent(
uint64 _openPrice,
uint64 _currentPrice,
bool _long,
uint24 _leverage
) public pure returns (int256 p) {
int256 pricePrecision = int256(ConstantsUtils.P_10);
int256 minPnlP = -100 * int256(ConstantsUtils.P_10);
int256 openPrice = int256(uint256(_openPrice));
int256 currentPrice = int256(uint256(_currentPrice));
int256 leverage = int256(uint256(_leverage));
p = _openPrice > 0
? ((_long ? currentPrice - openPrice : openPrice - currentPrice) * 100 * pricePrecision * leverage) /
openPrice /
1e3
: int256(0);
p = p < minPnlP ? minPnlP : p;
}
/**
* @dev Returns position size of trade in collateral tokens (avoids overflow from uint120 collateralAmount)
* @param _collateralAmount collateral of trade
* @param _leverage leverage of trade (1e3)
*/
function getPositionSizeCollateral(uint120 _collateralAmount, uint24 _leverage) internal pure returns (uint256) {
return (uint256(_collateralAmount) * _leverage) / 1e3;
}
/**
* @dev Calculates final spread % (1e10) for a trade
* @param _spreadP spread percentage (1e10)
* @param _long true if long, false if short
* @param _open true if open, false if close
*/
function getFixedSpreadP(uint256 _spreadP, bool _long, bool _open) public pure returns (int256) {
_spreadP = _spreadP / 2;
if (!_open) _long = !_long; // reverse spread direction on close
return int256(_spreadP) * int256(_long ? int256(1) : int256(-1));
}
/**
* @dev Returns negative pnl % of collateral amount due to price impact (1e10 %)
* @param _fixedSpreadP fixed spread percentage (1e10)
* @param _cumulVolPriceImpactP cumulative volume price impact percentage (1e10)
* @param _leverage trade leverage (1e3 precision)
* @param _long true if long, false if short
*/
function getNegativePnlFromOpeningPriceImpactP(
int256 _fixedSpreadP,
int256 _cumulVolPriceImpactP,
uint24 _leverage,
bool _long
) external pure returns (int256) {
return
((_fixedSpreadP + _cumulVolPriceImpactP) * int256(uint256(_leverage)) * (_long ? int256(1) : (-1))) / 1e3;
}
/**
* @dev Converts collateral value to USD (1e18 precision)
* @param _collateralAmount amount of collateral (collateral precision)
* @param _collateralPrecisionDelta precision delta of collateral (10^18/10^decimals)
* @param _collateralPriceUsd price of collateral in USD (1e8)
*/
function convertCollateralToUsd(
uint256 _collateralAmount,
uint128 _collateralPrecisionDelta,
uint256 _collateralPriceUsd
) public pure returns (uint256) {
return (_collateralAmount * _collateralPrecisionDelta * _collateralPriceUsd) / 1e8;
}
/**
* @dev Converts collateral value to GNS (1e18 precision)
* @param _collateralAmount amount of collateral (collateral precision)
* @param _collateralPrecisionDelta precision delta of collateral (10^18/10^decimals)
* @param _gnsPriceCollateral price of GNS in collateral (1e10)
*/
function convertCollateralToGns(
uint256 _collateralAmount,
uint128 _collateralPrecisionDelta,
uint256 _gnsPriceCollateral
) internal pure returns (uint256) {
return ((_collateralAmount * _collateralPrecisionDelta * ConstantsUtils.P_10) / _gnsPriceCollateral);
}
/**
* @dev Calculates trade value (useful when closing a trade)
* @dev Important: does not calculate if trade can be liquidated or not, has to be done by calling function
* @param _collateral amount of collateral (collateral precision)
* @param _percentProfit unrealized profit percentage (1e10)
* @param _feesCollateral pending holding fees - realized pnl + closing fee in collateral tokens (collateral precision)
* @param _collateralPrecisionDelta precision delta of collateral (10^18/10^decimals)
*/
function getTradeValuePure(
uint256 _collateral,
int256 _percentProfit,
int256 _feesCollateral,
uint128 _collateralPrecisionDelta
) public pure returns (uint256) {
int256 precisionDelta = int256(uint256(_collateralPrecisionDelta));
// Multiply collateral by precisionDelta so we don't lose precision for low decimals
int256 value = (int256(_collateral) *
precisionDelta +
(int256(_collateral) * precisionDelta * _percentProfit) /
int256(ConstantsUtils.P_10) /
100) /
precisionDelta -
_feesCollateral;
return value > 0 ? uint256(value) : uint256(0);
}
/**
* @dev Pure function that returns the liquidation pnl % threshold for a trade (1e10)
* @param _params trade liquidation params
* @param _leverage trade leverage (1e3 precision)
*/
function getLiqPnlThresholdP(
IPairsStorage.GroupLiquidationParams memory _params,
uint256 _leverage
) public pure returns (uint256) {
// By default use legacy threshold if liquidation params not set (trades opened before v9.2)
if (_params.maxLiqSpreadP == 0) return ConstantsUtils.LEGACY_LIQ_THRESHOLD_P;
if (_leverage <= _params.startLeverage) return _params.startLiqThresholdP;
if (_leverage >= _params.endLeverage) return _params.endLiqThresholdP;
return
_params.startLiqThresholdP -
((_leverage - _params.startLeverage) * (_params.startLiqThresholdP - _params.endLiqThresholdP)) /
(_params.endLeverage - _params.startLeverage);
}
/**
* @dev Returns price after impact (1e10)
* @param _oraclePrice oracle price (1e10)
* @param _totalPriceImpactP total price impact percentage (1e10)
*/
function getPriceAfterImpact(uint256 _oraclePrice, int256 _totalPriceImpactP) public pure returns (uint64) {
int256 priceAfterImpactRaw = int256(_oraclePrice) +
(int256(_oraclePrice) * _totalPriceImpactP) /
int256(ConstantsUtils.P_10) /
100;
if (priceAfterImpactRaw <= 0) revert IGeneralErrors.BelowMin();
if (priceAfterImpactRaw > int256(uint256(type(uint64).max))) revert IGeneralErrors.AboveMax();
return uint64(uint256(priceAfterImpactRaw));
}
/**
* @dev Validates trade's new adjusted initial leverage
* @param _adjustedInitialLeverage adjusted initial leverage (1e3)
*/
function validateAdjustedInitialLeverage(uint256 _adjustedInitialLeverage) external pure returns (bool) {
return !(_adjustedInitialLeverage > type(uint24).max || _adjustedInitialLeverage < ConstantsUtils.MIN_LEVERAGE);
}
/**
* @dev Returns trade's unrealized raw pnl (without fees or realized pnl) in collateral tokens (collateral precision)
* @param _trade trade struct
* @param _currentPairPrice current price of pair (1e10)
*/
function getTradeUnrealizedRawPnlCollateral(
ITradingStorage.Trade memory _trade,
uint64 _currentPairPrice
) external pure returns (int256) {
return
(getPnlPercent(_trade.openPrice, _currentPairPrice, _trade.long, _trade.leverage) *
int256(uint256(_trade.collateralAmount))) /
100 /
int256(ConstantsUtils.P_10);
}
// View functions
/**
* @dev Returns trade
* @param _user trade user
* @param _index trade index
*/
function getTrade(address _user, uint32 _index) internal view returns (ITradingStorage.Trade memory) {
return TradingStorageGetters.getTrade(_user, _index);
}
/**
* @dev Returns trade info
* @param _user trade user
* @param _index trade index
*/
function getTradeInfo(address _user, uint32 _index) internal view returns (ITradingStorage.TradeInfo memory) {
return TradingStorageGetters.getTradeInfo(_user, _index);
}
/**
* @dev Returns minimum position size in collateral tokens for a pair (collateral precision)
* @param _collateralIndex collateral index
* @param _pairIndex pair index
*/
function getMinPositionSizeCollateral(uint8 _collateralIndex, uint256 _pairIndex) public view returns (uint256) {
return
_getMultiCollatDiamond().getCollateralFromUsdNormalizedValue(
_collateralIndex,
_getMultiCollatDiamond().pairMinPositionSizeUsd(_pairIndex)
);
}
/**
* @dev Returns position size to use when charging fees (collateral precision * 1e3)
* @param _collateralIndex collateral index
* @param _pairIndex pair index
* @param _positionSizeCollateral trade position size in collateral tokens (collateral precision)
* @param _feeRateMultiplier fee rate multiplier (1e3)
*/
function getPositionSizeCollateralBasis(
uint8 _collateralIndex,
uint256 _pairIndex,
uint256 _positionSizeCollateral,
uint256 _feeRateMultiplier
) public view returns (uint256) {
uint256 minPositionSizeCollateral = getMinPositionSizeCollateral(_collateralIndex, _pairIndex) * 1e3;
uint256 adjustedPositionSizeCollateral = _positionSizeCollateral * _feeRateMultiplier;
return
adjustedPositionSizeCollateral > minPositionSizeCollateral
? adjustedPositionSizeCollateral
: minPositionSizeCollateral;
}
/**
* @dev Checks if total position size is not higher than maximum allowed open interest for a pair
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
* @param _long true if long, false if short
* @param _positionSizeCollateralDelta position size delta in collateral tokens (collateral precision)
* @param _currentPairPrice current pair price (1e10)
*/
function isWithinExposureLimits(
uint8 _collateralIndex,
uint16 _pairIndex,
bool _long,
uint256 _positionSizeCollateralDelta,
uint64 _currentPairPrice
) external view returns (bool) {
int256 currentSkewCollateral = getPairV10OiDynamicSkewCollateral(
_collateralIndex,
_pairIndex,
_currentPairPrice
);
int256 newSkewCollateral = currentSkewCollateral +
((int256(_positionSizeCollateralDelta) *
int256(uint256(_getMultiCollatDiamond().getCollateral(_collateralIndex).precisionDelta))) / 1e8) *
(_long ? int256(1) : -1); // 1e10
uint256 maxSkewCollateral = _getMultiCollatDiamond().getMaxSkewCollateral(_collateralIndex, _pairIndex); // 1e10
return
getPairTotalOiDynamicCollateral(_collateralIndex, _pairIndex, _long, _currentPairPrice) +
_positionSizeCollateralDelta <=
_getMultiCollatDiamond().getPairMaxOiCollateral(_collateralIndex, _pairIndex) &&
_getMultiCollatDiamond().withinMaxBorrowingGroupOi(
_collateralIndex,
_pairIndex,
_long,
_positionSizeCollateralDelta
) &&
isWithinSkewLimits(currentSkewCollateral, newSkewCollateral, maxSkewCollateral);
}
/**
* @dev Checks if new skew is within skew limits. New skew is considered within limits when any of the following are true:
* @dev a) max skew collateral is not set (0)
* @dev b) new skew collateral is within max skew collateral limits
* @dev c) new skew collateral is an improvement on current skew collateral, and is within max skew collateral limits if it switches sides
*
* @param _currentSkewCollateral current skew collateral (1e10)
* @param _newSkewCollateral new skew collateral (1e10)
* @param _maxSkewCollateral maximum skew collateral (1e10)
* @return true if new skew collateral is within limits, false otherwise
*/
function isWithinSkewLimits(
int256 _currentSkewCollateral,
int256 _newSkewCollateral,
uint256 _maxSkewCollateral
) internal pure returns (bool) {
// New skew is considered valid when:
return
// `_maxSkewCollateral` is zero (max skew is not set),
_maxSkewCollateral == 0 ||
// OR `abs(_newSkewCollateral) <= _maxSkewCollateral` (new skew is within max)
(uint256(_newSkewCollateral >= 0 ? _newSkewCollateral : -_newSkewCollateral) <= _maxSkewCollateral) ||
// OR `_newSkewCollateral` is an improvement on `_currentSkewCollateral` (current skew is above max, new skew is an improvement), AND
// `_newSkewCollateral` is within max skew limits if it has switched signs
(
_currentSkewCollateral >= 0
? _newSkewCollateral < _currentSkewCollateral && -_newSkewCollateral <= int256(_maxSkewCollateral)
: _newSkewCollateral > _currentSkewCollateral && _newSkewCollateral <= int256(_maxSkewCollateral)
);
}
/**
* @dev Convenient wrapper to return trade borrowing fee in collateral tokens (collateral precision)
* @param _trade trade input
* @param _currentPairPrice current pair price (1e10)
*/
function getTradeBorrowingFeeCollateral_old(
ITradingStorage.Trade memory _trade,
uint256 _currentPairPrice
) public view returns (uint256) {
return
_getMultiCollatDiamond().getTradeBorrowingFee(
IBorrowingFees.BorrowingFeeInput(
_trade.collateralIndex,
_trade.user,
_trade.pairIndex,
_trade.index,
_trade.long,
_trade.collateralAmount,
_trade.leverage,
_currentPairPrice
)
);
}
/**
* @dev Convenient wrapper to return trade liquidation price (1e10)
* @param _trade trade input
* @param _newOpenPrice new trade open price (1e10)
* @param _newCollateralAmount new trade collateral amount (collateral precision)
* @param _newLeverage new leverage (1e3)
* @param _additionalFeeCollateral additional fee in collateral tokens (collateral precision)
* @param _newLiquidationParams new trade liquidation params
* @param _currentPairPrice current pair price (1e10)
* @param _partialCloseMultiplier partial close multiplier (1e18)
* @param _beforeOpened if before trade was opened
*/
function getTradeLiquidationPrice(
ITradingStorage.Trade memory _trade,
uint64 _newOpenPrice,
uint256 _newCollateralAmount,
uint256 _newLeverage,
int256 _additionalFeeCollateral,
IPairsStorage.GroupLiquidationParams memory _newLiquidationParams,
uint64 _currentPairPrice,
uint256 _partialCloseMultiplier,
bool _beforeOpened
) public view returns (uint256) {
return
_getMultiCollatDiamond().getTradeLiquidationPrice(
IBorrowingFees.LiqPriceInput(
_trade.collateralIndex,
_trade.user,
_trade.pairIndex,
_trade.index,
_newOpenPrice,
_trade.long,
_newCollateralAmount,
_newLeverage,
_additionalFeeCollateral,
_newLiquidationParams,
_currentPairPrice,
_trade.isCounterTrade,
_partialCloseMultiplier > 0 ? _partialCloseMultiplier : 1e18,
_beforeOpened
)
);
}
/**
* @dev Convenient wrapper to return trade liquidation price (1e10)
* @param _trade trade input
* @param _additionalFeeCollateral additional fee in collateral tokens (collateral precision)
* @param _currentPairPrice current pair price (1e10)
*/
function getTradeLiquidationPrice(
ITradingStorage.Trade memory _trade,
int256 _additionalFeeCollateral,
uint64 _currentPairPrice
) public view returns (uint256) {
return
getTradeLiquidationPrice(
_trade,
_trade.openPrice,
_trade.collateralAmount,
_trade.leverage,
_additionalFeeCollateral,
_getMultiCollatDiamond().getTradeLiquidationParams(_trade.user, _trade.index),
_currentPairPrice,
0,
false
);
}
/**
* @dev Convenient wrapper to return trade liquidation price (1e10)
* @param _trade trade input
* @param _currentPairPrice current pair price (1e10)
*/
function getTradeLiquidationPrice(
ITradingStorage.Trade memory _trade,
uint64 _currentPairPrice
) public view returns (uint256) {
return getTradeLiquidationPrice(_trade, 0, _currentPairPrice);
}
/**
* @dev Convenient wrapper to return trade liquidation price before it was opened (1e10)
* @param _trade trade input
* @param _additionalFeeCollateral additional fee in collateral tokens (collateral precision)
* @param _finalCollateralAmount trade final collateral amount (collateral precision)
* @param _finalOpenPrice trade final open price (1e10)
*/
function getTradeLiquidationPriceBeforeOpened(
ITradingStorage.Trade memory _trade,
int256 _additionalFeeCollateral,
uint256 _finalCollateralAmount,
uint64 _finalOpenPrice
) public view returns (uint256) {
return
getTradeLiquidationPrice(
_trade,
_finalOpenPrice,
_finalCollateralAmount,
_trade.leverage,
_additionalFeeCollateral,
_getMultiCollatDiamond().getPairLiquidationParams(_trade.pairIndex),
0,
0,
true
);
}
/**
* @dev Returns net trade value in collateral tokens
* @param _trade trade data
* @param _percentProfit profit percentage (1e10)
* @param _closingFeesCollateral closing fees in collateral tokens (collateral precision)
* @param _currentPairPrice current price of pair (1e10)
*/
function getTradeValueCollateral(
ITradingStorage.Trade memory _trade,
int256 _percentProfit,
uint256 _closingFeesCollateral,
uint64 _currentPairPrice
) public view returns (uint256 valueCollateral) {
IFundingFees.TradeHoldingFees memory holdingFees = _getMultiCollatDiamond()
.getTradePendingHoldingFeesCollateral(_trade.user, _trade.index, _currentPairPrice);
(, , int256 totalRealizedPnlCollateral) = _getMultiCollatDiamond().getTradeRealizedPnlCollateral(
_trade.user,
_trade.index
);
valueCollateral = getTradeValuePure(
_trade.collateralAmount,
_percentProfit,
int256(_closingFeesCollateral) + holdingFees.totalFeeCollateral - totalRealizedPnlCollateral,
_getMultiCollatDiamond().getCollateral(_trade.collateralIndex).precisionDelta
);
}
/**
* @dev Returns trade opening price impact output
* @param _input input data
*/
function getTradeOpeningPriceImpact(
ITradingCommonUtils.TradePriceImpactInput memory _input
) external view returns (ITradingCommonUtils.TradePriceImpact memory output) {
ITradingStorage.Trade memory trade = _input.trade;
bool open = true;
uint256 positionSizeUsd = _getMultiCollatDiamond().getUsdNormalizedValue(
trade.collateralIndex,
_input.positionSizeCollateral
);
output.fixedSpreadP = getFixedSpreadP(
_getMultiCollatDiamond().pairSpreadP(trade.user, trade.pairIndex),
trade.long,
open
);
output.cumulVolPriceImpactP = _getMultiCollatDiamond().getTradeCumulVolPriceImpactP(
trade.user,
trade.pairIndex,
trade.long,
positionSizeUsd,
false,
open,
0
);
uint256 priceAfterSpreadAndCumulVolPriceImpact = getPriceAfterImpact(
_input.oraclePrice,
output.fixedSpreadP + output.cumulVolPriceImpactP
);
output.positionSizeToken =
(_input.positionSizeCollateral *
_getMultiCollatDiamond().getCollateral(trade.collateralIndex).precisionDelta *
1e10) /
priceAfterSpreadAndCumulVolPriceImpact;
output.skewPriceImpactP = _getMultiCollatDiamond().getTradeSkewPriceImpactP(
trade.collateralIndex,
trade.pairIndex,
trade.long,
output.positionSizeToken,
open
);
output.totalPriceImpactP = output.fixedSpreadP + output.cumulVolPriceImpactP + output.skewPriceImpactP;
output.priceAfterImpact = getPriceAfterImpact(_input.oraclePrice, output.totalPriceImpactP);
}
/**
* @dev Returns trade closing price impact output and trade value used to know if pnl is positive (collateral precision)
* @param _input input data
*/
function getTradeClosingPriceImpact(
ITradingCommonUtils.TradePriceImpactInput memory _input
) public view returns (ITradingCommonUtils.TradePriceImpact memory output, uint256 tradeValueCollateralNoFactor) {
ITradingStorage.Trade memory trade = _input.trade;
ITradingStorage.TradeInfo memory tradeInfo = TradingCommonUtils.getTradeInfo(trade.user, trade.index);
if (tradeInfo.contractsVersion < ITradingStorage.ContractsVersion.V9_2) {
output.priceAfterImpact = uint64(_input.oraclePrice);
return (output, 0);
}
bool open = false;
uint256 positionSizeUsd = _getMultiCollatDiamond().getUsdNormalizedValue(
trade.collateralIndex,
_input.positionSizeCollateral
);
output.positionSizeToken =
(_input.positionSizeCollateral * trade.positionSizeToken) /
getPositionSizeCollateral(trade.collateralAmount, trade.leverage);
output.fixedSpreadP = getFixedSpreadP(
_getMultiCollatDiamond().pairSpreadP(trade.user, trade.pairIndex),
trade.long,
open
);
output.skewPriceImpactP = tradeInfo.contractsVersion < ITradingStorage.ContractsVersion.V10
? int256(0)
: _getMultiCollatDiamond().getTradeSkewPriceImpactP(
trade.collateralIndex,
trade.pairIndex,
trade.long,
output.positionSizeToken,
open
);
if (!_input.useCumulativeVolPriceImpact) {
output.totalPriceImpactP = output.fixedSpreadP + output.skewPriceImpactP;
output.priceAfterImpact = getPriceAfterImpact(_input.oraclePrice, output.totalPriceImpactP);
return (output, 0);
}
// Calculate PnL without protection factor
int256 cumulVolPriceImpactP = _getMultiCollatDiamond().getTradeCumulVolPriceImpactP(
trade.user,
trade.pairIndex,
trade.long,
positionSizeUsd,
false, // assume pnl negative, so it doesn't use protection factor
open,
tradeInfo.lastPosIncreaseBlock
);
tradeValueCollateralNoFactor = getTradeValueCollateral(
trade,
getPnlPercent(
trade.openPrice,
getPriceAfterImpact(
_input.oraclePrice,
output.fixedSpreadP + cumulVolPriceImpactP + output.skewPriceImpactP
),
trade.long,
trade.leverage
),
getTotalTradeFeesCollateral(
trade.collateralIndex,
trade.user,
trade.pairIndex,
getPositionSizeCollateral(trade.collateralAmount, trade.leverage),
trade.isCounterTrade
),
_input.currentPairPrice
);
// Calculate final cumulative vol based on net PnL
output.cumulVolPriceImpactP = _getMultiCollatDiamond().getTradeCumulVolPriceImpactP(
trade.user,
trade.pairIndex,
trade.long,
positionSizeUsd,
tradeValueCollateralNoFactor > trade.collateralAmount, // _isPnlPositive = true when net pnl after fees is positive
open,
tradeInfo.lastPosIncreaseBlock
);
output.totalPriceImpactP = output.fixedSpreadP + output.cumulVolPriceImpactP + output.skewPriceImpactP;
output.priceAfterImpact = getPriceAfterImpact(_input.oraclePrice, output.totalPriceImpactP);
}
/**
* @dev Returns a trade's liquidation threshold % (1e10)
* @param _trade trade struct
*/
function getTradeLiqPnlThresholdP(ITradingStorage.Trade memory _trade) public view returns (uint256) {
return
getLiqPnlThresholdP(
_getMultiCollatDiamond().getTradeLiquidationParams(_trade.user, _trade.index),
_trade.leverage
);
}
/**
* @dev Returns total fee for a trade in collateral tokens
* @param _collateralIndex collateral index
* @param _trader address of trader
* @param _pairIndex index of pair
* @param _positionSizeCollateral position size in collateral tokens (collateral precision)
* @param _isCounterTrade whether trade is counter trade
*/
function getTotalTradeFeesCollateral(
uint8 _collateralIndex,
address _trader,
uint16 _pairIndex,
uint256 _positionSizeCollateral,
bool _isCounterTrade
) public view returns (uint256) {
uint256 feeRateP = _getMultiCollatDiamond().pairTotalPositionSizeFeeP(_pairIndex);
uint256 feeRateMultiplier = _isCounterTrade
? _getMultiCollatDiamond().getPairCounterTradeFeeRateMultiplier(_pairIndex)
: 1e3;
uint256 rawFeeCollateral = (getPositionSizeCollateralBasis(
_collateralIndex,
_pairIndex,
_positionSizeCollateral,
feeRateMultiplier
) * feeRateP) /
ConstantsUtils.P_10 /
100 /
1e3;
// Fee tier is applied on min fee, but counter trade fee rate multiplier doesn't impact min fee
return _getMultiCollatDiamond().calculateFeeAmount(_trader, rawFeeCollateral);
}
/**
* @dev Returns total liquidation fees for a trade in collateral tokens
* @param _pairIndex index of pair
* @param _collateralAmount trade collateral amount (collateral precision)
*/
function getTotalTradeLiqFeesCollateral(
uint16 _pairIndex,
uint256 _collateralAmount
) public view returns (uint256) {
uint256 totalLiqCollateralFeeP = _getMultiCollatDiamond().pairTotalLiqCollateralFeeP(_pairIndex);
return (_collateralAmount * totalLiqCollateralFeeP) / ConstantsUtils.P_10 / 100;
}
/**
* @dev Returns all fees for a trade in collateral tokens
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
* @param _trader address of trader
* @param _orderType corresponding order type
* @param _totalFeeCollateral total fee in collateral tokens (collateral precision)
*/
function getTradeFeesCollateral(
uint8 _collateralIndex,
uint16 _pairIndex,
address _trader,
ITradingStorage.PendingOrderType _orderType,
uint256 _totalFeeCollateral
) public view returns (IPairsStorage.TradeFees memory tradeFees) {
IPairsStorage.GlobalTradeFeeParams memory feeParams = _getMultiCollatDiamond().getTradeFeeParams(
_collateralIndex,
_pairIndex
);
tradeFees.totalFeeCollateral = _totalFeeCollateral;
// 1.1 Calculate referral fee
address referrer = _getMultiCollatDiamond().getTraderActiveReferrer(_trader);
if (referrer != address(0)) {
uint256 referralFeeP_override = _getMultiCollatDiamond()
.getReferralSettingsOverrides(referrer)
.referralFeeOverrideP;
uint256 referralFeeP = referralFeeP_override > 0 ? referralFeeP_override : feeParams.referralFeeP;
uint256 referralFeeRaw = (tradeFees.totalFeeCollateral * referralFeeP) / 1e3 / 100;
tradeFees.referralFeeCollateral =
(referralFeeRaw * _getMultiCollatDiamond().getReferrerFeeProgressP(referrer)) /
ConstantsUtils.P_10 /
100;
}
// 1.2 Remove referral fee from total used to calculate gov, trigger, otc, and gToken fees
uint256 totalFeeCollateral = tradeFees.totalFeeCollateral - tradeFees.referralFeeCollateral;
// 2. Gov fee
tradeFees.govFeeCollateral = ((totalFeeCollateral * feeParams.govFeeP) / 1e3 / 100);
// 3. Trigger fee
uint256 triggerOrderFeeCollateral = (totalFeeCollateral * feeParams.triggerOrderFeeP) / 1e3 / 100;
tradeFees.triggerOrderFeeCollateral = ConstantsUtils.isOrderTypeMarket(_orderType)
? 0
: triggerOrderFeeCollateral;
// 4. GNS OTC fee, gets the trigger fee when not charged (market orders)
uint256 missingTriggerOrderFeeCollateral = triggerOrderFeeCollateral - tradeFees.triggerOrderFeeCollateral;
tradeFees.gnsOtcFeeCollateral =
((totalFeeCollateral * feeParams.gnsOtcFeeP) / 1e3 / 100) +
missingTriggerOrderFeeCollateral;
// 5. gToken fee
tradeFees.gTokenFeeCollateral = (totalFeeCollateral * feeParams.gTokenFeeP) / 1e3 / 100;
// 6. gToken OC fee
tradeFees.gTokenOcFeeCollateral = (totalFeeCollateral * feeParams.gTokenOcFeeP) / 1e3 / 100;
}
/**
* @dev Returns minimum gov fee in collateral tokens (collateral precision)
* @param _collateralIndex collateral index
* @param _trader trader address
* @param _pairIndex pair index
*/
function getMinGovFeeCollateral(
uint8 _collateralIndex,
address _trader,
uint16 _pairIndex
) public view returns (uint256) {
uint256 totalFeeCollateral = getTotalTradeFeesCollateral(
_collateralIndex,
_trader,
_pairIndex,
0, // position size is 0 so it will use the minimum pos
false
) / 2; // charge fee on min pos / 2
return
(totalFeeCollateral * _getMultiCollatDiamond().getTradeFeeParams(_collateralIndex, _pairIndex).govFeeP) /
1e3 /
100;
}
/**
* @dev Reverts if user initiated any kind of pending market order on his trade
* @param _user trade user
* @param _index trade index
*/
function revertIfTradeHasPendingMarketOrder(address _user, uint32 _index) public view {
ITradingStorage.PendingOrderType[9] memory pendingOrderTypes = ConstantsUtils.getMarketOrderTypes();
ITradingStorage.Id memory tradeId = ITradingStorage.Id(_user, _index);
for (uint256 i; i < pendingOrderTypes.length; ++i) {
ITradingStorage.PendingOrderType orderType = pendingOrderTypes[i];
if (
orderType == ITradingStorage.PendingOrderType.MANUAL_HOLDING_FEES_REALIZATION ||
orderType == ITradingStorage.PendingOrderType.MANUAL_NEGATIVE_PNL_REALIZATION
) continue;
if (_getMultiCollatDiamond().getTradePendingOrderBlock(tradeId, orderType) > 0)
revert ITradingInteractionsUtils.ConflictingPendingOrder(orderType);
}
}
/**
* @dev Returns gToken contract for a collateral index
* @param _collateralIndex collateral index
*/
function getGToken(uint8 _collateralIndex) public view returns (IGToken) {
return IGToken(_getMultiCollatDiamond().getGToken(_collateralIndex));
}
/**
* @dev Returns pair total initial open interest (before v10 + after v10) in collateral tokens (collateral precision)
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
*/
function getPairTotalOisCollateral(
uint8 _collateralIndex,
uint16 _pairIndex
) internal view returns (uint256 totalOiLongCollateral, uint256 totalOiShortCollateral) {
(uint256 longOiBeforeV10Collateral, uint256 shortOiBeforeV10Collateral) = _getMultiCollatDiamond()
.getPairOisBeforeV10Collateral(_collateralIndex, _pairIndex);
IPriceImpact.PairOiCollateral memory pairOiAfterV10Collateral = _getMultiCollatDiamond()
.getPairOiAfterV10Collateral(_collateralIndex, _pairIndex);
return (
longOiBeforeV10Collateral + pairOiAfterV10Collateral.oiLongCollateral,
shortOiBeforeV10Collateral + pairOiAfterV10Collateral.oiShortCollateral
);
}
/**
* @dev Returns pair total dynamic open interest (before v10 + after v10) in collateral tokens (collateral precision)
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
* @param _currentPairPrice current pair price (1e10)
*/
function getPairTotalOisDynamicCollateral(
uint8 _collateralIndex,
uint16 _pairIndex,
uint256 _currentPairPrice
) internal view returns (uint256 oiLongCollateralDynamicAfterV10, uint256 oiShortCollateralDynamicAfterV10) {
// We have to use the initial collateral OIs for pre-v10 trades because we don't have OIs in token amount
(uint256 oiLongCollateralBeforeV10, uint256 oiShortCollateralBeforeV10) = _getMultiCollatDiamond()
.getPairOisBeforeV10Collateral(_collateralIndex, _pairIndex); // collateral precision
IPriceImpact.PairOiToken memory pairOiAfterV10Token = _getMultiCollatDiamond().getPairOiAfterV10Token(
_collateralIndex,
_pairIndex
);
uint256 precisionDelta = _getMultiCollatDiamond().getCollateral(_collateralIndex).precisionDelta;
oiLongCollateralDynamicAfterV10 =
oiLongCollateralBeforeV10 +
(pairOiAfterV10Token.oiLongToken * _currentPairPrice) /
1e10 /
precisionDelta; // collateral precision
oiShortCollateralDynamicAfterV10 =
oiShortCollateralBeforeV10 +
(pairOiAfterV10Token.oiShortToken * _currentPairPrice) /
1e10 /
precisionDelta; // collateral precision
}
/**
* @dev Returns pair total dynamic open interest (before v10 + after v10) in collateral tokens on one side only (collateral precision)
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
* @param _long true if long, false if short
* @param _currentPairPrice current pair price (1e10)
*/
function getPairTotalOiDynamicCollateral(
uint8 _collateralIndex,
uint16 _pairIndex,
bool _long,
uint256 _currentPairPrice
) internal view returns (uint256) {
(
uint256 oiLongCollateralDynamicAfterV10,
uint256 oiShortCollateralDynamicAfterV10
) = getPairTotalOisDynamicCollateral(_collateralIndex, _pairIndex, _currentPairPrice); // collateral precision
return _long ? oiLongCollateralDynamicAfterV10 : oiShortCollateralDynamicAfterV10;
}
/**
* @dev Returns pair open interest skew (v10 only) in tokens (1e18)
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
*/
function getPairV10OiTokenSkewCollateral(uint8 _collateralIndex, uint16 _pairIndex) internal view returns (int256) {
IPriceImpact.PairOiToken memory pairOiToken = _getMultiCollatDiamond().getPairOiAfterV10Token(
_collateralIndex,
_pairIndex
);
return int256(uint256(pairOiToken.oiLongToken)) - int256(uint256(pairOiToken.oiShortToken));
}
/**
* @dev Returns pair dynamic skew (v10 only) in collateral tokens (1e10 precision)
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
* @param _currentPairPrice current price of pair (1e10)
*/
function getPairV10OiDynamicSkewCollateral(
uint8 _collateralIndex,
uint16 _pairIndex,
uint256 _currentPairPrice
) internal view returns (int256) {
return (getPairV10OiTokenSkewCollateral(_collateralIndex, _pairIndex) * int256(_currentPairPrice)) / 1e18; // 1e10
}
/**
* @dev Returns min fee in collateral tokens for a pair (collateral precision)
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
*/
function getPairMinFeeCollateral(uint8 _collateralIndex, uint16 _pairIndex) public view returns (uint256) {
return
_getMultiCollatDiamond().getCollateralFromUsdNormalizedValue(
_collateralIndex,
_getMultiCollatDiamond().pairMinFeeUsd(_pairIndex)
);
}
/**
* @dev Returns min opening collateral for a pair (collateral precision)
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
*/
function getPairMinOpeningCollateral(uint8 _collateralIndex, uint16 _pairIndex) public view returns (uint256) {
return 5 * getPairMinFeeCollateral(_collateralIndex, _pairIndex);
}
/**
* @dev Validates a counter trade based on pair OI skew, and returns how much collateral to send back
* @param _trade trade struct
* @param _positionSizeCollateral position size in collateral tokens (collateral precision)
* @param _currentPairPrice current pair price (1e10)
*/
function validateCounterTrade(
ITradingStorage.Trade memory _trade,
uint256 _positionSizeCollateral,
uint64 _currentPairPrice
) external view returns (bool isValidated, uint256 exceedingPositionSizeCollateral) {
int256 positionSizeCollateralSigned = int256(_positionSizeCollateral) * (_trade.long ? int256(1) : int256(-1));
int256 pairOiSkewCollateral = (getPairV10OiDynamicSkewCollateral(
_trade.collateralIndex,
_trade.pairIndex,
_currentPairPrice
) * 1e8) / int256(uint256(_getMultiCollatDiamond().getCollateral(_trade.collateralIndex).precisionDelta)); // collateral precision
// Signed position size of trade should be opposite sign of pair OI skew since it should lower exposure
if (
pairOiSkewCollateral == 0 ||
(pairOiSkewCollateral > 0 && positionSizeCollateralSigned > 0) ||
(pairOiSkewCollateral < 0 && positionSizeCollateralSigned < 0)
) return (false, 0);
// Calculate exceeding position size to make skew 0
uint256 maxPositionSizeCollateral = uint256(
pairOiSkewCollateral < 0 ? -pairOiSkewCollateral : pairOiSkewCollateral
);
if (_positionSizeCollateral > maxPositionSizeCollateral)
exceedingPositionSizeCollateral = _positionSizeCollateral - maxPositionSizeCollateral;
return (true, exceedingPositionSizeCollateral);
}
/**
* @dev Returns trade's effective leverage (1e3 precision)
* @param _trade trade struct
* @param _newOpenPrice new trade open price (1e10)
* @param _newCollateralAmount new trade collateral amount (collateral precision)
* @param _newLeverage new leverage (1e3)
* @param _currentPairPrice current price of pair (1e10)
* @param _additionalFeeCollateral additional fee / negative pnl in collateral tokens (collateral precision)
*/
function getTradeNewEffectiveLeverage(
ITradingStorage.Trade memory _trade,
uint64 _newOpenPrice,
uint120 _newCollateralAmount,
uint24 _newLeverage,
uint64 _currentPairPrice,
uint256 _additionalFeeCollateral
) external view returns (uint24) {
ITradingStorage.Trade memory newTrade = _trade;
newTrade.openPrice = _newOpenPrice;
newTrade.collateralAmount = _newCollateralAmount;
newTrade.leverage = _newLeverage;
uint256 newPosSizeCollateral = getPositionSizeCollateral(newTrade.collateralAmount, newTrade.leverage);
(ITradingCommonUtils.TradePriceImpact memory priceImpact, ) = getTradeClosingPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(
newTrade,
_currentPairPrice,
newPosSizeCollateral,
_currentPairPrice,
true
)
);
uint256 newMarginValueCollateral = getTradeValueCollateral(
newTrade,
getPnlPercent(newTrade.openPrice, priceImpact.priceAfterImpact, newTrade.long, newTrade.leverage),
getTotalTradeFeesCollateral(
newTrade.collateralIndex,
newTrade.user,
newTrade.pairIndex,
newPosSizeCollateral,
newTrade.isCounterTrade
) + _additionalFeeCollateral,
_currentPairPrice
);
if (newMarginValueCollateral == 0) return type(uint24).max;
uint256 newPosSizeCollateralDynamic = (newPosSizeCollateral * _currentPairPrice) / _newOpenPrice;
uint256 newEffectiveLeverage = (newPosSizeCollateralDynamic * 1e3) / newMarginValueCollateral;
uint24 maxLeverage = type(uint24).max;
return newEffectiveLeverage > maxLeverage ? maxLeverage : uint24(newEffectiveLeverage);
}
/**
* @dev Returns a trade's available collateral in diamond contract (collateral precision)
* @param _trader trader address
* @param _index trade index
* @param _collateralAmount trade collateral amount (collateral precision)
*/
function getTradeAvailableCollateralInDiamond(
address _trader,
uint32 _index,
uint256 _collateralAmount
) internal view returns (uint256) {
int256 tradeAvailableCollateralInDiamondRaw = getTradeAvailableCollateralInDiamondRaw(
_trader,
_index,
_collateralAmount
);
/// @dev Under no circumstance should the total available collateral in diamond be negative after a trading operation
if (tradeAvailableCollateralInDiamondRaw < 0) {
revert IGeneralErrors.BelowMin();
}
return uint256(tradeAvailableCollateralInDiamondRaw);
}
/**
* @dev Returns a trade's raw available collateral in diamond contract (collateral precision)
* @param _trader trader address
* @param _index trade index
* @param _collateralAmount trade collateral amount (collateral precision)
*/
function getTradeAvailableCollateralInDiamondRaw(
address _trader,
uint32 _index,
uint256 _collateralAmount
) internal view returns (int256) {
IFundingFees.TradeFeesData memory tradeFeesData = _getMultiCollatDiamond().getTradeFeesData(_trader, _index);
return
int256(_collateralAmount + uint256(tradeFeesData.virtualAvailableCollateralInDiamond)) -
int256(
uint256(
tradeFeesData.realizedTradingFeesCollateral +
tradeFeesData.manuallyRealizedNegativePnlCollateral +
tradeFeesData.alreadyTransferredNegativePnlCollateral
)
);
}
/**
* @dev Returns the current market price adjusted for skew impact
* @param _collateralIndex index of collateral
* @param _pairIndex index of pair
* @param _currentPairPrice current pair price (1e10)
*/
function getMarketPrice(
uint8 _collateralIndex,
uint16 _pairIndex,
uint64 _currentPairPrice
) internal view returns (uint64 result) {
int256 skewImpactP = _getMultiCollatDiamond().getTradeSkewPriceImpactP(
_collateralIndex,
_pairIndex,
true, // Not used for size 0
0, // Size 0 for current market price
true // Not used for size 0
);
result = getPriceAfterImpact(_currentPairPrice, skewImpactP);
}
/**
* @dev Reverse engineers oracle price from market price by removing skew impact
* @param _collateralIndex Collateral index
* @param _pairIndex Pair index
* @param _marketPrice Market price (includes skew impact)
* @return oraclePrice Oracle price without skew impact
*/
function deriveOraclePrice(
uint8 _collateralIndex,
uint16 _pairIndex,
uint64 _marketPrice
) internal view returns (uint64) {
// Get current skew impact with position size 0
int256 skewImpactP = _getMultiCollatDiamond().getTradeSkewPriceImpactP(
_collateralIndex,
_pairIndex,
true, // Not used for size 0
0, // Size 0 for current skew impact
true // Not used for size 0
);
// Reverse getMarketPrice
return
uint64(
Math.mulDiv(
uint256(_marketPrice),
ConstantsUtils.P_10 * 100,
uint256(int256(ConstantsUtils.P_10 * 100) + skewImpactP),
Math.Rounding.Up
)
);
}
// Transfers
/**
* @dev Transfers collateral from trader
* @param _collateralIndex index of the collateral
* @param _from sending address
* @param _amountCollateral amount of collateral to receive (collateral precision)
*/
function transferCollateralFrom(uint8 _collateralIndex, address _from, uint256 _amountCollateral) public {
if (_amountCollateral > 0) {
_getMultiCollatDiamond().getCollateral(_collateralIndex).collateral.transferFrom(
_from,
address(this),
_amountCollateral
);
}
}
/**
* @dev Transfers collateral to trader
* @param _collateralIndex index of the collateral
* @param _to receiving address
* @param _amountCollateral amount of collateral to transfer (collateral precision)
*/
function transferCollateralTo(uint8 _collateralIndex, address _to, uint256 _amountCollateral) internal {
transferCollateralTo(_collateralIndex, _to, _amountCollateral, true);
}
/**
* @dev Transfers collateral to trader
* @param _collateralIndex index of the collateral
* @param _to receiving address
* @param _amountCollateral amount of collateral to transfer (collateral precision)
* @param _unwrapNativeToken whether to try and unwrap native token before sending
*/
function transferCollateralTo(
uint8 _collateralIndex,
address _to,
uint256 _amountCollateral,
bool _unwrapNativeToken
) internal {
if (_amountCollateral > 0) {
address collateral = _getMultiCollatDiamond().getCollateral(_collateralIndex).collateral;
if (
_unwrapNativeToken &&
ChainUtils.isWrappedNativeToken(collateral) &&
ChainConfigUtils.getNativeTransferEnabled()
) {
collateral.unwrapAndTransferNative(
_to,
_amountCollateral,
uint256(ChainConfigUtils.getNativeTransferGasLimit())
);
} else {
collateral.transfer(_to, _amountCollateral);
}
}
}
/**
* @dev Transfers GNS to address
* @param _to receiving address
* @param _amountGns amount of GNS to transfer (1e18)
*/
function transferGnsTo(address _to, uint256 _amountGns) internal {
if (_amountGns > 0) {
AddressStoreUtils.getAddresses().gns.transfer(_to, _amountGns);
}
}
/**
* @dev Transfers GNS from address
* @param _from sending address
* @param _amountGns amount of GNS to receive (1e18)
*/
function transferGnsFrom(address _from, uint256 _amountGns) internal {
if (_amountGns > 0) {
AddressStoreUtils.getAddresses().gns.transferFrom(_from, address(this), _amountGns);
}
}
/**
* @dev Sends collateral to gToken vault
* @param _collateralIndex collateral index
* @param _amountCollateral amount of collateral to send to vault (collateral precision)
* @param _trader trader address
* @param _burn true if should burn
*/
function transferCollateralToVault(
uint8 _collateralIndex,
uint256 _amountCollateral,
address _trader,
bool _burn
) public {
if (_amountCollateral > 0) getGToken(_collateralIndex).receiveAssets(_amountCollateral, _trader, _burn);
}
/**
* @dev Sends fee collateral to gToken vault
* @param _collateralIndex collateral index
* @param _amountCollateral amount of collateral to send to vault (collateral precision)
* @param _trader trader address
*/
function transferFeeToVault(uint8 _collateralIndex, uint256 _amountCollateral, address _trader) public {
transferCollateralToVault(_collateralIndex, _amountCollateral, _trader, false); // don't burn fees
}
/**
* @dev Receives collateral from gToken vault
* @param _collateralIndex collateral index
* @param _amountCollateral amount of collateral to receive from vault (collateral precision)
*/
function receiveCollateralFromVault(uint8 _collateralIndex, uint256 _amountCollateral) public {
if (_amountCollateral > 0) getGToken(_collateralIndex).sendAssets(_amountCollateral, address(this));
}
/**
* @dev Handles value transfers based on amount to send to trader and available collateral in diamond
* @param _trade trade struct
* @param _collateralSentToTrader total amount to send to trader (collateral precision), can only be negative for partial closes (leverage decrease)
* @param _availableCollateralInDiamond part of _collateralSentToTrader available in diamond balance (collateral precision), can be negative for full/partial closes (collateral decrease)
*/
function handleTradeValueTransfer(
ITradingStorage.Trade memory _trade,
int256 _collateralSentToTrader,
int256 _availableCollateralInDiamond
) external {
if (_availableCollateralInDiamond < 0 && _collateralSentToTrader < 0) revert IGeneralErrors.NotAuthorized();
if (_collateralSentToTrader > _availableCollateralInDiamond) {
// Calculate amount to be received from gToken
int256 collateralFromGToken = _collateralSentToTrader - _availableCollateralInDiamond;
// Receive PNL from gToken; This is sent to the trader at a later point
receiveCollateralFromVault(_trade.collateralIndex, uint256(collateralFromGToken));
} else if (_collateralSentToTrader < _availableCollateralInDiamond) {
// Send loss to gToken
transferCollateralToVault(
_trade.collateralIndex,
uint256(_availableCollateralInDiamond - _collateralSentToTrader),
_trade.user,
// any amount sent to vault at this point is negative PnL, funding fees already sent with _burn = false
// only use _burn = true if the pair does not have a skipBurn flag active
!_getMultiCollatDiamond().getPairFlags(_trade.collateralIndex, _trade.pairIndex).skipBurn
);
}
// Send collateral to trader, if any
if (_collateralSentToTrader > 0) {
transferCollateralTo(_trade.collateralIndex, _trade.user, uint256(_collateralSentToTrader));
}
emit ITradingCommonUtils.TradeValueTransferred(
_trade.collateralIndex,
_trade.user,
_trade.index,
_collateralSentToTrader,
_availableCollateralInDiamond
);
}
// Fees
/**
* @dev Updates a trader's fee tiers points based on his trade size
* @param _collateralIndex collateral index
* @param _trader address of trader
* @param _positionSizeCollateral position size in collateral tokens (collateral precision)
* @param _pairIndex index of pair
*/
function updateFeeTierPoints(
uint8 _collateralIndex,
address _trader,
uint256 _pairIndex,
uint256 _positionSizeCollateral
) public {
uint256 usdNormalizedPositionSize = _getMultiCollatDiamond().getUsdNormalizedValue(
_collateralIndex,
_positionSizeCollateral
);
_getMultiCollatDiamond().updateTraderPoints(_trader, usdNormalizedPositionSize, _pairIndex);
}
/**
* @dev Distributes fee to gToken vault
* @param _collateralIndex index of collateral
* @param _trader address of trader
* @param _valueCollateral fee in collateral tokens (collateral precision)
*/
function distributeVaultFeeCollateral(uint8 _collateralIndex, address _trader, uint256 _valueCollateral) public {
getGToken(_collateralIndex).distributeReward(_valueCollateral);
emit ITradingCommonUtils.GTokenFeeCharged(_trader, _collateralIndex, _valueCollateral);
}
/**
* @dev Distributes fee to gToken vault OC
* @param _collateralIndex index of collateral
* @param _trader address of trader
* @param _valueCollateral fee in collateral tokens (collateral precision)
*/
function distributeVaultOcFeeCollateral(
uint8 _collateralIndex,
address _trader,
uint256 _valueCollateral
) internal {
transferFeeToVault(_collateralIndex, _valueCollateral, _trader);
emit ITradingCommonUtils.GTokenOcFeeCharged(_trader, _collateralIndex, _valueCollateral);
}
/**
* @dev Distributes gov fees exact amount
* @param _collateralIndex index of collateral
* @param _trader address of trader
* @param _govFeeCollateral position size in collateral tokens (collateral precision)
*/
function distributeExactGovFeeCollateral(
uint8 _collateralIndex,
address _trader,
uint256 _govFeeCollateral
) public {
TradingCallbacksUtils._getStorage().pendingGovFees[_collateralIndex] += _govFeeCollateral;
emit ITradingCommonUtils.GovFeeCharged(_trader, _collateralIndex, _govFeeCollateral);
}
/**
* @dev Increases OTC balance to be distributed once OTC is executed
* @param _collateralIndex collateral index
* @param _trader trader address
* @param _amountCollateral amount of collateral tokens to distribute (collateral precision)
*/
function distributeGnsOtcFeeCollateral(uint8 _collateralIndex, address _trader, uint256 _amountCollateral) public {
_getMultiCollatDiamond().addOtcCollateralBalance(_collateralIndex, _amountCollateral);
emit ITradingCommonUtils.GnsOtcFeeCharged(_trader, _collateralIndex, _amountCollateral);
}
/**
* @dev Distributes trigger fee in GNS tokens
* @param _trader address of trader
* @param _collateralIndex index of collateral
* @param _triggerFeeCollateral trigger fee in collateral tokens (collateral precision)
* @param _gnsPriceCollateral gns/collateral price (1e10 precision)
* @param _collateralPrecisionDelta collateral precision delta (10^18/10^decimals)
*/
function distributeTriggerFeeGns(
address _trader,
uint8 _collateralIndex,
uint256 _triggerFeeCollateral,
uint256 _gnsPriceCollateral,
uint128 _collateralPrecisionDelta
) public {
transferFeeToVault(_collateralIndex, _triggerFeeCollateral, _trader);
uint256 triggerFeeGns = convertCollateralToGns(
_triggerFeeCollateral,
_collateralPrecisionDelta,
_gnsPriceCollateral
);
_getMultiCollatDiamond().distributeTriggerReward(triggerFeeGns);
emit ITradingCommonUtils.TriggerFeeCharged(_trader, _collateralIndex, _triggerFeeCollateral);
}
/**
* @dev Distributes opening fees for trade and returns the trade fees charged in collateral tokens
* @dev Before calling: should refresh fee tier points
* @param _trade trade struct
* @param _positionSizeCollateral position size in collateral tokens (collateral precision)
* @param _orderType trade order type
* @param _totalFeeCollateral total fee in collateral tokens (collateral precision)
*/
function processFees(
ITradingStorage.Trade memory _trade,
uint256 _positionSizeCollateral,
ITradingStorage.PendingOrderType _orderType,
uint256 _totalFeeCollateral
) external returns (uint256) {
uint128 collateralPrecisionDelta = _getMultiCollatDiamond()
.getCollateral(_trade.collateralIndex)
.precisionDelta;
uint256 gnsPriceCollateral = _getMultiCollatDiamond().getGnsPriceCollateralIndex(_trade.collateralIndex);
// 1. Calculate all fees
IPairsStorage.TradeFees memory tradeFees = getTradeFeesCollateral(
_trade.collateralIndex,
_trade.pairIndex,
_trade.user,
_orderType,
_totalFeeCollateral
);
// 2.1 Distribute referral fee
if (tradeFees.referralFeeCollateral > 0) {
distributeReferralFeeCollateral(
_trade.collateralIndex,
_trade.user,
_positionSizeCollateral,
tradeFees.referralFeeCollateral,
gnsPriceCollateral
);
}
// 2.2 Distribute gov fee
distributeExactGovFeeCollateral(_trade.collateralIndex, _trade.user, tradeFees.govFeeCollateral);
// 2.3 Distribute trigger fee
if (tradeFees.triggerOrderFeeCollateral > 0) {
distributeTriggerFeeGns(
_trade.user,
_trade.collateralIndex,
tradeFees.triggerOrderFeeCollateral,
gnsPriceCollateral,
collateralPrecisionDelta
);
}
// 2.4 Distribute GNS OTC fee
distributeGnsOtcFeeCollateral(_trade.collateralIndex, _trade.user, tradeFees.gnsOtcFeeCollateral);
// 2.5 Distribute GToken fees
distributeVaultFeeCollateral(_trade.collateralIndex, _trade.user, tradeFees.gTokenFeeCollateral);
// 2.6 Distribute GToken OC fees
if (tradeFees.gTokenOcFeeCollateral > 0) {
distributeVaultOcFeeCollateral(_trade.collateralIndex, _trade.user, tradeFees.gTokenOcFeeCollateral);
}
// 3. Credit fee tier points
updateFeeTierPoints(_trade.collateralIndex, _trade.user, _trade.pairIndex, _positionSizeCollateral);
emit ITradingCommonUtils.FeesProcessed(
_trade.collateralIndex,
_trade.user,
_positionSizeCollateral,
_orderType,
tradeFees.totalFeeCollateral
);
return tradeFees.totalFeeCollateral;
}
/**
* @dev Distributes referral rewards and returns the amount charged in collateral tokens
* @param _collateralIndex collateral index
* @param _trader address of trader
* @param _positionSizeCollateral position size in collateral tokens (collateral precision)
* @param _referralFeeCollateral referral fee in collateral tokens (collateral precision)
* @param _gnsPriceCollateral gns/collateral price (1e10 precision)
*/
function distributeReferralFeeCollateral(
uint8 _collateralIndex,
address _trader,
uint256 _positionSizeCollateral,
uint256 _referralFeeCollateral,
uint256 _gnsPriceCollateral
) public {
_getMultiCollatDiamond().distributeReferralReward(
_trader,
_getMultiCollatDiamond().getUsdNormalizedValue(_collateralIndex, _positionSizeCollateral),
_getMultiCollatDiamond().getUsdNormalizedValue(_collateralIndex, _referralFeeCollateral),
_getMultiCollatDiamond().getGnsPriceUsd(_collateralIndex, _gnsPriceCollateral)
);
transferFeeToVault(_collateralIndex, _referralFeeCollateral, _trader);
emit ITradingCommonUtils.ReferralFeeCharged(_trader, _collateralIndex, _referralFeeCollateral);
}
// Open interests
/**
* @dev Update protocol open interest (any amount)
* @param _trade trade struct
* @param _positionSizeCollateral position size in collateral tokens (collateral precision)
* @param _positionSizeToken position size in tokens (1e18)
* @param _open whether it corresponds to a trade opening or closing
* @param _isPnlPositive whether it corresponds to a positive pnl trade (only relevant when _open = false)
* @param _currentPairPrice current pair price (1e10)
* @param _isPartial whether it corresponds to a partial update
*/
function _updateOi(
ITradingStorage.Trade memory _trade,
uint256 _positionSizeCollateral,
uint256 _positionSizeToken,
bool _open,
bool _isPnlPositive,
uint64 _currentPairPrice,
bool _isPartial
) private {
if (_isPartial || !_open)
_getMultiCollatDiamond().realizeHoldingFeesOnOpenTrade(_trade.user, _trade.index, _currentPairPrice);
_getMultiCollatDiamond().storeTradeInitialAccFees(
_trade.user,
_trade.index,
_trade.collateralIndex,
_trade.pairIndex,
_trade.long,
_currentPairPrice
);
_getMultiCollatDiamond().handleTradeBorrowingCallback(
_trade.collateralIndex,
_trade.user,
_trade.pairIndex,
_trade.index,
_positionSizeCollateral,
_open,
_trade.long,
_currentPairPrice
); // updates group OI
_getMultiCollatDiamond().addPriceImpactOpenInterest(
_trade.user,
_trade.index,
_positionSizeCollateral,
_open,
_isPnlPositive
);
if (_open) {
_getMultiCollatDiamond().updatePairOiAfterV10(
_trade.collateralIndex,
_trade.pairIndex,
_positionSizeCollateral,
_positionSizeToken,
true, // increase
_trade.long
);
} else {
if (
_getMultiCollatDiamond().getTradeContractsVersion(_trade.user, _trade.index) <
ITradingStorage.ContractsVersion.V10
) {
_getMultiCollatDiamond().updatePairOiBeforeV10(
_trade.collateralIndex,
_trade.pairIndex,
_trade.long,
false, // decrease
_positionSizeCollateral
);
} else {
_getMultiCollatDiamond().updatePairOiAfterV10(
_trade.collateralIndex,
_trade.pairIndex,
_positionSizeCollateral,
_positionSizeToken,
false, // decrease
_trade.long
);
}
}
}
/**
* @dev Handles all necessary OI-related callbacks for opening a new trade
* @param _trade trade struct
* @param _currentPairPrice current pair price (1e10)
*/
function addNewTradeOi(ITradingStorage.Trade memory _trade, uint64 _currentPairPrice) external {
uint256 positionSizeCollateral = getPositionSizeCollateral(_trade.collateralAmount, _trade.leverage);
_updateOi(_trade, positionSizeCollateral, _trade.positionSizeToken, true, false, _currentPairPrice, false);
}
/**
* @dev Handles all necessary OI-related callbacks for closing a trade
* @param _trade trade struct
* @param _isPnlPositive whether it corresponds to a positive pnl trade
* @param _currentPairPrice current pair price (1e10)
*/
function removeTradeOi(
ITradingStorage.Trade memory _trade,
bool _isPnlPositive,
uint64 _currentPairPrice
) external {
uint256 positionSizeCollateral = getPositionSizeCollateral(_trade.collateralAmount, _trade.leverage);
_updateOi(
_trade,
positionSizeCollateral,
_trade.positionSizeToken,
false,
_isPnlPositive,
_currentPairPrice,
false
);
}
/**
* @dev Handles OI delta for an existing trade (for trade updates)
* @param _trade trade struct
* @param _newPositionSizeCollateral new position size in collateral tokens (collateral precision)
* @param _positionSizeTokenDelta position size delta in tokens (1e18)
* @param _isPnlPositive whether it corresponds to a positive pnl trade (only relevant when closing)
* @param _currentPairPrice current pair price (1e10)
*/
function handleOiDelta(
ITradingStorage.Trade memory _trade,
uint256 _newPositionSizeCollateral,
uint256 _positionSizeTokenDelta,
bool _isPnlPositive,
uint64 _currentPairPrice
) external {
uint256 existingPositionSizeCollateral = getPositionSizeCollateral(_trade.collateralAmount, _trade.leverage);
bool isIncrease = _newPositionSizeCollateral > existingPositionSizeCollateral;
uint256 positionSizeCollateralDelta = isIncrease
? _newPositionSizeCollateral - existingPositionSizeCollateral
: existingPositionSizeCollateral - _newPositionSizeCollateral;
_updateOi(
_trade,
positionSizeCollateralDelta,
_positionSizeTokenDelta,
isIncrease,
_isPnlPositive,
_currentPairPrice,
true
);
}
/**
* @dev Returns current address as multi-collateral diamond interface to call other facets functions.
*/
function _getMultiCollatDiamond() internal view returns (IGNSMultiCollatDiamond) {
return IGNSMultiCollatDiamond(address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IGNSMultiCollatDiamond.sol";
import "../interfaces/IERC20.sol";
import "../interfaces/IWETH9.sol";
import "./StorageUtils.sol";
import "./PackingUtils.sol";
import "./ChainUtils.sol";
import "./ConstantsUtils.sol";
import "./updateLeverage/UpdateLeverageLifecycles.sol";
import "./updatePositionSize/UpdatePositionSizeLifecycles.sol";
/**
* @dev GNSTradingInteractions facet external library
*/
library TradingInteractionsUtils {
using PackingUtils for uint256;
using SafeERC20 for IERC20;
uint256 private constant MIN_MARKET_TIMEOUT_SECONDS = 1 minutes;
uint256 private constant MAX_MARKET_TIMEOUT_SECONDS = 5 minutes;
uint256 private constant FIXED_LINK_PER_TRIGGER = 1e18;
/**
* @dev Modifier to only allow trading action when trading is activated (= revert if not activated)
*/
modifier tradingActivated() {
_tradingActivated();
_;
}
/**
* @dev Modifier to only allow trading action when trading is activated or close only (= revert if paused)
*/
modifier tradingActivatedOrCloseOnly() {
_tradingActivatedOrCloseOnly();
_;
}
/**
* @dev Modifier to prevent calling function from delegated action
*/
modifier notDelegatedAction() {
_notDelegatedAction();
_;
}
/**
* @dev Validates, temporarly stores, and cleans up signed market pair prices.
*/
modifier useSignedLookbackPrices(IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices) {
bool isLookback = true;
_getMultiCollatDiamond().validateSignedPairPrices(_signedPairPrices, isLookback);
_;
_getMultiCollatDiamond().cleanUpSignedPairPrices();
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function initializeTrading(uint16 _marketOrdersTimeoutBlocks, address[] memory _usersByPassTriggerLink) external {
updateMarketOrdersTimeoutBlocks(_marketOrdersTimeoutBlocks);
bool[] memory shouldByPass = new bool[](_usersByPassTriggerLink.length);
for (uint256 i = 0; i < _usersByPassTriggerLink.length; i++) {
shouldByPass[i] = true;
}
updateByPassTriggerLink(_usersByPassTriggerLink, shouldByPass);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function updateMarketOrdersTimeoutBlocks(uint16 _valueBlocks) public {
if (_valueBlocks == 0) revert IGeneralErrors.ZeroValue();
uint256 timeoutSeconds = ChainUtils.convertBlocksToSeconds(uint256(_valueBlocks));
if (timeoutSeconds < MIN_MARKET_TIMEOUT_SECONDS) revert IGeneralErrors.BelowMin();
if (timeoutSeconds > MAX_MARKET_TIMEOUT_SECONDS) revert IGeneralErrors.AboveMax();
_getStorage().marketOrdersTimeoutBlocks = _valueBlocks;
emit ITradingInteractionsUtils.MarketOrdersTimeoutBlocksUpdated(_valueBlocks);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function updateByPassTriggerLink(address[] memory _users, bool[] memory _shouldByPass) public {
ITradingInteractions.TradingInteractionsStorage storage s = _getStorage();
if (_users.length != _shouldByPass.length) revert IGeneralErrors.WrongLength();
for (uint256 i = 0; i < _users.length; i++) {
address user = _users[i];
bool value = _shouldByPass[i];
s.byPassTriggerLink[user] = value;
emit ITradingInteractionsUtils.ByPassTriggerLinkUpdated(user, value);
}
}
/**
* @dev Check IFundingFeesUtils interface for documentation
*/
function initiateManualNegativePnlRealization(
address _trader,
uint32 _index,
bool _isHoldingFeesRealization
) external tradingActivatedOrCloseOnly {
ITradingStorage.Trade memory trade = TradingCommonUtils.getTrade(_trader, _index);
if (!trade.isOpen) revert ITradingInteractionsUtils.NoTrade();
if (trade.tradeType != ITradingStorage.TradeType.TRADE) revert IGeneralErrors.WrongTradeType();
ITradingStorage.PendingOrder memory pendingOrder;
pendingOrder.trade.user = trade.user;
pendingOrder.trade.index = trade.index;
pendingOrder.user = msg.sender;
pendingOrder.orderType = _isHoldingFeesRealization
? ITradingStorage.PendingOrderType.MANUAL_HOLDING_FEES_REALIZATION
: ITradingStorage.PendingOrderType.MANUAL_NEGATIVE_PNL_REALIZATION;
ITradingStorage.Id memory orderId = _getMultiCollatDiamond().getPrice(
IPriceAggregator.GetPriceInput(
trade.collateralIndex,
trade.pairIndex,
pendingOrder,
0, /// no LINK fee
ChainUtils.getBlockNumber(),
false
)
);
emit ITradingInteractionsUtils.ManualNegativePnlRealizationInitiated(
orderId,
_trader,
_index,
_isHoldingFeesRealization
);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function setTradingDelegate(address _delegate) external {
if (_delegate == address(0)) revert IGeneralErrors.ZeroAddress();
_getStorage().delegations[msg.sender] = _delegate;
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function removeTradingDelegate() external {
delete _getStorage().delegations[msg.sender];
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function delegatedTradingAction(
address _trader,
bytes calldata _callData
) external notDelegatedAction returns (bytes memory) {
ITradingInteractions.TradingInteractionsStorage storage s = _getStorage();
if (s.delegations[_trader] != msg.sender) revert ITradingInteractionsUtils.DelegateNotApproved();
s.senderOverride = _trader;
(bool success, bytes memory result) = address(this).delegatecall(_callData);
if (!success) {
if (result.length < 4) revert(); // not a custom error (4 byte signature) or require() message
assembly {
let len := mload(result)
revert(add(result, 0x20), len)
}
}
s.senderOverride = address(0);
return result;
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function openTrade(
ITradingStorage.Trade memory _trade,
uint16 _maxSlippageP,
address _referrer
) external tradingActivated {
_openTrade(_trade, _maxSlippageP, _referrer, false);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function openTradeNative(
ITradingStorage.Trade memory _trade,
uint16 _maxSlippageP,
address _referrer
) external tradingActivated notDelegatedAction {
_trade.collateralAmount = _wrapNativeToken(_trade.collateralIndex);
_openTrade(_trade, _maxSlippageP, _referrer, true);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function updateMaxClosingSlippageP(
uint32 _index,
uint16 _maxClosingSlippageP
) external tradingActivatedOrCloseOnly {
ITradingStorage.Id memory tradeId = ITradingStorage.Id(_msgSender(), _index);
_checkNoPendingTrigger(tradeId, ITradingStorage.PendingOrderType.TP_CLOSE);
_checkNoPendingTrigger(tradeId, ITradingStorage.PendingOrderType.SL_CLOSE);
_getMultiCollatDiamond().updateTradeMaxClosingSlippageP(tradeId, _maxClosingSlippageP);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function closeTradeMarket(uint32 _index, uint64 _expectedPrice) external tradingActivatedOrCloseOnly {
if (_expectedPrice == 0) revert IGeneralErrors.ZeroValue();
address sender = _msgSender();
TradingCommonUtils.revertIfTradeHasPendingMarketOrder(sender, _index);
ITradingStorage.Trade memory t = TradingCommonUtils.getTrade(sender, _index);
ITradingStorage.PendingOrder memory pendingOrder;
pendingOrder.trade.user = t.user;
pendingOrder.trade.index = t.index;
pendingOrder.trade.pairIndex = t.pairIndex;
pendingOrder.trade.openPrice = _expectedPrice;
pendingOrder.user = sender;
pendingOrder.orderType = ITradingStorage.PendingOrderType.MARKET_CLOSE;
ITradingStorage.Id memory orderId = _getMultiCollatDiamond().getPrice(
IPriceAggregator.GetPriceInput(
t.collateralIndex,
t.pairIndex,
pendingOrder,
TradingCommonUtils.getPositionSizeCollateral(t.collateralAmount, t.leverage),
ChainUtils.getBlockNumber(),
t.isCounterTrade
)
);
emit ITradingInteractionsUtils.MarketOrderInitiated(orderId, sender, t.pairIndex, false);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function updateOpenOrder(
uint32 _index,
uint64 _openPrice,
uint64 _tp,
uint64 _sl,
uint16 _maxSlippageP
) external tradingActivated {
address sender = _msgSender();
ITradingStorage.Trade memory o = TradingCommonUtils.getTrade(sender, _index);
_checkNoPendingTrigger(
ITradingStorage.Id({user: o.user, index: o.index}),
ConstantsUtils.getPendingOpenOrderType(o.tradeType)
);
_getMultiCollatDiamond().updateOpenOrderDetails(
ITradingStorage.Id({user: o.user, index: o.index}),
_openPrice,
_tp,
_sl,
_maxSlippageP
);
emit ITradingInteractionsUtils.OpenLimitUpdated(
sender,
o.pairIndex,
_index,
_openPrice,
_tp,
_sl,
_maxSlippageP
);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function cancelOpenOrder(uint32 _index) external tradingActivatedOrCloseOnly {
address sender = _msgSender();
ITradingStorage.Trade memory o = TradingCommonUtils.getTrade(sender, _index);
ITradingStorage.Id memory tradeId = ITradingStorage.Id({user: o.user, index: o.index});
if (o.tradeType == ITradingStorage.TradeType.TRADE) revert IGeneralErrors.WrongTradeType();
_checkNoPendingTrigger(tradeId, ConstantsUtils.getPendingOpenOrderType(o.tradeType));
_getMultiCollatDiamond().closeTrade(tradeId, false, 0);
TradingCommonUtils.transferCollateralTo(o.collateralIndex, sender, o.collateralAmount);
emit ITradingInteractionsUtils.OpenLimitCanceled(sender, o.pairIndex, _index);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function updateTp(uint32 _index, uint64 _newTp) external tradingActivated {
address sender = _msgSender();
ITradingStorage.Trade memory t = TradingCommonUtils.getTrade(sender, _index);
ITradingStorage.Id memory tradeId = ITradingStorage.Id({user: t.user, index: t.index});
_checkNoPendingTrigger(tradeId, ITradingStorage.PendingOrderType.TP_CLOSE);
_getMultiCollatDiamond().updateTradeTp(tradeId, _newTp);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function updateSl(uint32 _index, uint64 _newSl) external tradingActivated {
address sender = _msgSender();
ITradingStorage.Trade memory t = TradingCommonUtils.getTrade(sender, _index);
ITradingStorage.Id memory tradeId = ITradingStorage.Id({user: t.user, index: t.index});
_checkNoPendingTrigger(tradeId, ITradingStorage.PendingOrderType.SL_CLOSE);
_getMultiCollatDiamond().updateTradeSl(tradeId, _newSl);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function updateLeverage(uint32 _index, uint24 _newLeverage) external tradingActivated {
_updateLeverage(_index, _newLeverage, false, 0);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function updateLeverageNative(uint32 _index, uint24 _newLeverage) external tradingActivated notDelegatedAction {
// If msg.value is 0, process as non-native.
if (msg.value == 0) {
_updateLeverage(_index, _newLeverage, false, 0);
return;
}
// Track native balance available and ensure collateralIndex is a valid native token
uint8 collateralIndex = TradingCommonUtils.getTrade(_msgSender(), _index).collateralIndex;
uint120 nativeBalance = _wrapNativeToken(collateralIndex);
// Perform updateLeverage request
uint120 unusedBalance = _updateLeverage(_index, _newLeverage, true, nativeBalance);
// Return any remaining balance to the sender
TradingCommonUtils.transferCollateralTo(collateralIndex, _msgSender(), unusedBalance);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function increasePositionSize(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice,
uint16 _maxSlippageP
) external tradingActivated {
_increasePositionSize(_index, _collateralDelta, _leverageDelta, _expectedPrice, _maxSlippageP, false);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function increasePositionSizeNative(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice,
uint16 _maxSlippageP
) external tradingActivated notDelegatedAction {
// If msg.value is zero then redirect the call to the non-native version of this function
if (msg.value == 0) {
return
_increasePositionSize(_index, _collateralDelta, _leverageDelta, _expectedPrice, _maxSlippageP, false);
}
// Ensure `_collateralDelta` matches msg.value and that collateralIndex is a valid native token
_collateralDelta = _wrapNativeToken(TradingCommonUtils.getTrade(_msgSender(), _index).collateralIndex);
// Process position size increase request
_increasePositionSize(_index, _collateralDelta, _leverageDelta, _expectedPrice, _maxSlippageP, true);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function decreasePositionSize(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice
) external tradingActivatedOrCloseOnly {
UpdatePositionSizeLifecycles.requestDecreasePositionSize(
IUpdatePositionSize.DecreasePositionSizeInput(
_msgSender(),
_index,
_collateralDelta,
_leverageDelta,
_expectedPrice
)
);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function withdrawPositivePnl(uint32 _index, uint120 _amountCollateral) external tradingActivatedOrCloseOnly {
if (_amountCollateral == 0) revert IGeneralErrors.ZeroValue();
address sender = _msgSender();
TradingCommonUtils.revertIfTradeHasPendingMarketOrder(sender, _index);
ITradingStorage.Trade memory t = TradingCommonUtils.getTrade(sender, _index);
if (!t.isOpen) revert ITradingInteractionsUtils.NoTrade();
if (t.tradeType != ITradingStorage.TradeType.TRADE) revert IGeneralErrors.WrongTradeType();
ITradingStorage.PendingOrder memory pendingOrder;
pendingOrder.trade.user = t.user;
pendingOrder.trade.index = t.index;
pendingOrder.trade.collateralAmount = _amountCollateral;
pendingOrder.user = sender;
pendingOrder.orderType = ITradingStorage.PendingOrderType.PNL_WITHDRAWAL;
ITradingStorage.Id memory orderId = _getMultiCollatDiamond().getPrice(
IPriceAggregator.GetPriceInput(
t.collateralIndex,
t.pairIndex,
pendingOrder,
TradingCommonUtils.getMinPositionSizeCollateral(t.collateralIndex, t.pairIndex) / 2,
ChainUtils.getBlockNumber(),
t.isCounterTrade
)
);
emit ITradingInteractionsUtils.PositivePnlWithdrawalInitiated(orderId, sender, _index, _amountCollateral);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function triggerOrder(uint256 _packed) external notDelegatedAction {
(
address sender,
bool byPassesLinkCost,
ITradingStorage.Trade memory t,
ITradingStorage.PendingOrderType orderType
) = _validateTriggerOrder(_packed);
if (!byPassesLinkCost) {
IERC20(_getMultiCollatDiamond().getChainlinkToken()).safeTransferFrom(
sender,
address(this),
FIXED_LINK_PER_TRIGGER
);
}
ITradingStorage.PendingOrder memory pendingOrder;
pendingOrder.trade.user = t.user;
pendingOrder.trade.index = t.index;
pendingOrder.trade.pairIndex = t.pairIndex;
pendingOrder.user = sender;
pendingOrder.orderType = orderType;
ITradingStorage.Id memory orderId = _getPriceTriggerOrder(t, pendingOrder);
emit ITradingInteractionsUtils.TriggerOrderInitiated(orderId, t.user, t.pairIndex, byPassesLinkCost);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function triggerOrderWithSignatures(
uint256 _packed,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) external notDelegatedAction {
(
address sender,
bool byPassesLinkCost,
ITradingStorage.Trade memory t,
ITradingStorage.PendingOrderType orderType
) = _validateTriggerOrder(_packed);
if (!byPassesLinkCost) revert IGeneralErrors.NotAuthorized();
// Execute trigger. Signature validation is done in this function's modifier
_executeTriggerOrderWithSignature(sender, t, orderType, _signedPairPrices);
}
function _executeTriggerOrderWithSignature(
address _sender,
ITradingStorage.Trade memory _t,
ITradingStorage.PendingOrderType _orderType,
IPriceAggregator.SignedPairPrices[] calldata _signedPairPrices
) internal useSignedLookbackPrices(_signedPairPrices) {
ITradingCallbacks.AggregatorAnswer memory finalAnswer = _getMultiCollatDiamond().getPairSignedMedianTemporary(
_t.pairIndex
);
if (
_signedPairPrices[0].fromBlock !=
_getMultiCollatDiamond().getLookbackFromBlock(_t.user, _t.index, _orderType)
) revert ITradingInteractionsUtils.WrongFromBlock();
if (
_orderType == ITradingStorage.PendingOrderType.LIMIT_OPEN ||
_orderType == ITradingStorage.PendingOrderType.STOP_OPEN
) {
_getMultiCollatDiamond().executeTriggerOpenOrderCallbackDirect(finalAnswer, _t, _orderType, _sender);
} else if (
_orderType == ITradingStorage.PendingOrderType.TP_CLOSE ||
_orderType == ITradingStorage.PendingOrderType.SL_CLOSE ||
_orderType == ITradingStorage.PendingOrderType.LIQ_CLOSE
) {
_getMultiCollatDiamond().executeTriggerCloseOrderCallbackDirect(finalAnswer, _t, _orderType, _sender);
}
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function cancelOrderAfterTimeout(uint32 _orderIndex) external tradingActivatedOrCloseOnly {
address sender = _msgSender();
ITradingStorage.Id memory orderId = ITradingStorage.Id({user: sender, index: _orderIndex});
ITradingStorage.PendingOrder memory order = _getMultiCollatDiamond().getPendingOrder(orderId);
ITradingStorage.Trade memory pendingTrade = order.trade;
ITradingStorage.Trade memory trade = TradingCommonUtils.getTrade(pendingTrade.user, pendingTrade.index);
if (!order.isOpen) revert ITradingInteractionsUtils.NoOrder();
if (!ConstantsUtils.isOrderTypeMarket(order.orderType)) revert IGeneralErrors.WrongOrderType();
if (ChainUtils.getBlockNumber() < order.createdBlock + _getStorage().marketOrdersTimeoutBlocks)
revert ITradingInteractionsUtils.WaitTimeout();
_getMultiCollatDiamond().closePendingOrder(orderId);
bool isMarketOpen = order.orderType == ITradingStorage.PendingOrderType.MARKET_OPEN;
if (
isMarketOpen ||
(order.orderType == ITradingStorage.PendingOrderType.UPDATE_LEVERAGE &&
pendingTrade.leverage < trade.leverage) ||
order.orderType == ITradingStorage.PendingOrderType.MARKET_PARTIAL_OPEN
) {
uint8 collateralIndex = isMarketOpen ? pendingTrade.collateralIndex : trade.collateralIndex;
TradingCommonUtils.transferCollateralTo(collateralIndex, pendingTrade.user, pendingTrade.collateralAmount);
emit ITradingInteractionsUtils.CollateralReturnedAfterTimeout(
orderId,
collateralIndex,
pendingTrade.user,
pendingTrade.collateralAmount
);
}
emit ITradingInteractionsUtils.ChainlinkCallbackTimeout(
orderId,
isMarketOpen ? pendingTrade.pairIndex : trade.pairIndex
);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function getWrappedNativeToken() internal view returns (address) {
return ChainUtils.getWrappedNativeToken();
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function isWrappedNativeToken(address _token) external view returns (bool) {
return ChainUtils.isWrappedNativeToken(_token);
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function getTradingDelegate(address _trader) external view returns (address) {
return _getStorage().delegations[_trader];
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function getMarketOrdersTimeoutBlocks() external view returns (uint16) {
return _getStorage().marketOrdersTimeoutBlocks;
}
/**
* @dev Check ITradingInteractionsUtils interface for documentation
*/
function getByPassTriggerLink(address _user) external view returns (bool) {
return _getStorage().byPassTriggerLink[_user];
}
/**
* @dev Returns storage slot to use when fetching storage relevant to library
*/
function _getSlot() internal pure returns (uint256) {
return StorageUtils.GLOBAL_TRADING_SLOT;
}
/**
* @dev Returns storage pointer for storage struct in diamond contract, at defined slot
*/
function _getStorage() internal pure returns (ITradingInteractions.TradingInteractionsStorage storage s) {
uint256 storageSlot = _getSlot();
assembly {
s.slot := storageSlot
}
}
/**
* @dev Returns current address as multi-collateral diamond interface to call other facets functions.
*/
function _getMultiCollatDiamond() internal view returns (IGNSMultiCollatDiamond) {
return IGNSMultiCollatDiamond(address(this));
}
/**
* @dev Internal function for openTrade and openTradeNative
* @param _trade trade data
* @param _maxSlippageP max slippage percentage (1e3 precision)
* @param _referrer referrer address
* @param _isNative if true we skip the collateral transfer from user to contract
*/
function _openTrade(
ITradingStorage.Trade memory _trade,
uint16 _maxSlippageP,
address _referrer,
bool _isNative
) internal {
address sender = _msgSender();
_trade.user = sender;
_trade.index = 0; // this will be defined once trade is stored
_trade.positionSizeToken = 0; // will be overriden in callback
_trade.__placeholder = 0;
uint256 positionSizeCollateral = TradingCommonUtils.getPositionSizeCollateral(
_trade.collateralAmount,
_trade.leverage
);
// Trade collateral needs to be >= 5x min trading fee
if (
_trade.collateralAmount <
TradingCommonUtils.getPairMinOpeningCollateral(_trade.collateralIndex, _trade.pairIndex)
) revert ITradingInteractionsUtils.InsufficientCollateral();
if (
_trade.leverage < _getMultiCollatDiamond().pairMinLeverage(_trade.pairIndex) ||
_trade.leverage > _getMultiCollatDiamond().pairMaxLeverage(_trade.pairIndex)
) revert ITradingInteractionsUtils.WrongLeverage();
if (
(_trade.isCounterTrade &&
_trade.leverage > _getMultiCollatDiamond().getPairCounterTradeMaxLeverage(_trade.pairIndex))
) revert IGeneralErrors.WrongTradeType();
ITradingCommonUtils.TradePriceImpact memory priceImpact = TradingCommonUtils.getTradeOpeningPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(_trade, _trade.openPrice, positionSizeCollateral, 0, true)
);
if (
TradingCommonUtils.getNegativePnlFromOpeningPriceImpactP(
priceImpact.fixedSpreadP,
priceImpact.cumulVolPriceImpactP,
_trade.leverage,
_trade.long
) > int256(ConstantsUtils.MAX_OPEN_NEGATIVE_PNL_P)
) revert ITradingInteractionsUtils.PriceImpactTooHigh();
if (!_isNative)
TradingCommonUtils.transferCollateralFrom(_trade.collateralIndex, sender, _trade.collateralAmount);
if (_trade.tradeType != ITradingStorage.TradeType.TRADE) {
ITradingStorage.TradeInfo memory tradeInfo;
tradeInfo.maxSlippageP = _maxSlippageP;
_trade = _getMultiCollatDiamond().storeTrade(_trade, tradeInfo, 0);
emit ITradingInteractionsUtils.OpenOrderPlaced(sender, _trade.pairIndex, _trade.index);
} else {
ITradingStorage.PendingOrder memory pendingOrder;
pendingOrder.trade = _trade;
pendingOrder.user = sender;
pendingOrder.orderType = ITradingStorage.PendingOrderType.MARKET_OPEN;
pendingOrder.maxSlippageP = _maxSlippageP;
ITradingStorage.Id memory orderId = _getMultiCollatDiamond().getPrice(
IPriceAggregator.GetPriceInput(
_trade.collateralIndex,
_trade.pairIndex,
pendingOrder,
positionSizeCollateral,
ChainUtils.getBlockNumber(),
_trade.isCounterTrade
)
);
emit ITradingInteractionsUtils.MarketOrderInitiated(orderId, sender, _trade.pairIndex, true);
}
if (_referrer != address(0)) {
_getMultiCollatDiamond().registerPotentialReferrer(sender, _referrer);
}
}
/**
* @dev Internal function for updateLeverage and updateLeverageNative
* @param _index index of trade
* @param _newLeverage new leverage (1e3)
* @param _isNative whether the collateral transfer is with native tokens
* @param _nativeValue how much native collateral is available
*/
function _updateLeverage(
uint32 _index,
uint24 _newLeverage,
bool _isNative,
uint120 _nativeValue
) internal returns (uint120) {
return
UpdateLeverageLifecycles.requestUpdateLeverage(
IUpdateLeverage.UpdateLeverageInput(_msgSender(), _index, _newLeverage),
_isNative,
_nativeValue
);
}
/**
* @dev Internal function for increasePositionSize and increasePositionSizeNative
* @param _index index of trade
* @param _collateralDelta collateral to add (collateral precision)
* @param _leverageDelta partial trade leverage (1e3)
* @param _expectedPrice expected price of execution (1e10 precision)
* @param _maxSlippageP max slippage % (1e3)
* @param _isNative whether the collateral transfer is native
*/
function _increasePositionSize(
uint32 _index,
uint120 _collateralDelta,
uint24 _leverageDelta,
uint64 _expectedPrice,
uint16 _maxSlippageP,
bool _isNative
) internal {
UpdatePositionSizeLifecycles.requestIncreasePositionSize(
IUpdatePositionSize.IncreasePositionSizeInput(
_msgSender(),
_index,
_collateralDelta,
_leverageDelta,
_expectedPrice,
_maxSlippageP
),
_isNative
);
}
/**
* @dev Revert if there is an active pending order for the trade
* @param _tradeId trade id
* @param _orderType order type
*/
function _checkNoPendingTrigger(
ITradingStorage.Id memory _tradeId,
ITradingStorage.PendingOrderType _orderType
) internal view {
if (
_getMultiCollatDiamond().hasActiveOrder(
_getMultiCollatDiamond().getTradePendingOrderBlock(_tradeId, _orderType)
)
) revert ITradingInteractionsUtils.PendingTrigger();
}
/**
* @dev Validates trigger order
* @param _packed packed trigger order data
*/
function _validateTriggerOrder(
uint256 _packed
)
internal
view
returns (
address sender,
bool byPassesLinkCost,
ITradingStorage.Trade memory t,
ITradingStorage.PendingOrderType orderType
)
{
(uint8 _orderType, address _trader, uint32 _index) = _packed.unpackTriggerOrder();
orderType = ITradingStorage.PendingOrderType(_orderType);
if (ConstantsUtils.isOrderTypeMarket(orderType)) revert IGeneralErrors.WrongOrderType();
bool isOpenLimit = orderType == ITradingStorage.PendingOrderType.LIMIT_OPEN ||
orderType == ITradingStorage.PendingOrderType.STOP_OPEN;
ITradingStorage.TradingActivated activated = _getMultiCollatDiamond().getTradingActivated();
if (
(isOpenLimit && activated != ITradingStorage.TradingActivated.ACTIVATED) ||
(!isOpenLimit && activated == ITradingStorage.TradingActivated.PAUSED)
) {
revert IGeneralErrors.Paused();
}
t = TradingCommonUtils.getTrade(_trader, _index);
if (!t.isOpen) revert ITradingInteractionsUtils.NoTrade();
_checkNoPendingTrigger(ITradingStorage.Id({user: t.user, index: t.index}), orderType);
sender = msg.sender; // can't be a delegated action anyway
byPassesLinkCost = _getStorage().byPassTriggerLink[sender];
uint256 positionSizeCollateral = TradingCommonUtils.getPositionSizeCollateral(t.collateralAmount, t.leverage);
if (isOpenLimit) {
ITradingCommonUtils.TradePriceImpact memory priceImpact = TradingCommonUtils.getTradeOpeningPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(t, t.openPrice, positionSizeCollateral, 0, true)
);
if (
TradingCommonUtils.getNegativePnlFromOpeningPriceImpactP(
priceImpact.fixedSpreadP,
priceImpact.cumulVolPriceImpactP,
t.leverage,
t.long
) > int256(ConstantsUtils.MAX_OPEN_NEGATIVE_PNL_P)
) revert ITradingInteractionsUtils.PriceImpactTooHigh();
}
_getMultiCollatDiamond().validateOpenTradeOrder(t, orderType);
}
/**
* @dev Initiate price aggregator request for trigger order
* @param _trade trade
* @param _pendingOrder pending order to store
*/
function _getPriceTriggerOrder(
ITradingStorage.Trade memory _trade,
ITradingStorage.PendingOrder memory _pendingOrder
) internal returns (ITradingStorage.Id memory) {
return
_getMultiCollatDiamond().getPrice(
IPriceAggregator.GetPriceInput(
_trade.collateralIndex,
_trade.pairIndex,
_pendingOrder,
0,
_getMultiCollatDiamond().getLookbackFromBlock(_trade.user, _trade.index, _pendingOrder.orderType),
_trade.isCounterTrade
)
);
}
/**
* @dev Receives native token and sends back wrapped token to user
* @param _collateralIndex index of the collateral
*/
function _wrapNativeToken(uint8 _collateralIndex) internal returns (uint120) {
address collateral = _getMultiCollatDiamond().getCollateral(_collateralIndex).collateral;
uint256 nativeValue = msg.value;
if (nativeValue == 0) {
revert IGeneralErrors.ZeroValue();
}
if (nativeValue > type(uint120).max) {
revert IGeneralErrors.Overflow();
}
if (!ChainUtils.isWrappedNativeToken(collateral)) {
revert ITradingInteractionsUtils.NotWrappedNativeToken();
}
IWETH9(collateral).deposit{value: nativeValue}();
emit ITradingInteractionsUtils.NativeTokenWrapped(msg.sender, nativeValue);
return uint120(nativeValue);
}
/**
* @dev Returns the caller of the transaction (overriden by trader address if delegatedAction is called)
*/
function _msgSender() internal view returns (address) {
address senderOverride = _getStorage().senderOverride;
if (senderOverride == address(0)) {
return msg.sender;
} else {
return senderOverride;
}
}
/**
* @dev Modifier helper for `tradingActivated()`. Saves bytecode size.
*/
function _tradingActivated() internal view {
if (_getMultiCollatDiamond().getTradingActivated() != ITradingStorage.TradingActivated.ACTIVATED)
revert IGeneralErrors.Paused();
}
/**
* @dev Modifier helper for `tradingActivatedOrCloseOnly()`. Saves bytecode size.
*/
function _tradingActivatedOrCloseOnly() internal view {
if (_getMultiCollatDiamond().getTradingActivated() == ITradingStorage.TradingActivated.PAUSED)
revert IGeneralErrors.Paused();
}
/**
* @dev Modifier helper for `notDelegatedAction()`. Saves bytecode size.
*/
function _notDelegatedAction() internal view {
if (_getStorage().senderOverride != address(0)) revert ITradingInteractionsUtils.DelegatedActionNotAllowed();
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../interfaces/IGNSMultiCollatDiamond.sol";
import "./StorageUtils.sol";
/**
* @dev External library for array getters to save bytecode size in facet libraries
*/
library TradingStorageGetters {
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getCollateral(uint8 _index) external view returns (ITradingStorage.Collateral memory) {
return _getStorage().collaterals[_index];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function isCollateralActive(uint8 _index) public view returns (bool) {
return _getStorage().collaterals[_index].isActive;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function isCollateralListed(uint8 _index) external view returns (bool) {
return _getStorage().collaterals[_index].precision > 0;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function isCollateralGns(uint8 _index) external view returns (bool) {
uint8 gnsIndex = _getStorage().gnsCollateralIndex;
return gnsIndex > 0 && gnsIndex == _index;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getCollateralsCount() external view returns (uint8) {
return _getStorage().lastCollateralIndex;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getCollaterals() external view returns (ITradingStorage.Collateral[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
ITradingStorage.Collateral[] memory collaterals = new ITradingStorage.Collateral[](s.lastCollateralIndex);
for (uint8 i = 1; i <= s.lastCollateralIndex; ++i) {
collaterals[i - 1] = s.collaterals[i];
}
return collaterals;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getCollateralIndex(address _collateral) external view returns (uint8) {
return _getStorage().collateralIndex[_collateral];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getGnsCollateralIndex() external view returns (uint8) {
return _getStorage().gnsCollateralIndex;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTradingActivated() external view returns (ITradingStorage.TradingActivated) {
return _getStorage().tradingActivated;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTraderStored(address _trader) external view returns (bool) {
return _getStorage().traderStored[_trader];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTradersCount() external view returns (uint256) {
return _getStorage().traders.length;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTraders(uint32 _offset, uint32 _limit) public view returns (address[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
if (s.traders.length == 0) return new address[](0);
uint256 lastIndex = s.traders.length - 1;
_limit = _limit == 0 || _limit > lastIndex ? uint32(lastIndex) : _limit;
address[] memory traders = new address[](_limit - _offset + 1);
uint32 currentIndex;
for (uint32 i = _offset; i <= _limit; ++i) {
address trader = s.traders[i];
if (
s.userCounters[trader][ITradingStorage.CounterType.TRADE].openCount > 0 ||
s.userCounters[trader][ITradingStorage.CounterType.PENDING_ORDER].openCount > 0
) {
traders[currentIndex++] = trader;
}
}
return traders;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTrade(address _trader, uint32 _index) internal view returns (ITradingStorage.Trade memory) {
return _getStorage().trades[_trader][_index];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTrades(address _trader) public view returns (ITradingStorage.Trade[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
ITradingStorage.Counter memory traderCounter = s.userCounters[_trader][ITradingStorage.CounterType.TRADE];
ITradingStorage.Trade[] memory trades = new ITradingStorage.Trade[](traderCounter.openCount);
uint32 currentIndex;
for (uint32 i; i < traderCounter.currentIndex; ++i) {
ITradingStorage.Trade storage trade = s.trades[_trader][i];
if (trade.isOpen) {
trades[currentIndex++] = trade;
// Exit loop if all open trades have been found
if (currentIndex == traderCounter.openCount) break;
}
}
return trades;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getAllTradesForTraders(
address[] memory _traders,
uint256 _offset,
uint256 _limit
) public view returns (ITradingStorage.Trade[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
uint256 currentTradeIndex; // current global trade index
uint256 currentArrayIndex; // current index in returned trades array
ITradingStorage.Trade[] memory trades = new ITradingStorage.Trade[](_limit - _offset + 1);
// Fetch all trades for each trader
for (uint256 i; i < _traders.length; ++i) {
// Exit loop if limit is reached
if (currentTradeIndex > _limit) break;
// Skip if next trader address is 0; `getTraders` can return address(0)
address trader = _traders[i];
if (trader == address(0)) continue;
// Fetch trader trade counter
ITradingStorage.Counter memory traderCounter = s.userCounters[trader][ITradingStorage.CounterType.TRADE];
// Exit if user has no open trades
// We check because `getTraders` also traders with pending orders
if (traderCounter.openCount == 0) continue;
// If current trade index + openCount is lte to offset, skip to next trader
if (currentTradeIndex + traderCounter.openCount <= _offset) {
currentTradeIndex += traderCounter.openCount;
continue;
}
ITradingStorage.Trade[] memory traderTrades = getTrades(trader);
// Add trader trades to final trades array only if within _offset and _limit
for (uint256 j; j < traderTrades.length; ++j) {
if (currentTradeIndex > _limit) break; // Exit loop if limit is reached
// Only process trade if currentTradeIndex is >= offset
if (currentTradeIndex >= _offset) {
trades[currentArrayIndex++] = traderTrades[j];
}
currentTradeIndex++;
}
}
return trades;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getAllTrades(uint256 _offset, uint256 _limit) external view returns (ITradingStorage.Trade[] memory) {
// Fetch all traders with open trades (no pagination, return size is not an issue here)
address[] memory traders = getTraders(0, 0);
return getAllTradesForTraders(traders, _offset, _limit);
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTradeInfo(address _trader, uint32 _index) internal view returns (ITradingStorage.TradeInfo memory) {
return _getStorage().tradeInfos[_trader][_index];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTradeInfos(address _trader) public view returns (ITradingStorage.TradeInfo[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
ITradingStorage.Counter memory traderCounter = s.userCounters[_trader][ITradingStorage.CounterType.TRADE];
ITradingStorage.TradeInfo[] memory tradeInfos = new ITradingStorage.TradeInfo[](traderCounter.openCount);
uint32 currentIndex;
for (uint32 i; i < traderCounter.currentIndex; ++i) {
if (s.trades[_trader][i].isOpen) {
tradeInfos[currentIndex++] = s.tradeInfos[_trader][i];
// Exit loop if all open trade infos have been found
if (currentIndex == traderCounter.openCount) break;
}
}
return tradeInfos;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getAllTradeInfosForTraders(
address[] memory _traders,
uint256 _offset,
uint256 _limit
) public view returns (ITradingStorage.TradeInfo[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
uint256 currentTradeIndex; // current global trade index
uint256 currentArrayIndex; // current index in returned trades array
ITradingStorage.TradeInfo[] memory tradesInfos = new ITradingStorage.TradeInfo[](_limit - _offset + 1);
// Fetch all trades for each trader
for (uint256 i; i < _traders.length; ++i) {
// Exit loop if limit is reached
if (currentTradeIndex > _limit) break;
// Skip if next trader address is 0; `getTraders` can return address(0)
address trader = _traders[i];
if (trader == address(0)) continue;
// Fetch trader trade counter
ITradingStorage.Counter memory traderCounter = s.userCounters[trader][ITradingStorage.CounterType.TRADE];
// Exit if user has no open trades
// We check because `getTraders` also traders with pending orders
if (traderCounter.openCount == 0) continue;
// If current trade index + openCount is lte to offset, skip to next trader
if (currentTradeIndex + traderCounter.openCount <= _offset) {
currentTradeIndex += traderCounter.openCount;
continue;
}
ITradingStorage.TradeInfo[] memory traderTradesInfos = getTradeInfos(trader);
// Add trader trades to final trades array only if within _offset and _limit
for (uint256 j; j < traderTradesInfos.length; ++j) {
if (currentTradeIndex > _limit) break; // Exit loop if limit is reached
if (currentTradeIndex >= _offset) {
tradesInfos[currentArrayIndex++] = traderTradesInfos[j];
}
currentTradeIndex++;
}
}
return tradesInfos;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getAllTradeInfos(
uint256 _offset,
uint256 _limit
) external view returns (ITradingStorage.TradeInfo[] memory) {
// Fetch all traders with open trades (no pagination, return size is not an issue here)
address[] memory traders = getTraders(0, 0);
return getAllTradeInfosForTraders(traders, _offset, _limit);
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getPendingOrder(
ITradingStorage.Id memory _orderId
) external view returns (ITradingStorage.PendingOrder memory) {
return _getStorage().pendingOrders[_orderId.user][_orderId.index];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getPendingOrders(address _trader) public view returns (ITradingStorage.PendingOrder[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
ITradingStorage.Counter memory traderCounter = s.userCounters[_trader][
ITradingStorage.CounterType.PENDING_ORDER
];
ITradingStorage.PendingOrder[] memory pendingOrders = new ITradingStorage.PendingOrder[](
traderCounter.openCount
);
// Return early
if (traderCounter.openCount == 0) return pendingOrders;
uint32 currentIndex;
for (uint32 i; i < traderCounter.currentIndex; ++i) {
if (s.pendingOrders[_trader][i].isOpen) {
pendingOrders[currentIndex++] = s.pendingOrders[_trader][i];
// Exit loop if all open pending orders have been found
if (currentIndex == traderCounter.openCount) break;
}
}
return pendingOrders;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getAllPendingOrdersForTraders(
address[] memory _traders,
uint256 _offset,
uint256 _limit
) public view returns (ITradingStorage.PendingOrder[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
uint256 currentPendingOrderIndex; // current global pending order index
uint256 currentArrayIndex; // current index in returned pending orders array
ITradingStorage.PendingOrder[] memory pendingOrders = new ITradingStorage.PendingOrder[](_limit - _offset + 1);
// Fetch all trades for each trader
for (uint256 i; i < _traders.length; ++i) {
// Exit loop if limit is reached
if (currentPendingOrderIndex > _limit) break;
// Skip if next trader address is 0; `getTraders` can return address(0)
address trader = _traders[i];
if (trader == address(0)) continue;
// Fetch trader trade counter
ITradingStorage.Counter memory traderCounter = s.userCounters[trader][
ITradingStorage.CounterType.PENDING_ORDER
];
// Exit if user has no open pending orders
// We check because `getTraders` also traders with pending orders
if (traderCounter.openCount == 0) continue;
// If current trade index + openCount is lte to offset, skip to next trader
if (currentPendingOrderIndex + traderCounter.openCount <= _offset) {
currentPendingOrderIndex += traderCounter.openCount;
continue;
}
ITradingStorage.PendingOrder[] memory traderPendingOrders = getPendingOrders(trader);
// Add trader trades to final trades array only if within _offset and _limit
for (uint256 j; j < traderPendingOrders.length; ++j) {
if (currentPendingOrderIndex > _limit) break; // Exit loop if limit is reached
if (currentPendingOrderIndex >= _offset) {
pendingOrders[currentArrayIndex++] = traderPendingOrders[j];
}
currentPendingOrderIndex++;
}
}
return pendingOrders;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getAllPendingOrders(
uint256 _offset,
uint256 _limit
) external view returns (ITradingStorage.PendingOrder[] memory) {
// Fetch all traders with open trades (no pagination, return size is not an issue here)
address[] memory traders = getTraders(0, 0);
return getAllPendingOrdersForTraders(traders, _offset, _limit);
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTradePendingOrderBlock(
ITradingStorage.Id memory _tradeId,
ITradingStorage.PendingOrderType _orderType
) external view returns (uint256) {
return _getStorage().tradePendingOrderBlock[_tradeId.user][_tradeId.index][_orderType];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getCounters(
address _trader,
ITradingStorage.CounterType _type
) external view returns (ITradingStorage.Counter memory) {
return _getStorage().userCounters[_trader][_type];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getCountersForTraders(
address[] calldata _traders,
ITradingStorage.CounterType _counterType
) external view returns (ITradingStorage.Counter[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
ITradingStorage.Counter[] memory counters = new ITradingStorage.Counter[](_traders.length);
for (uint256 i; i < _traders.length; ++i) {
counters[i] = s.userCounters[_traders[i]][_counterType];
}
return counters;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getGToken(uint8 _collateralIndex) external view returns (address) {
return _getStorage().gTokens[_collateralIndex];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTradeLiquidationParams(
address _trader,
uint32 _index
) external view returns (IPairsStorage.GroupLiquidationParams memory) {
return _getStorage().tradeLiquidationParams[_trader][_index];
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTradesLiquidationParams(
address _trader
) public view returns (IPairsStorage.GroupLiquidationParams[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
ITradingStorage.Counter memory traderCounter = s.userCounters[_trader][ITradingStorage.CounterType.TRADE];
IPairsStorage.GroupLiquidationParams[]
memory tradeLiquidationParams = new IPairsStorage.GroupLiquidationParams[](traderCounter.openCount);
uint32 currentIndex;
for (uint32 i; i < traderCounter.currentIndex; ++i) {
if (s.trades[_trader][i].isOpen) {
tradeLiquidationParams[currentIndex++] = s.tradeLiquidationParams[_trader][i];
// Exit loop if all open trades have been found
if (currentIndex == traderCounter.openCount) break;
}
}
return tradeLiquidationParams;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getAllTradesLiquidationParamsForTraders(
address[] memory _traders,
uint256 _offset,
uint256 _limit
) public view returns (IPairsStorage.GroupLiquidationParams[] memory) {
ITradingStorage.TradingStorage storage s = _getStorage();
uint256 currentTradeLiquidationParamIndex; // current global trade liquidation params index
uint256 currentArrayIndex; // current index in returned trade liquidation params array
IPairsStorage.GroupLiquidationParams[]
memory tradeLiquidationParams = new IPairsStorage.GroupLiquidationParams[](_limit - _offset + 1);
// Fetch all trades for each trader
for (uint256 i; i < _traders.length; ++i) {
// Exit loop if limit is reached
if (currentTradeLiquidationParamIndex > _limit) break;
// Skip if next trader address is 0; `getTraders` can return address(0)
address trader = _traders[i];
if (trader == address(0)) continue;
// Fetch trader trade counter
ITradingStorage.Counter memory traderCounter = s.userCounters[trader][ITradingStorage.CounterType.TRADE];
// Exit if user has no open trades
// We check because `getTraders` also traders with pending orders
if (traderCounter.openCount == 0) continue;
// If current trade index + openCount is lte to offset, skip to next trader
if (currentTradeLiquidationParamIndex + traderCounter.openCount <= _offset) {
currentTradeLiquidationParamIndex += traderCounter.openCount;
continue;
}
IPairsStorage.GroupLiquidationParams[] memory traderLiquidationParams = getTradesLiquidationParams(trader);
// Add trader trades to final trades array only if within _offset and _limit
for (uint256 j; j < traderLiquidationParams.length; ++j) {
if (currentTradeLiquidationParamIndex > _limit) break; // Exit loop if limit is reached
if (currentTradeLiquidationParamIndex >= _offset) {
tradeLiquidationParams[currentArrayIndex++] = traderLiquidationParams[j];
}
currentTradeLiquidationParamIndex++;
}
}
return tradeLiquidationParams;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getAllTradesLiquidationParams(
uint256 _offset,
uint256 _limit
) external view returns (IPairsStorage.GroupLiquidationParams[] memory) {
// Fetch all traders with open trades (no pagination, return size is not an issue here)
address[] memory traders = getTraders(0, 0);
return getAllTradesLiquidationParamsForTraders(traders, _offset, _limit);
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getCurrentContractsVersion() external pure returns (ITradingStorage.ContractsVersion) {
return ITradingStorage.ContractsVersion.V10;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getTradeContractsVersion(
address _trader,
uint32 _index
) external view returns (ITradingStorage.ContractsVersion) {
return _getStorage().tradeInfos[_trader][_index].contractsVersion;
}
/**
* @dev Check ITradingStorageUtils interface for documentation
*/
function getLookbackFromBlock(
address _trader,
uint32 _index,
ITradingStorage.PendingOrderType _orderType
) external view returns (uint32) {
ITradingStorage.TradeInfo memory tradeInfo = getTradeInfo(_trader, _index);
return
_orderType == ITradingStorage.PendingOrderType.SL_CLOSE
? tradeInfo.slLastUpdatedBlock
: _orderType == ITradingStorage.PendingOrderType.TP_CLOSE
? tradeInfo.tpLastUpdatedBlock
: tradeInfo.createdBlock;
}
/**
* @dev Returns storage slot to use when fetching storage relevant to library
*/
function _getSlot() internal pure returns (uint256) {
return StorageUtils.GLOBAL_TRADING_STORAGE_SLOT;
}
/**
* @dev Returns storage pointer for storage struct in diamond contract, at defined slot
*/
function _getStorage() internal pure returns (ITradingStorage.TradingStorage storage s) {
uint256 storageSlot = _getSlot();
assembly {
s.slot := storageSlot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../../interfaces/IGNSMultiCollatDiamond.sol";
import "../TradingCommonUtils.sol";
/**
*
* @dev This is an external library for leverage update lifecycles
* @dev Used by GNSTrading and GNSTradingCallbacks facets
*/
library UpdateLeverageLifecycles {
/**
* @dev Initiate update leverage order, done in 2 steps because need to cancel if liquidation price reached
* @param _input request decrease leverage input
* @param _isNative whether this request is using native tokens
* @param _nativeBalance amount of native tokens available. Always 0 when `_isNative` is false, and >0 when true.
*/
function requestUpdateLeverage(
IUpdateLeverageUtils.UpdateLeverageInput memory _input,
bool _isNative,
uint120 _nativeBalance
) external returns (uint120) {
// 1. Request validation
(ITradingStorage.Trade memory trade, bool isIncrease, uint256 collateralDelta) = _validateRequest(_input);
// 2. If decrease leverage, transfer collateral delta to diamond
if (!isIncrease) {
// If it's native, we have already transferred the balance
if (_isNative) {
// If the request uses more balance (`collateralDelta`) than is available (`_nativeBalance`) then revert
if (collateralDelta > _nativeBalance) revert ITradingInteractionsUtils.InsufficientCollateral();
// Deduct `collateralDelta` from native collateral
_nativeBalance -= uint120(collateralDelta);
} else {
// Transfer in collateral
TradingCommonUtils.transferCollateralFrom(trade.collateralIndex, trade.user, collateralDelta);
}
}
// 3. Create pending order and make price aggregator request
ITradingStorage.Id memory orderId = _initiateRequest(trade, _input.newLeverage, collateralDelta);
emit IUpdateLeverageUtils.LeverageUpdateInitiated(
orderId,
_input.user,
trade.pairIndex,
_input.index,
isIncrease,
_input.newLeverage
);
// Return the amount of native collateral remaining
return _nativeBalance;
}
/**
* @dev Execute update leverage callback
* @param _order pending order struct
* @param _answer price aggregator request answer
*/
function executeUpdateLeverage(
ITradingStorage.PendingOrder memory _order,
ITradingCallbacks.AggregatorAnswer memory _answer
) external {
// 1. Prepare values
ITradingStorage.Trade memory pendingTrade = _order.trade;
ITradingStorage.Trade memory existingTrade = TradingCommonUtils.getTrade(pendingTrade.user, pendingTrade.index);
bool isIncrease = pendingTrade.leverage > existingTrade.leverage;
// 2. Refresh trader fee tier cache
TradingCommonUtils.updateFeeTierPoints(
existingTrade.collateralIndex,
existingTrade.user,
existingTrade.pairIndex,
0
);
// 3. Prepare useful values
IUpdateLeverageUtils.UpdateLeverageValues memory values = _prepareCallbackValues(
existingTrade,
pendingTrade,
isIncrease,
_answer
);
// 4. Callback validation
ITradingCallbacks.CancelReason cancelReason = _validateCallback(existingTrade, values, _answer, isIncrease);
// 5. Handle callback
_handleCallback(existingTrade, pendingTrade, values, cancelReason, isIncrease, _answer);
emit IUpdateLeverageUtils.LeverageUpdateExecuted(
_answer.orderId,
isIncrease,
cancelReason,
existingTrade.collateralIndex,
existingTrade.user,
existingTrade.pairIndex,
existingTrade.index,
_answer.current,
pendingTrade.collateralAmount,
values
);
}
/**
* @dev Returns current address as multi-collateral diamond interface to call other facets functions.
*/
function _getMultiCollatDiamond() internal view returns (IGNSMultiCollatDiamond) {
return IGNSMultiCollatDiamond(address(this));
}
/**
* @dev Returns new trade collateral amount based on new leverage (collateral precision)
* @param _existingCollateralAmount existing trade collateral amount (collateral precision)
* @param _existingLeverage existing trade leverage (1e3)
* @param _newLeverage new trade leverage (1e3)
*/
function _getNewCollateralAmount(
uint256 _existingCollateralAmount,
uint256 _existingLeverage,
uint256 _newLeverage
) internal pure returns (uint120) {
return uint120((_existingCollateralAmount * _existingLeverage) / _newLeverage);
}
/**
* @dev Fetches trade, does validation for update leverage request, and returns useful data
* @param _input request input struct
*/
function _validateRequest(
IUpdateLeverageUtils.UpdateLeverageInput memory _input
) internal view returns (ITradingStorage.Trade memory trade, bool isIncrease, uint256 collateralDelta) {
trade = TradingCommonUtils.getTrade(_input.user, _input.index);
isIncrease = _input.newLeverage > trade.leverage;
// 1. Check trade exists
if (!trade.isOpen) revert IGeneralErrors.DoesntExist();
// 2. Revert if any market order (market close, increase leverage, partial open, partial close) already exists for trade
TradingCommonUtils.revertIfTradeHasPendingMarketOrder(_input.user, _input.index);
// 3. Revert if collateral not active
if (!_getMultiCollatDiamond().isCollateralActive(trade.collateralIndex))
revert IGeneralErrors.InvalidCollateralIndex();
// 4. Validate leverage update
if (
_input.newLeverage == trade.leverage ||
!TradingCommonUtils.validateAdjustedInitialLeverage(_input.newLeverage)
) revert ITradingInteractionsUtils.WrongLeverage();
// 5. Validate new collateral amount (enough to pay min closing fee)
uint256 newCollateralAmount = _getNewCollateralAmount(
trade.collateralAmount,
trade.leverage,
_input.newLeverage
);
if (newCollateralAmount < TradingCommonUtils.getPairMinFeeCollateral(trade.collateralIndex, trade.pairIndex))
revert ITradingInteractionsUtils.InsufficientCollateral();
// 6. Calculate collateral delta
collateralDelta = isIncrease
? trade.collateralAmount - newCollateralAmount
: newCollateralAmount - trade.collateralAmount;
}
/**
* @dev Stores pending update leverage order and makes price aggregator request
* @param _trade trade struct
* @param _newLeverage new leverage (1e3)
* @param _collateralDelta trade collateral delta (collateral precision)
*/
function _initiateRequest(
ITradingStorage.Trade memory _trade,
uint24 _newLeverage,
uint256 _collateralDelta
) internal returns (ITradingStorage.Id memory) {
// 1. Store pending order
ITradingStorage.PendingOrder memory pendingOrder;
{
ITradingStorage.Trade memory pendingOrderTrade;
pendingOrderTrade.user = _trade.user;
pendingOrderTrade.index = _trade.index;
pendingOrderTrade.leverage = _newLeverage;
pendingOrderTrade.collateralAmount = uint120(_collateralDelta);
pendingOrder.trade = pendingOrderTrade;
pendingOrder.user = _trade.user;
pendingOrder.orderType = ITradingStorage.PendingOrderType.UPDATE_LEVERAGE;
}
// 2. Request price
return
_getMultiCollatDiamond().getPrice(
IPriceAggregator.GetPriceInput(
_trade.collateralIndex,
_trade.pairIndex,
pendingOrder,
TradingCommonUtils.getMinPositionSizeCollateral(_trade.collateralIndex, _trade.pairIndex) / 2,
0,
_trade.isCounterTrade
)
);
}
/**
* @dev Calculates values for callback
* @param _existingTrade existing trade struct
* @param _pendingTrade pending trade struct
* @param _isIncrease true if increase leverage, false if decrease leverage
* @param _answer price aggregator answer
*/
function _prepareCallbackValues(
ITradingStorage.Trade memory _existingTrade,
ITradingStorage.Trade memory _pendingTrade,
bool _isIncrease,
ITradingCallbacks.AggregatorAnswer memory _answer
) internal view returns (IUpdateLeverageUtils.UpdateLeverageValues memory values) {
if (_existingTrade.isOpen == false) return values;
values.newLeverage = _pendingTrade.leverage;
values.govFeeCollateral = TradingCommonUtils.getMinGovFeeCollateral(
_existingTrade.collateralIndex,
_existingTrade.user,
_existingTrade.pairIndex
);
values.newCollateralAmount = (
_isIncrease
? _existingTrade.collateralAmount - _pendingTrade.collateralAmount
: _existingTrade.collateralAmount + _pendingTrade.collateralAmount
);
values.existingLiqPrice = TradingCommonUtils.getTradeLiquidationPrice(_existingTrade, _answer.current);
values.newLiqPrice = TradingCommonUtils.getTradeLiquidationPrice(
_existingTrade,
_existingTrade.openPrice,
values.newCollateralAmount,
values.newLeverage,
int256(values.govFeeCollateral),
_getMultiCollatDiamond().getTradeLiquidationParams(_existingTrade.user, _existingTrade.index),
_answer.current,
0, // not partial close
false
);
if (_isIncrease) {
values.newEffectiveLeverage = TradingCommonUtils.getTradeNewEffectiveLeverage(
_existingTrade,
_existingTrade.openPrice,
uint120(values.newCollateralAmount),
uint24(values.newLeverage),
_answer.current,
values.govFeeCollateral
);
values.totalTradeAvailableCollateralInDiamond = TradingCommonUtils.getTradeAvailableCollateralInDiamond(
_existingTrade.user,
_existingTrade.index,
_existingTrade.collateralAmount
);
values.availableCollateralInDiamond = _pendingTrade.collateralAmount >
values.totalTradeAvailableCollateralInDiamond
? values.totalTradeAvailableCollateralInDiamond
: _pendingTrade.collateralAmount;
}
}
/**
* @dev Validates callback, and returns corresponding cancel reason
* @param _existingTrade existing trade struct
* @param _values pre-calculated useful values
* @param _answer price aggregator answer
* @param _isIncrease true if increase leverage, false if decrease leverage
*/
function _validateCallback(
ITradingStorage.Trade memory _existingTrade,
IUpdateLeverage.UpdateLeverageValues memory _values,
ITradingCallbacks.AggregatorAnswer memory _answer,
bool _isIncrease
) internal view returns (ITradingCallbacks.CancelReason) {
return
!_existingTrade.isOpen
? ITradingCallbacks.CancelReason.NO_TRADE
: (
_existingTrade.long
? (_answer.current <= _values.existingLiqPrice || _answer.current <= _values.newLiqPrice)
: (_answer.current >= _values.existingLiqPrice || _answer.current >= _values.newLiqPrice)
)
? ITradingCallbacks.CancelReason.LIQ_REACHED
: _isIncrease &&
_values.newEffectiveLeverage >
_getMultiCollatDiamond().pairMaxLeverage(_existingTrade.pairIndex)
? ITradingCallbacks.CancelReason.MAX_LEVERAGE
: _isIncrease &&
_existingTrade.isCounterTrade &&
_values.newEffectiveLeverage >
_getMultiCollatDiamond().getPairCounterTradeMaxLeverage(_existingTrade.pairIndex)
? ITradingCallbacks.CancelReason.COUNTER_TRADE_CANCELED
: ITradingCallbacks.CancelReason.NONE;
}
/**
* @dev Handles trade update, removes gov fee OI, and transfers collateral delta (for both successful and failed requests)
* @param _trade trade struct
* @param _pendingTrade pending trade struct
* @param _values pre-calculated useful values
* @param _cancelReason cancel reason
* @param _isIncrease true if increase leverage, false if decrease leverage
*/
function _handleCallback(
ITradingStorage.Trade memory _trade,
ITradingStorage.Trade memory _pendingTrade,
IUpdateLeverageUtils.UpdateLeverageValues memory _values,
ITradingCallbacks.CancelReason _cancelReason,
bool _isIncrease,
ITradingCallbacks.AggregatorAnswer memory _answer
) internal {
// 1. Request successful
if (_cancelReason == ITradingCallbacks.CancelReason.NONE) {
// 1.1 Update trade collateral and leverage, openPrice stays the same
_getMultiCollatDiamond().updateTradePosition(
ITradingStorage.Id(_trade.user, _trade.index),
uint120(_values.newCollateralAmount),
uint24(_values.newLeverage),
_trade.openPrice,
ITradingStorage.PendingOrderType.UPDATE_LEVERAGE, // ignore dust OI deltas
0,
false,
_answer.current
);
// 1.2 If leverage increase, transfer collateral delta to trader
if (_isIncrease) {
TradingCommonUtils.handleTradeValueTransfer(
_trade,
int256(uint256(_pendingTrade.collateralAmount)),
int256(_values.availableCollateralInDiamond)
);
_getMultiCollatDiamond().storeVirtualAvailableCollateralInDiamond(
_trade.user,
_trade.index,
_values.newCollateralAmount
); /// @dev don't allow available collateral in diamond to be negative after withdrawal
}
} else if (!_isIncrease) {
// 2. Request failed (decrease leverage)
TradingCommonUtils.transferCollateralTo(
_trade.collateralIndex,
_trade.user,
_pendingTrade.collateralAmount
);
}
// 3. If trade exists, charge and distribute gov fee
if (_cancelReason != ITradingCallbacks.CancelReason.NO_TRADE) {
uint256 finalGovFeeCollateral = _getMultiCollatDiamond().realizeTradingFeesOnOpenTrade(
_trade.user,
_trade.index,
_values.govFeeCollateral,
_answer.current
);
TradingCommonUtils.distributeExactGovFeeCollateral(
_trade.collateralIndex,
_trade.user,
finalGovFeeCollateral
);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import "../../interfaces/IGNSMultiCollatDiamond.sol";
import "../../interfaces/IERC20.sol";
import "../ConstantsUtils.sol";
import "../TradingCommonUtils.sol";
/**
*
* @dev This is an internal utils library for position size decreases
* @dev Used by UpdatePositionSizeLifecycles internal library
*/
library DecreasePositionSizeUtils {
/**
* @dev Validates decrease position size request
*
* @dev Possible inputs: collateral delta > 0 and leverage delta = 0 (decrease collateral by collateral delta)
* collateral delta = 0 and leverage delta > 0 (decrease leverage by leverage delta)
*
* @param _trade trade of request
* @param _input input values
*/
function validateRequest(
ITradingStorage.Trade memory _trade,
IUpdatePositionSizeUtils.DecreasePositionSizeInput memory _input
) internal view returns (uint256 positionSizeCollateralDelta) {
// 1. Revert if both collateral and leverage are zero or if both are non-zero
if (
(_input.collateralDelta == 0 && _input.leverageDelta == 0) ||
(_input.collateralDelta > 0 && _input.leverageDelta > 0)
) revert IUpdatePositionSizeUtils.InvalidDecreasePositionSizeInput();
// 2. If we update the leverage, check new leverage is above the minimum
bool isLeverageUpdate = _input.leverageDelta > 0;
if (
isLeverageUpdate &&
!TradingCommonUtils.validateAdjustedInitialLeverage(_trade.leverage - _input.leverageDelta)
) revert ITradingInteractionsUtils.WrongLeverage();
// 3. Revert if expected price is zero
if (_input.expectedPrice == 0) revert IGeneralErrors.ZeroValue();
// 4. Validate new collateral amount (enough to pay min closing fee)
if (
_trade.collateralAmount - _input.collateralDelta <
TradingCommonUtils.getPairMinFeeCollateral(_trade.collateralIndex, _trade.pairIndex)
) revert ITradingInteractionsUtils.InsufficientCollateral();
// 5. Calculate position size collateral delta
positionSizeCollateralDelta = TradingCommonUtils.getPositionSizeCollateral(
isLeverageUpdate ? _trade.collateralAmount : _input.collateralDelta,
isLeverageUpdate ? _input.leverageDelta : _trade.leverage
);
}
/**
* @dev Calculates values for callback
* @param _existingTrade existing trade data
* @param _partialTrade partial trade data
* @param _answer price aggregator answer
*/
function prepareCallbackValues(
ITradingStorage.Trade memory _existingTrade,
ITradingStorage.Trade memory _partialTrade,
ITradingCallbacks.AggregatorAnswer memory _answer
) internal view returns (IUpdatePositionSizeUtils.DecreasePositionSizeValues memory values) {
// 1. Calculate position size delta and existing position size
values.isLeverageUpdate = _partialTrade.leverage > 0;
values.positionSizeCollateralDelta = TradingCommonUtils.getPositionSizeCollateral(
values.isLeverageUpdate ? _existingTrade.collateralAmount : _partialTrade.collateralAmount,
values.isLeverageUpdate ? _partialTrade.leverage : _existingTrade.leverage
);
values.existingPositionSizeCollateral = TradingCommonUtils.getPositionSizeCollateral(
_existingTrade.collateralAmount,
_existingTrade.leverage
);
// 2. Apply spread and price impact to answer.current
(values.priceImpact, ) = TradingCommonUtils.getTradeClosingPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(
_existingTrade,
_answer.current,
values.positionSizeCollateralDelta,
_answer.current,
true
)
);
// 3. Calculate existing trade pnl
values.existingPnlPercent = TradingCommonUtils.getPnlPercent(
_existingTrade.openPrice,
values.priceImpact.priceAfterImpact,
_existingTrade.long,
_existingTrade.leverage
);
values.partialRawPnlCollateral =
(values.existingPnlPercent *
int256(uint256(_existingTrade.collateralAmount)) *
int256(uint256(values.positionSizeCollateralDelta))) /
int256(uint256(values.existingPositionSizeCollateral)) /
1e10 /
100;
values.partialNetPnlCollateral =
((int256(
TradingCommonUtils.getTradeValueCollateral(
_existingTrade,
values.existingPnlPercent,
0, // don't apply closing fee here, we apply it after (otherwise min fee wouldn't work)
_answer.current
)
) - int256(uint256(_existingTrade.collateralAmount))) * int256(values.positionSizeCollateralDelta)) /
int256(values.existingPositionSizeCollateral);
// 3. Calculate partial trade closing fees
values.closingFeeCollateral = TradingCommonUtils.getTotalTradeFeesCollateral(
_existingTrade.collateralIndex,
_existingTrade.user,
_existingTrade.pairIndex,
values.positionSizeCollateralDelta,
_existingTrade.isCounterTrade
);
// 4. Calculate value sent to trader
values.collateralSentToTrader =
int256(uint256(_partialTrade.collateralAmount)) +
values.partialNetPnlCollateral -
int256(values.closingFeeCollateral); /// @dev can't be <= 0 for collateral decreases because trade could be liquidated, unless collateral delta is < min fee
if (values.isLeverageUpdate) {
values.pnlToRealizeCollateral =
values.partialRawPnlCollateral -
(values.collateralSentToTrader > 0 ? values.collateralSentToTrader : int256(0));
}
// 5. Calculate new collateral amount and leverage
values.newCollateralAmount = _existingTrade.collateralAmount - _partialTrade.collateralAmount;
values.newLeverage = _existingTrade.leverage - _partialTrade.leverage;
// 6. Calculate existing and new trade liquidation price
values.existingLiqPrice = TradingCommonUtils.getTradeLiquidationPrice(_existingTrade, _answer.current);
values.newLiqPrice = TradingCommonUtils.getTradeLiquidationPrice(
_existingTrade,
_existingTrade.openPrice,
values.newCollateralAmount,
values.newLeverage,
values.isLeverageUpdate ? int256(values.closingFeeCollateral) - values.pnlToRealizeCollateral : int256(0),
_getMultiCollatDiamond().getTradeLiquidationParams(_existingTrade.user, _existingTrade.index),
_answer.current,
values.isLeverageUpdate
? 1e18
: ((values.existingPositionSizeCollateral - values.positionSizeCollateralDelta) * 1e18) /
values.existingPositionSizeCollateral,
false
);
/// @dev Other calculations are in separate helper called after realizing pending holding fees
// Because they can be impacted by pending holding fees realization (eg. available in diamond calc)
}
/**
* @dev Calculates remaining values for success callback
* @param _existingTrade existing trade data
* @param _partialTrade partial trade data
* @param _values existing values struct (will be updated in-place)
*/
function prepareSuccessCallbackValues(
ITradingStorage.Trade memory _existingTrade,
ITradingStorage.Trade memory _partialTrade,
IUpdatePositionSizeUtils.DecreasePositionSizeValues memory _values
) internal view {
// 1. Fetch existing trade available in diamond (after pending holding fees are realized)
_values.totalAvailableCollateralInDiamond = TradingCommonUtils.getTradeAvailableCollateralInDiamond(
_existingTrade.user,
_existingTrade.index,
_existingTrade.collateralAmount
);
// 2.1 Calculate available collateral in diamond (collateral decrease)
// Collateral decreases are simpler because it's like scaling down the whole trade
// Trader receives proportional collateral + net pnl - partial closing fee, available in diamond is proportional to total available in diamond
// Then the whole trade's value is scaled down (including available in diamond)
if (!_values.isLeverageUpdate) {
uint256 missingCollateralFromDiamond = uint256(_existingTrade.collateralAmount) -
_values.totalAvailableCollateralInDiamond;
_values.availableCollateralInDiamond =
int256(uint256(_partialTrade.collateralAmount)) -
int256(
Math.mulDiv(
missingCollateralFromDiamond,
_values.positionSizeCollateralDelta,
_values.existingPositionSizeCollateral,
Math.Rounding.Up
)
) -
int256(_values.closingFeeCollateral); /// @dev can be negative if partial available in diamond < closing fee
} else if (_values.collateralSentToTrader < 0) {
// 2.2 Leverage decreases are more complex, available collateral in diamond is always 0 (we just settle pnl)
// We send net PnL to trader, and realize (unrealized partial pnl - what we sent to trader)
// So that new trade value = old trade value - what we sent to trader (no value created or destroyed)
// When net PnL is negative, we only send the raw negative PnL to vault (unrealized + realized PnL - manually realized - already sent)
// And store it as already transferred to vault so we don't transfer it again later and reduce available in diamond
IFundingFees.TradeFeesData memory tradeFeesData = _getMultiCollatDiamond().getTradeFeesData(
_existingTrade.user,
_existingTrade.index
);
int256 pnlCollateralToSendToVault = _values.partialRawPnlCollateral +
(tradeFeesData.realizedPnlCollateral * int256(uint256(_values.positionSizeCollateralDelta))) /
int256(_values.existingPositionSizeCollateral) +
int256(
Math.mulDiv(
uint256(tradeFeesData.manuallyRealizedNegativePnlCollateral) +
uint256(tradeFeesData.alreadyTransferredNegativePnlCollateral),
_values.positionSizeCollateralDelta,
_values.existingPositionSizeCollateral,
Math.Rounding.Up
)
);
// Re-check if there is any negative pnl to send to vault (net pnl could be negative because of trading fees for example)
_values.collateralSentToTrader = pnlCollateralToSendToVault < 0 ? pnlCollateralToSendToVault : int256(0);
// Make sure we never send more than what we have in diamond to vault
// This is probably unreachable because we already remove manually realized and already transferred negative pnl
// And the only other thing that impacts available collateral in diamond is realized trading fees
// But in this case we should always have enough in diamond because if net PnL < -available in diamond, trade can be liquidated
_values.collateralSentToTrader = _values.collateralSentToTrader <
-int256(_values.totalAvailableCollateralInDiamond)
? -int256(_values.totalAvailableCollateralInDiamond)
: _values.collateralSentToTrader;
}
}
/**
* @dev Validates callback, and returns corresponding cancel reason
* @param _values pre-calculated useful values
*/
function validateCallback(
ITradingStorage.Trade memory _existingTrade,
ITradingStorage.PendingOrder memory _pendingOrder,
IUpdatePositionSizeUtils.DecreasePositionSizeValues memory _values,
ITradingCallbacks.AggregatorAnswer memory _answer
) internal view returns (ITradingCallbacks.CancelReason) {
// Max slippage calculations
uint256 expectedPrice = _pendingOrder.trade.openPrice;
uint256 maxSlippageP = _getMultiCollatDiamond()
.getTradeInfo(_existingTrade.user, _existingTrade.index)
.maxSlippageP;
uint256 maxSlippage = (expectedPrice *
(maxSlippageP > 0 ? maxSlippageP : ConstantsUtils.DEFAULT_MAX_CLOSING_SLIPPAGE_P)) /
100 /
1e3;
return
(
_existingTrade.long
? (_answer.current <= _values.existingLiqPrice || _answer.current <= _values.newLiqPrice)
: (_answer.current >= _values.existingLiqPrice || _answer.current >= _values.newLiqPrice)
)
? ITradingCallbacks.CancelReason.LIQ_REACHED
: (
_existingTrade.long
? _values.priceImpact.priceAfterImpact < expectedPrice - maxSlippage
: _values.priceImpact.priceAfterImpact > expectedPrice + maxSlippage
)
? ITradingCallbacks.CancelReason.SLIPPAGE
: !_values.isLeverageUpdate && _values.collateralSentToTrader < 0
? ITradingCallbacks.CancelReason.WRONG_TRADE // possible if collateral delta < min fee
: ITradingCallbacks.CancelReason.NONE;
}
/**
* @dev Updates trade (for successful request)
* @param _existingTrade existing trade data
* @param _partialTrade partial trade data
* @param _values pre-calculated useful values
* @param _answer price aggregator answer
*/
function updateTradeSuccess(
ITradingStorage.Trade memory _existingTrade,
ITradingStorage.Trade memory _partialTrade,
IUpdatePositionSizeUtils.DecreasePositionSizeValues memory _values,
ITradingCallbacks.AggregatorAnswer memory _answer
) internal {
// 1. Update trade in storage (realizes pending holding fees)
_getMultiCollatDiamond().updateTradePosition(
ITradingStorage.Id(_existingTrade.user, _existingTrade.index),
_values.newCollateralAmount,
_values.newLeverage,
_existingTrade.openPrice, // open price stays the same
ITradingStorage.PendingOrderType.MARKET_PARTIAL_CLOSE, // don't refresh liquidation params
_values.priceImpact.positionSizeToken,
_values.existingPnlPercent > 0,
_answer.current
);
// 2. Calculate remaining success callback values
prepareSuccessCallbackValues(_existingTrade, _partialTrade, _values);
// 3. Handle collateral/pnl transfers with vault/diamond/user
TradingCommonUtils.handleTradeValueTransfer(
_existingTrade,
_values.collateralSentToTrader,
int256(_values.availableCollateralInDiamond)
);
if (_values.isLeverageUpdate) {
// 4.1.1 Realize pnl so new trade value + collateral sent to trader = previous trade value (no value created or destroyed)
// First we realize the partial pnl amount so prev raw pnl = new raw pnl, and then we remove what we sent from trader
_getMultiCollatDiamond().realizePnlOnOpenTrade(
_existingTrade.user,
_existingTrade.index,
_values.pnlToRealizeCollateral
);
// 4.1.2 Decrease collat available in diamond (if we've sent negative PnL to vault)
if (_values.collateralSentToTrader < 0) {
_getMultiCollatDiamond().storeAlreadyTransferredNegativePnl(
_existingTrade.user,
_existingTrade.index,
uint256(-_values.collateralSentToTrader)
);
}
// 4.1.3 Realize trading fee (if leverage decrease)
// Not needed for collateral decreases because closing fee is removed from available collateral in diamond and trade value
// So vault sends as much as if there was no closing fee, but we send less to the trader, so we always have the partial closing fee in diamond
// This is especially important for lev decreases when nothing is available in diamond, this will transfer the closing fee from vault
_getMultiCollatDiamond().realizeTradingFeesOnOpenTrade(
_existingTrade.user,
_existingTrade.index,
_values.closingFeeCollateral,
_answer.current
);
} else {
// 4.2.1 Proportionally reduce realized pnl and available collateral in diamond (if collat decrease)
// So that new trade value + collateral sent to trader = previous trade value (no value created or destroyed)
// For leverage decreases we have to use another approach since it only settles net PnL and doesn't touch collateral
_getMultiCollatDiamond().downscaleTradeFeesData(
_existingTrade.user,
_existingTrade.index,
_values.positionSizeCollateralDelta,
_values.existingPositionSizeCollateral,
_values.newCollateralAmount
);
// 4.2.2 Store realized trading fees for UI
// Since we don't call realizeTradingFeesOnOpenTrade (partial close fee taken from amount sent to trader)
_getMultiCollatDiamond().storeUiRealizedTradingFeesCollateral(
_existingTrade.user,
_existingTrade.index,
_values.closingFeeCollateral
);
}
// 5. Store partial realized pnl for UI
// collateral sent to trader >= 0: the trade net pnl decreases by partial net pnl (which was sent to trader)
// either by scaling all values down (collat decrease) or adjusting realized pnl (lev decrease)
// collateral sent to trader < 0: the trade net pnl stays the same, but it's shifted from raw pnl to realized pnl
_getMultiCollatDiamond().storeUiRealizedPnlPartialCloseCollateral(
_existingTrade.user,
_existingTrade.index,
_values.collateralSentToTrader >= 0
? _values.partialNetPnlCollateral - int256(_values.closingFeeCollateral)
: _values.partialRawPnlCollateral
);
}
/**
* @dev Handles callback canceled case (for failed request)
* @param _existingTrade trade to update
* @param _cancelReason cancel reason
* @param _answer price aggregator answer
*/
function handleCanceled(
ITradingStorage.Trade memory _existingTrade,
ITradingCallbacks.CancelReason _cancelReason,
ITradingCallbacks.AggregatorAnswer memory _answer
) internal {
if (_cancelReason != ITradingCallbacks.CancelReason.NO_TRADE) {
// 1. Distribute gov fee
uint256 govFeeCollateral = TradingCommonUtils.getMinGovFeeCollateral(
_existingTrade.collateralIndex,
_existingTrade.user,
_existingTrade.pairIndex
);
uint256 finalGovFeeCollateral = _getMultiCollatDiamond().realizeTradingFeesOnOpenTrade(
_existingTrade.user,
_existingTrade.index,
govFeeCollateral,
_answer.current
);
TradingCommonUtils.distributeExactGovFeeCollateral(
_existingTrade.collateralIndex,
_existingTrade.user,
finalGovFeeCollateral
);
}
}
/**
* @dev Returns current address as multi-collateral diamond interface to call other facets functions.
*/
function _getMultiCollatDiamond() internal view returns (IGNSMultiCollatDiamond) {
return IGNSMultiCollatDiamond(address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import "../../interfaces/IGNSMultiCollatDiamond.sol";
import "../../interfaces/IERC20.sol";
import "../ConstantsUtils.sol";
import "../TradingCommonUtils.sol";
/**
*
* @dev This is an internal utils library for position size increases
* @dev Used by UpdatePositionSizeLifecycles internal library
*/
library IncreasePositionSizeUtils {
/**
* @dev Validates increase position request.
*
* @dev Possible inputs: collateral delta > 0 and leverage delta > 0 (increase position size by collateral delta * leverage delta)
* collateral delta = 0 and leverage delta > 0 (increase trade leverage by leverage delta)
*
* @param _trade trade of request
* @param _input input values
*/
function validateRequest(
ITradingStorage.Trade memory _trade,
IUpdatePositionSizeUtils.IncreasePositionSizeInput memory _input
) internal view returns (uint256 positionSizeCollateralDelta) {
// 0. Make sure trade is opened after v10 (otherwise can bypass funding fees / skew price impact)
if (
_getMultiCollatDiamond().getTradeContractsVersion(_trade.user, _trade.index) <
ITradingStorage.ContractsVersion.V10
) revert IGeneralErrors.NotAuthorized();
// 1. Zero values checks
if (_input.leverageDelta == 0 || _input.expectedPrice == 0 || _input.maxSlippageP == 0)
revert IUpdatePositionSizeUtils.InvalidIncreasePositionSizeInput();
// 2. Revert if adjusted initial leverage is invalid
bool isLeverageUpdate = _input.collateralDelta == 0;
positionSizeCollateralDelta = TradingCommonUtils.getPositionSizeCollateral(
isLeverageUpdate ? _trade.collateralAmount : _input.collateralDelta,
_input.leverageDelta
);
uint256 newLeverage = isLeverageUpdate
? uint256(_trade.leverage) + _input.leverageDelta
: ((TradingCommonUtils.getPositionSizeCollateral(_trade.collateralAmount, _trade.leverage) +
positionSizeCollateralDelta) * 1e3) / (_trade.collateralAmount + _input.collateralDelta);
if (!TradingCommonUtils.validateAdjustedInitialLeverage(newLeverage))
revert ITradingInteractionsUtils.WrongLeverage();
}
/**
* @dev Calculates values for callback
* @param _existingTrade existing trade data
* @param _partialTrade partial trade data
* @param _answer price aggregator answer
*/
function prepareCallbackValues(
ITradingStorage.Trade memory _existingTrade,
ITradingStorage.Trade memory _partialTrade,
ITradingCallbacks.AggregatorAnswer memory _answer
) internal view returns (IUpdatePositionSizeUtils.IncreasePositionSizeValues memory values) {
// 1.1 Calculate position size delta
bool isLeverageUpdate = _partialTrade.collateralAmount == 0;
values.positionSizeCollateralDelta = TradingCommonUtils.getPositionSizeCollateral(
isLeverageUpdate ? _existingTrade.collateralAmount : _partialTrade.collateralAmount,
_partialTrade.leverage
);
// 1.2 Validate counter trade and update position size delta if needed
if (_existingTrade.isCounterTrade) {
(values.isCounterTradeValidated, values.exceedingPositionSizeCollateral) = TradingCommonUtils
.validateCounterTrade(_existingTrade, values.positionSizeCollateralDelta, _answer.current);
if (values.isCounterTradeValidated && values.exceedingPositionSizeCollateral > 0) {
if (isLeverageUpdate) {
// For leverage updates, simply reduce leverage delta to reach 0 skew
_partialTrade.leverage -= uint24(
Math.mulDiv(
values.exceedingPositionSizeCollateral,
1e3,
_existingTrade.collateralAmount,
Math.Rounding.Up
)
);
} else {
// For collateral adds, reduce collateral delta to reach 0 skew
values.counterTradeCollateralToReturn = Math.mulDiv(
values.exceedingPositionSizeCollateral,
1e3,
_partialTrade.leverage,
Math.Rounding.Up
);
_partialTrade.collateralAmount -= uint120(values.counterTradeCollateralToReturn);
}
values.positionSizeCollateralDelta = TradingCommonUtils.getPositionSizeCollateral(
isLeverageUpdate ? _existingTrade.collateralAmount : _partialTrade.collateralAmount,
_partialTrade.leverage
);
}
}
values.existingPositionSizeCollateral = TradingCommonUtils.getPositionSizeCollateral(
_existingTrade.collateralAmount,
_existingTrade.leverage
);
values.newPositionSizeCollateral = values.existingPositionSizeCollateral + values.positionSizeCollateralDelta;
// 2.1 Calculate new collateral amount and leverage
values.newCollateralAmount = _existingTrade.collateralAmount + _partialTrade.collateralAmount;
values.newLeverage = isLeverageUpdate
? _existingTrade.leverage + _partialTrade.leverage
: (values.newPositionSizeCollateral * 1e3) / values.newCollateralAmount;
// 2.2 Re-calculate new position size and position size delta due to potential new leverage rounding issues
values.newPositionSizeCollateral = (values.newCollateralAmount * values.newLeverage) / 1e3;
if (values.newPositionSizeCollateral < values.existingPositionSizeCollateral) return values;
values.positionSizeCollateralDelta = values.newPositionSizeCollateral - values.existingPositionSizeCollateral;
// 3. Calculate price impact values
values.priceImpact = TradingCommonUtils.getTradeOpeningPriceImpact(
ITradingCommonUtils.TradePriceImpactInput(
_existingTrade,
_answer.current,
values.positionSizeCollateralDelta,
0,
true
)
);
// 4. Calculate existing trade pnl
values.existingPnlCollateral = TradingCommonUtils.getTradeUnrealizedRawPnlCollateral(
_existingTrade,
values.priceImpact.priceAfterImpact
);
// 5. Calculate partial trade opening fees
values.openingFeesCollateral = TradingCommonUtils.getTotalTradeFeesCollateral(
_existingTrade.collateralIndex,
_existingTrade.user,
_existingTrade.pairIndex,
values.positionSizeCollateralDelta,
_existingTrade.isCounterTrade
);
// 6. Calculate new open price
values.oldPosSizePlusPnlCollateral = uint256(
int256(values.existingPositionSizeCollateral) +
values.existingPnlCollateral *
(_existingTrade.long ? int256(1) : int256(-1))
); // longs: min pnl = -collateral <= position size, shorts: max pnl = collateral * leverage = position size => no underflow
values.newOpenPrice = Math.mulDiv(
values.oldPosSizePlusPnlCollateral *
uint256(_existingTrade.openPrice) +
values.positionSizeCollateralDelta *
values.priceImpact.priceAfterImpact,
1,
values.oldPosSizePlusPnlCollateral + values.positionSizeCollateralDelta,
_existingTrade.long ? Math.Rounding.Up : Math.Rounding.Down
);
// 7. Calculate existing and new liq price
values.existingLiqPrice = TradingCommonUtils.getTradeLiquidationPrice(_existingTrade, _answer.current);
values.newLiqPrice = TradingCommonUtils.getTradeLiquidationPrice(
_existingTrade,
uint64(values.newOpenPrice),
values.newCollateralAmount,
values.newLeverage,
int256(values.openingFeesCollateral),
_getMultiCollatDiamond().getPairLiquidationParams(_existingTrade.pairIndex), // new liquidation params
_answer.current,
0, // not partial close
false
);
// 8. Calculate new effective leverage
values.newEffectiveLeverage = TradingCommonUtils.getTradeNewEffectiveLeverage(
_existingTrade,
uint64(values.newOpenPrice),
uint120(values.newCollateralAmount),
uint24(values.newLeverage),
_answer.current,
values.openingFeesCollateral
);
}
/**
* @dev Validates callback, and returns corresponding cancel reason
* @param _existingTrade existing trade data
* @param _values pre-calculated useful values
* @param _expectedPrice user expected price before callback (1e10)
* @param _maxSlippageP maximum slippage percentage from expected price (1e3)
*/
function validateCallback(
ITradingStorage.Trade memory _existingTrade,
IUpdatePositionSizeUtils.IncreasePositionSizeValues memory _values,
ITradingCallbacks.AggregatorAnswer memory _answer,
uint256 _expectedPrice,
uint256 _maxSlippageP
) internal view returns (ITradingCallbacks.CancelReason cancelReason) {
uint256 maxSlippage = (uint256(_expectedPrice) * _maxSlippageP) / 100 / 1e3;
cancelReason = _values.newPositionSizeCollateral <= _values.existingPositionSizeCollateral
? ITradingCallbacks.CancelReason.WRONG_TRADE
: (
_existingTrade.long
? _values.priceImpact.priceAfterImpact > _expectedPrice + maxSlippage
: _values.priceImpact.priceAfterImpact < _expectedPrice - maxSlippage
)
? ITradingCallbacks.CancelReason.SLIPPAGE
: _existingTrade.tp > 0 &&
(_existingTrade.long ? _answer.current >= _existingTrade.tp : _answer.current <= _existingTrade.tp)
? ITradingCallbacks.CancelReason.TP_REACHED
: _existingTrade.sl > 0 &&
(
_existingTrade.long
? _answer.current <= _existingTrade.sl
: _answer.current >= _existingTrade.sl
)
? ITradingCallbacks.CancelReason.SL_REACHED
: (
_existingTrade.long
? (_answer.current <= _values.existingLiqPrice ||
_answer.current <= _values.newLiqPrice)
: (_answer.current >= _values.existingLiqPrice ||
_answer.current >= _values.newLiqPrice)
)
? ITradingCallbacks.CancelReason.LIQ_REACHED
: !TradingCommonUtils.isWithinExposureLimits(
_existingTrade.collateralIndex,
_existingTrade.pairIndex,
_existingTrade.long,
_values.positionSizeCollateralDelta,
_answer.current
)
? ITradingCallbacks.CancelReason.EXPOSURE_LIMITS
: _values.newEffectiveLeverage >
_getMultiCollatDiamond().pairMaxLeverage(_existingTrade.pairIndex)
? ITradingCallbacks.CancelReason.MAX_LEVERAGE
: _existingTrade.isCounterTrade &&
(!_values.isCounterTradeValidated ||
_values.newEffectiveLeverage >
_getMultiCollatDiamond().getPairCounterTradeMaxLeverage(
_existingTrade.pairIndex
))
? ITradingCallbacks.CancelReason.COUNTER_TRADE_CANCELED
: ITradingCallbacks.CancelReason.NONE;
}
/**
* @dev Updates trade (for successful request)
* @param _existingTrade existing trade data
* @param _values pre-calculated useful values
* @param _answer price aggregator answer
*/
function updateTradeSuccess(
ITradingStorage.Trade memory _existingTrade,
IUpdatePositionSizeUtils.IncreasePositionSizeValues memory _values,
ITradingCallbacks.AggregatorAnswer memory _answer
) internal {
// 1. Update trade in storage (realizes pending holding fees)
_getMultiCollatDiamond().updateTradePosition(
ITradingStorage.Id(_existingTrade.user, _existingTrade.index),
uint120(_values.newCollateralAmount),
uint24(_values.newLeverage),
uint64(_values.newOpenPrice),
ITradingStorage.PendingOrderType.MARKET_PARTIAL_OPEN, // refresh liquidation params
_values.priceImpact.positionSizeToken,
false,
_answer.current
);
// 2. Charge opening fees on trade
_getMultiCollatDiamond().realizeTradingFeesOnOpenTrade(
_existingTrade.user,
_existingTrade.index,
_values.openingFeesCollateral,
_answer.current
);
// 3. Return any exceeding collateral to trader (for counter trades)
if (_existingTrade.isCounterTrade && _values.counterTradeCollateralToReturn > 0) {
TradingCommonUtils.transferCollateralTo(
_existingTrade.collateralIndex,
_existingTrade.user,
_values.counterTradeCollateralToReturn
);
emit ITradingCallbacksUtils.CounterTradeCollateralReturned(
_answer.orderId,
_existingTrade.collateralIndex,
_existingTrade.user,
_values.counterTradeCollateralToReturn
);
}
}
/**
* @dev Handles callback canceled case (for failed request)
* @param _existingTrade existing trade data
* @param _partialTrade partial trade data
* @param _cancelReason cancel reason
* @param _answer price aggregator answer
* @param _values values struct
*/
function handleCanceled(
ITradingStorage.Trade memory _existingTrade,
ITradingStorage.Trade memory _partialTrade,
ITradingCallbacks.CancelReason _cancelReason,
ITradingCallbacks.AggregatorAnswer memory _answer,
IUpdatePositionSizeUtils.IncreasePositionSizeValues memory _values
) internal {
// 1. Charge gov fee on trade (if trade exists)
if (_cancelReason != ITradingCallbacks.CancelReason.NO_TRADE) {
// 1.1 Distribute gov fee
uint256 govFeeCollateral = TradingCommonUtils.getMinGovFeeCollateral(
_existingTrade.collateralIndex,
_existingTrade.user,
_existingTrade.pairIndex
);
uint256 finalGovFeeCollateral = _getMultiCollatDiamond().realizeTradingFeesOnOpenTrade(
_existingTrade.user,
_existingTrade.index,
govFeeCollateral,
_answer.current
);
TradingCommonUtils.distributeExactGovFeeCollateral(
_existingTrade.collateralIndex,
_existingTrade.user,
finalGovFeeCollateral
);
}
// 2. Send back partial collateral to trader
TradingCommonUtils.transferCollateralTo(
_existingTrade.collateralIndex,
_existingTrade.user,
_partialTrade.collateralAmount + _values.counterTradeCollateralToReturn
);
}
/**
* @dev Returns current address as multi-collateral diamond interface to call other facets functions.
*/
function _getMultiCollatDiamond() internal view returns (IGNSMultiCollatDiamond) {
return IGNSMultiCollatDiamond(address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "../../interfaces/IGNSMultiCollatDiamond.sol";
import "./IncreasePositionSizeUtils.sol";
import "./DecreasePositionSizeUtils.sol";
import "../ChainUtils.sol";
import "../ConstantsUtils.sol";
import "../TradingCommonUtils.sol";
/**
*
* @dev This is an external library for position size updates lifecycles
* @dev Used by GNSTrading and GNSTradingCallbacks facets
*/
library UpdatePositionSizeLifecycles {
/**
* @dev Initiate increase position size order, done in 2 steps because position size changes
* @param _input request increase position size input struct
*/
function requestIncreasePositionSize(
IUpdatePositionSizeUtils.IncreasePositionSizeInput memory _input,
bool _isNative
) external {
// 1. Base validation
ITradingStorage.Trade memory trade = _baseValidateRequest(_input.user, _input.index);
// 2. Increase position size validation
uint256 positionSizeCollateralDelta = IncreasePositionSizeUtils.validateRequest(trade, _input);
// 3. Transfer collateral delta from trader to diamond contract (nothing transferred for leverage update)
// When `_isNative` is true, then payment has already been transferred in as native tokens and wrapped
if (!_isNative)
TradingCommonUtils.transferCollateralFrom(trade.collateralIndex, _input.user, _input.collateralDelta);
// 4. Create pending order and make price aggregator request
ITradingStorage.Id memory orderId = _initiateRequest(
trade,
true,
_input.collateralDelta,
_input.leverageDelta,
positionSizeCollateralDelta,
_input.expectedPrice,
_input.maxSlippageP
);
emit IUpdatePositionSizeUtils.PositionSizeUpdateInitiated(
orderId,
trade.user,
trade.pairIndex,
trade.index,
true,
_input.collateralDelta,
_input.leverageDelta
);
}
/**
* @dev Initiate decrease position size order, done in 2 steps because position size changes
* @param _input request decrease position size input struct
*/
function requestDecreasePositionSize(IUpdatePositionSizeUtils.DecreasePositionSizeInput memory _input) external {
// 1. Base validation
ITradingStorage.Trade memory trade = _baseValidateRequest(_input.user, _input.index);
// 2. Decrease position size validation
uint256 positionSizeCollateralDelta = DecreasePositionSizeUtils.validateRequest(trade, _input);
// 3. Store pending order and make price aggregator request
ITradingStorage.Id memory orderId = _initiateRequest(
trade,
false,
_input.collateralDelta,
_input.leverageDelta,
positionSizeCollateralDelta,
_input.expectedPrice,
0
);
emit IUpdatePositionSizeUtils.PositionSizeUpdateInitiated(
orderId,
trade.user,
trade.pairIndex,
trade.index,
false,
_input.collateralDelta,
_input.leverageDelta
);
}
/**
* @dev Execute increase position size market callback
* @param _order corresponding pending order
* @param _answer price aggregator answer
*/
function executeIncreasePositionSizeMarket(
ITradingStorage.PendingOrder memory _order,
ITradingCallbacks.AggregatorAnswer memory _answer
) external {
// 1. Prepare vars
ITradingStorage.Trade memory partialTrade = _order.trade;
ITradingStorage.Trade memory existingTrade = TradingCommonUtils.getTrade(partialTrade.user, partialTrade.index);
IUpdatePositionSizeUtils.IncreasePositionSizeValues memory values;
// 2. Refresh trader fee tier cache
TradingCommonUtils.updateFeeTierPoints(
existingTrade.collateralIndex,
existingTrade.user,
existingTrade.pairIndex,
0
);
// 3. Base validation (trade open, market open)
ITradingCallbacks.CancelReason cancelReason = _validateBaseFulfillment(existingTrade);
// 4. If passes base validation, validate further
if (cancelReason == ITradingCallbacks.CancelReason.NONE) {
// 4.1 Prepare useful values (position size delta, pnl, fees, new open price, etc.)
values = IncreasePositionSizeUtils.prepareCallbackValues(existingTrade, partialTrade, _answer);
// 4.2 Further validation
cancelReason = IncreasePositionSizeUtils.validateCallback(
existingTrade,
values,
_answer,
partialTrade.openPrice,
_order.maxSlippageP
);
// 5. If passes further validation, execute callback
if (cancelReason == ITradingCallbacks.CancelReason.NONE) {
IncreasePositionSizeUtils.updateTradeSuccess(existingTrade, values, _answer);
TradingCommonUtils.processFees(
existingTrade,
values.positionSizeCollateralDelta,
_order.orderType,
values.openingFeesCollateral
);
}
}
// 6. If didn't pass validation, charge gov fee (if trade exists) and return partial collateral (if any)
if (cancelReason != ITradingCallbacks.CancelReason.NONE)
IncreasePositionSizeUtils.handleCanceled(existingTrade, partialTrade, cancelReason, _answer, values);
emit IUpdatePositionSizeUtils.PositionSizeIncreaseExecuted(
_answer.orderId,
cancelReason,
existingTrade.collateralIndex,
existingTrade.user,
existingTrade.pairIndex,
existingTrade.index,
existingTrade.long,
_answer.current,
_getMultiCollatDiamond().getCollateralPriceUsd(existingTrade.collateralIndex),
partialTrade.collateralAmount,
partialTrade.leverage,
values
);
}
/**
* @dev Execute decrease position size market callback
* @param _order corresponding pending order
* @param _answer price aggregator answer
*/
function executeDecreasePositionSizeMarket(
ITradingStorage.PendingOrder memory _order,
ITradingCallbacks.AggregatorAnswer memory _answer
) external {
// 1. Prepare vars
ITradingStorage.Trade memory partialTrade = _order.trade;
ITradingStorage.Trade memory existingTrade = TradingCommonUtils.getTrade(partialTrade.user, partialTrade.index);
IUpdatePositionSizeUtils.DecreasePositionSizeValues memory values;
// 2. Refresh trader fee tier cache
TradingCommonUtils.updateFeeTierPoints(
existingTrade.collateralIndex,
existingTrade.user,
existingTrade.pairIndex,
0
);
// 3. Base validation (trade open, market open)
ITradingCallbacks.CancelReason cancelReason = _validateBaseFulfillment(existingTrade);
// 4. If passes base validation, validate further
if (cancelReason == ITradingCallbacks.CancelReason.NONE) {
// 4.1 Prepare useful values (position size delta, closing fees, borrowing fees, etc.)
values = DecreasePositionSizeUtils.prepareCallbackValues(existingTrade, partialTrade, _answer);
// 4.2 Further validation
cancelReason = DecreasePositionSizeUtils.validateCallback(existingTrade, _order, values, _answer);
// 5. If passes further validation, execute callback
if (cancelReason == ITradingCallbacks.CancelReason.NONE) {
DecreasePositionSizeUtils.updateTradeSuccess(existingTrade, partialTrade, values, _answer);
TradingCommonUtils.processFees(
existingTrade,
values.positionSizeCollateralDelta,
_order.orderType,
values.closingFeeCollateral
);
}
}
// 6. If didn't pass validation and trade exists, charge gov fee and remove corresponding OI
if (cancelReason != ITradingCallbacks.CancelReason.NONE)
DecreasePositionSizeUtils.handleCanceled(existingTrade, cancelReason, _answer);
emit IUpdatePositionSizeUtils.PositionSizeDecreaseExecuted(
_answer.orderId,
cancelReason,
existingTrade.collateralIndex,
existingTrade.user,
existingTrade.pairIndex,
existingTrade.index,
existingTrade.long,
_answer.current,
_getMultiCollatDiamond().getCollateralPriceUsd(existingTrade.collateralIndex),
partialTrade.collateralAmount,
partialTrade.leverage,
values
);
}
/**
* @dev Returns current address as multi-collateral diamond interface to call other facets functions.
*/
function _getMultiCollatDiamond() internal view returns (IGNSMultiCollatDiamond) {
return IGNSMultiCollatDiamond(address(this));
}
/**
* @dev Basic validation for increase/decrease position size request
* @param _trader trader address
* @param _index trade index
*/
function _baseValidateRequest(
address _trader,
uint32 _index
) internal view returns (ITradingStorage.Trade memory trade) {
trade = TradingCommonUtils.getTrade(_trader, _index);
// 1. Check trade exists
if (!trade.isOpen) revert IGeneralErrors.DoesntExist();
// 2. Revert if any market order (market close, increase leverage, partial open, partial close) already exists for trade
TradingCommonUtils.revertIfTradeHasPendingMarketOrder(_trader, _index);
// 3. Revert if collateral not active
if (!_getMultiCollatDiamond().isCollateralActive(trade.collateralIndex))
revert IGeneralErrors.InvalidCollateralIndex();
}
/**
* @dev Creates pending order, makes price aggregator request, and returns corresponding pending order id
* @param _trade trade to update
* @param _isIncrease whether is increase or decrease position size order
* @param _collateralAmount partial trade collateral amount (collateral precision)
* @param _leverage partial trade leverage (1e3)
* @param _positionSizeCollateralDelta position size delta in collateral tokens (collateral precision)
* @param _expectedPrice reference price for max slippage check (1e10), only useful for increase position size
* @param _maxSlippageP max slippage % (1e3), only useful for increase position size
*/
function _initiateRequest(
ITradingStorage.Trade memory _trade,
bool _isIncrease,
uint120 _collateralAmount,
uint24 _leverage,
uint256 _positionSizeCollateralDelta,
uint64 _expectedPrice,
uint16 _maxSlippageP
) internal returns (ITradingStorage.Id memory) {
// 1. Initialize partial trade
ITradingStorage.Trade memory pendingOrderTrade;
pendingOrderTrade.user = _trade.user;
pendingOrderTrade.index = _trade.index;
pendingOrderTrade.collateralAmount = _collateralAmount;
pendingOrderTrade.leverage = _leverage;
pendingOrderTrade.openPrice = _expectedPrice; // useful for max slippage checks
// 2. Store pending order
ITradingStorage.PendingOrder memory pendingOrder;
pendingOrder.trade = pendingOrderTrade;
pendingOrder.user = _trade.user;
pendingOrder.orderType = _isIncrease
? ITradingStorage.PendingOrderType.MARKET_PARTIAL_OPEN
: ITradingStorage.PendingOrderType.MARKET_PARTIAL_CLOSE;
pendingOrder.maxSlippageP = _maxSlippageP;
// 3. Make price aggregator request
return
_getMultiCollatDiamond().getPrice(
IPriceAggregator.GetPriceInput(
_trade.collateralIndex,
_trade.pairIndex,
pendingOrder,
_positionSizeCollateralDelta,
0,
_trade.isCounterTrade
)
);
}
/**
* @dev Basic validation for callbacks, returns corresponding cancel reason
* @param _trade trade struct
*/
function _validateBaseFulfillment(
ITradingStorage.Trade memory _trade
) internal pure returns (ITradingCallbacks.CancelReason) {
return !_trade.isOpen ? ITradingCallbacks.CancelReason.NO_TRADE : ITradingCallbacks.CancelReason.NONE;
}
}{
"optimizer": {
"enabled": true,
"runs": 750
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/core/facets/GNSTradingInteractions.sol": {
"TradingInteractionsUtils": "0xdb4f120d7079e7ec16763aac52c3b6fe1cd62489"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AboveMax","type":"error"},{"inputs":[],"name":"AlreadyBeingMarketClosed","type":"error"},{"inputs":[],"name":"AlreadyExists","type":"error"},{"inputs":[],"name":"BelowMin","type":"error"},{"inputs":[],"name":"BlockOrder","type":"error"},{"inputs":[],"name":"CollateralNotActive","type":"error"},{"inputs":[{"internalType":"enum ITradingStorage.PendingOrderType","name":"","type":"uint8"}],"name":"ConflictingPendingOrder","type":"error"},{"inputs":[],"name":"DelegateNotApproved","type":"error"},{"inputs":[],"name":"DelegatedActionNotAllowed","type":"error"},{"inputs":[],"name":"DoesntExist","type":"error"},{"inputs":[],"name":"InitError","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientCollateral","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAddresses","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidCollateralIndex","type":"error"},{"inputs":[],"name":"InvalidDecreasePositionSizeInput","type":"error"},{"inputs":[],"name":"InvalidIncreasePositionSizeInput","type":"error"},{"inputs":[],"name":"InvalidInputLength","type":"error"},{"inputs":[],"name":"NewPositionSizeSmaller","type":"error"},{"inputs":[],"name":"NoOrder","type":"error"},{"inputs":[],"name":"NoSl","type":"error"},{"inputs":[],"name":"NoTp","type":"error"},{"inputs":[],"name":"NoTrade","type":"error"},{"inputs":[],"name":"NotAllowed","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"NotWrappedNativeToken","type":"error"},{"inputs":[],"name":"NotYourOrder","type":"error"},{"inputs":[],"name":"Overflow","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"PendingTrigger","type":"error"},{"inputs":[],"name":"PriceImpactTooHigh","type":"error"},{"inputs":[],"name":"PriceZero","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"UnsupportedChain","type":"error"},{"inputs":[],"name":"WaitTimeout","type":"error"},{"inputs":[],"name":"WrongAccess","type":"error"},{"inputs":[],"name":"WrongFromBlock","type":"error"},{"inputs":[],"name":"WrongIndex","type":"error"},{"inputs":[],"name":"WrongLength","type":"error"},{"inputs":[],"name":"WrongLeverage","type":"error"},{"inputs":[],"name":"WrongOrder","type":"error"},{"inputs":[],"name":"WrongOrderType","type":"error"},{"inputs":[],"name":"WrongParams","type":"error"},{"inputs":[],"name":"WrongSl","type":"error"},{"inputs":[],"name":"WrongTp","type":"error"},{"inputs":[],"name":"WrongTradeType","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"enum IAddressStore.Role","name":"role","type":"uint8"},{"indexed":false,"internalType":"bool","name":"access","type":"bool"}],"name":"AccessControlUpdated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"gns","type":"address"},{"internalType":"address","name":"gnsStaking","type":"address"},{"internalType":"address","name":"treasury","type":"address"}],"indexed":false,"internalType":"struct IAddressStore.Addresses","name":"addresses","type":"tuple"}],"name":"AddressesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"bypass","type":"bool"}],"name":"ByPassTriggerLinkUpdated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"pendingOrderId","type":"tuple"},{"indexed":true,"internalType":"uint256","name":"pairIndex","type":"uint256"}],"name":"ChainlinkCallbackTimeout","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"pendingOrderId","type":"tuple"},{"indexed":true,"internalType":"uint8","name":"collateralIndex","type":"uint8"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"CollateralReturnedAfterTimeout","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint16","name":"pairIndex","type":"uint16"},{"indexed":true,"internalType":"uint32","name":"index","type":"uint32"}],"name":"CouldNotCloseTrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":false,"internalType":"bool","name":"isIncrease","type":"bool"},{"indexed":false,"internalType":"enum ITradingCallbacks.CancelReason","name":"cancelReason","type":"uint8"},{"indexed":true,"internalType":"uint8","name":"collateralIndex","type":"uint8"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint256","name":"pairIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oraclePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"components":[{"internalType":"uint256","name":"newLeverage","type":"uint256"},{"internalType":"uint256","name":"newCollateralAmount","type":"uint256"},{"internalType":"uint256","name":"existingLiqPrice","type":"uint256"},{"internalType":"uint256","name":"newLiqPrice","type":"uint256"},{"internalType":"uint256","name":"govFeeCollateral","type":"uint256"},{"internalType":"uint256","name":"newEffectiveLeverage","type":"uint256"},{"internalType":"uint256","name":"totalTradeAvailableCollateralInDiamond","type":"uint256"},{"internalType":"uint256","name":"availableCollateralInDiamond","type":"uint256"}],"indexed":false,"internalType":"struct IUpdateLeverage.UpdateLeverageValues","name":"values","type":"tuple"}],"name":"LeverageUpdateExecuted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint256","name":"pairIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isIncrease","type":"bool"},{"indexed":false,"internalType":"uint256","name":"newLeverage","type":"uint256"}],"name":"LeverageUpdateInitiated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint32","name":"index","type":"uint32"},{"indexed":false,"internalType":"bool","name":"isHoldingFeesRealization","type":"bool"}],"name":"ManualNegativePnlRealizationInitiated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint16","name":"pairIndex","type":"uint16"},{"indexed":false,"internalType":"bool","name":"open","type":"bool"}],"name":"MarketOrderInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newValueBlocks","type":"uint256"}],"name":"MarketOrdersTimeoutBlocksUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint256","name":"nativeTokenAmount","type":"uint256"}],"name":"NativeTokenWrapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint16","name":"pairIndex","type":"uint16"},{"indexed":true,"internalType":"uint32","name":"index","type":"uint32"}],"name":"OpenLimitCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint16","name":"pairIndex","type":"uint16"},{"indexed":true,"internalType":"uint32","name":"index","type":"uint32"},{"indexed":false,"internalType":"uint64","name":"newPrice","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newTp","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newSl","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"maxSlippageP","type":"uint64"}],"name":"OpenLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint16","name":"pairIndex","type":"uint16"},{"indexed":true,"internalType":"uint32","name":"index","type":"uint32"}],"name":"OpenOrderPlaced","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":false,"internalType":"enum ITradingCallbacks.CancelReason","name":"cancelReason","type":"uint8"},{"indexed":true,"internalType":"uint8","name":"collateralIndex","type":"uint8"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint256","name":"pairIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"bool","name":"long","type":"bool"},{"indexed":false,"internalType":"uint256","name":"oraclePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralPriceUsd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leverageDelta","type":"uint256"},{"components":[{"internalType":"bool","name":"isLeverageUpdate","type":"bool"},{"internalType":"uint256","name":"positionSizeCollateralDelta","type":"uint256"},{"internalType":"uint256","name":"existingPositionSizeCollateral","type":"uint256"},{"internalType":"uint256","name":"existingLiqPrice","type":"uint256"},{"internalType":"uint256","name":"newLiqPrice","type":"uint256"},{"components":[{"internalType":"uint256","name":"positionSizeToken","type":"uint256"},{"internalType":"int256","name":"fixedSpreadP","type":"int256"},{"internalType":"int256","name":"cumulVolPriceImpactP","type":"int256"},{"internalType":"int256","name":"skewPriceImpactP","type":"int256"},{"internalType":"int256","name":"totalPriceImpactP","type":"int256"},{"internalType":"uint64","name":"priceAfterImpact","type":"uint64"}],"internalType":"struct ITradingCommonUtils.TradePriceImpact","name":"priceImpact","type":"tuple"},{"internalType":"int256","name":"existingPnlPercent","type":"int256"},{"internalType":"int256","name":"partialRawPnlCollateral","type":"int256"},{"internalType":"int256","name":"partialNetPnlCollateral","type":"int256"},{"internalType":"int256","name":"pnlToRealizeCollateral","type":"int256"},{"internalType":"uint256","name":"closingFeeCollateral","type":"uint256"},{"internalType":"uint256","name":"totalAvailableCollateralInDiamond","type":"uint256"},{"internalType":"int256","name":"availableCollateralInDiamond","type":"int256"},{"internalType":"int256","name":"collateralSentToTrader","type":"int256"},{"internalType":"uint120","name":"newCollateralAmount","type":"uint120"},{"internalType":"uint24","name":"newLeverage","type":"uint24"}],"indexed":false,"internalType":"struct IUpdatePositionSize.DecreasePositionSizeValues","name":"values","type":"tuple"}],"name":"PositionSizeDecreaseExecuted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":false,"internalType":"enum ITradingCallbacks.CancelReason","name":"cancelReason","type":"uint8"},{"indexed":true,"internalType":"uint8","name":"collateralIndex","type":"uint8"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint256","name":"pairIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"bool","name":"long","type":"bool"},{"indexed":false,"internalType":"uint256","name":"oraclePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralPriceUsd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leverageDelta","type":"uint256"},{"components":[{"internalType":"uint256","name":"positionSizeCollateralDelta","type":"uint256"},{"internalType":"uint256","name":"existingPositionSizeCollateral","type":"uint256"},{"internalType":"uint256","name":"newPositionSizeCollateral","type":"uint256"},{"internalType":"uint256","name":"newCollateralAmount","type":"uint256"},{"internalType":"uint256","name":"newLeverage","type":"uint256"},{"components":[{"internalType":"uint256","name":"positionSizeToken","type":"uint256"},{"internalType":"int256","name":"fixedSpreadP","type":"int256"},{"internalType":"int256","name":"cumulVolPriceImpactP","type":"int256"},{"internalType":"int256","name":"skewPriceImpactP","type":"int256"},{"internalType":"int256","name":"totalPriceImpactP","type":"int256"},{"internalType":"uint64","name":"priceAfterImpact","type":"uint64"}],"internalType":"struct ITradingCommonUtils.TradePriceImpact","name":"priceImpact","type":"tuple"},{"internalType":"int256","name":"existingPnlCollateral","type":"int256"},{"internalType":"uint256","name":"oldPosSizePlusPnlCollateral","type":"uint256"},{"internalType":"uint256","name":"newOpenPrice","type":"uint256"},{"internalType":"uint256","name":"openingFeesCollateral","type":"uint256"},{"internalType":"uint256","name":"existingLiqPrice","type":"uint256"},{"internalType":"uint256","name":"newLiqPrice","type":"uint256"},{"internalType":"bool","name":"isCounterTradeValidated","type":"bool"},{"internalType":"uint256","name":"exceedingPositionSizeCollateral","type":"uint256"},{"internalType":"uint256","name":"counterTradeCollateralToReturn","type":"uint256"},{"internalType":"uint256","name":"newEffectiveLeverage","type":"uint256"}],"indexed":false,"internalType":"struct IUpdatePositionSize.IncreasePositionSizeValues","name":"values","type":"tuple"}],"name":"PositionSizeIncreaseExecuted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint256","name":"pairIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isIncrease","type":"bool"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leverageDelta","type":"uint256"}],"name":"PositionSizeUpdateInitiated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint32","name":"index","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amountCollateral","type":"uint256"}],"name":"PositivePnlWithdrawalInitiated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"indexed":false,"internalType":"struct ITradingStorage.Id","name":"orderId","type":"tuple"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint16","name":"pairIndex","type":"uint16"},{"indexed":false,"internalType":"bool","name":"byPassesLinkCost","type":"bool"}],"name":"TriggerOrderInitiated","type":"event"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"}],"name":"cancelOpenOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_orderIndex","type":"uint32"}],"name":"cancelOrderAfterTimeout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint64","name":"_expectedPrice","type":"uint64"}],"name":"closeTradeMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint120","name":"_collateralDelta","type":"uint120"},{"internalType":"uint24","name":"_leverageDelta","type":"uint24"},{"internalType":"uint64","name":"_expectedPrice","type":"uint64"}],"name":"decreasePositionSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_trader","type":"address"},{"internalType":"bytes","name":"_callData","type":"bytes"}],"name":"delegatedTradingAction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAddresses","outputs":[{"components":[{"internalType":"address","name":"gns","type":"address"},{"internalType":"address","name":"gnsStaking","type":"address"},{"internalType":"address","name":"treasury","type":"address"}],"internalType":"struct IAddressStore.Addresses","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getByPassTriggerLink","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketOrdersTimeoutBlocks","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_trader","type":"address"}],"name":"getTradingDelegate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWrappedNativeToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"enum IAddressStore.Role","name":"_role","type":"uint8"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"enum IAddressStore.Role","name":"_roleA","type":"uint8"},{"internalType":"enum IAddressStore.Role","name":"_roleB","type":"uint8"}],"name":"hasRoles","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint120","name":"_collateralDelta","type":"uint120"},{"internalType":"uint24","name":"_leverageDelta","type":"uint24"},{"internalType":"uint64","name":"_expectedPrice","type":"uint64"},{"internalType":"uint16","name":"_maxSlippageP","type":"uint16"}],"name":"increasePositionSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint120","name":"_collateralDelta","type":"uint120"},{"internalType":"uint24","name":"_leverageDelta","type":"uint24"},{"internalType":"uint64","name":"_expectedPrice","type":"uint64"},{"internalType":"uint16","name":"_maxSlippageP","type":"uint16"}],"name":"increasePositionSizeNative","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_govTimelock","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_govEmergencyTimelock","type":"address"}],"name":"initializeGovEmergencyTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_marketOrdersTimeoutBlocks","type":"uint16"},{"internalType":"address[]","name":"_usersByPassTriggerLink","type":"address[]"}],"name":"initializeTrading","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_trader","type":"address"},{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"bool","name":"_isHoldingFees","type":"bool"}],"name":"initiateManualNegativePnlRealization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"isWrappedNativeToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"},{"internalType":"uint16","name":"pairIndex","type":"uint16"},{"internalType":"uint24","name":"leverage","type":"uint24"},{"internalType":"bool","name":"long","type":"bool"},{"internalType":"bool","name":"isOpen","type":"bool"},{"internalType":"uint8","name":"collateralIndex","type":"uint8"},{"internalType":"enum ITradingStorage.TradeType","name":"tradeType","type":"uint8"},{"internalType":"uint120","name":"collateralAmount","type":"uint120"},{"internalType":"uint64","name":"openPrice","type":"uint64"},{"internalType":"uint64","name":"tp","type":"uint64"},{"internalType":"uint64","name":"sl","type":"uint64"},{"internalType":"bool","name":"isCounterTrade","type":"bool"},{"internalType":"uint160","name":"positionSizeToken","type":"uint160"},{"internalType":"uint24","name":"__placeholder","type":"uint24"}],"internalType":"struct ITradingStorage.Trade","name":"_trade","type":"tuple"},{"internalType":"uint16","name":"_maxSlippageP","type":"uint16"},{"internalType":"address","name":"_referrer","type":"address"}],"name":"openTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"},{"internalType":"uint16","name":"pairIndex","type":"uint16"},{"internalType":"uint24","name":"leverage","type":"uint24"},{"internalType":"bool","name":"long","type":"bool"},{"internalType":"bool","name":"isOpen","type":"bool"},{"internalType":"uint8","name":"collateralIndex","type":"uint8"},{"internalType":"enum ITradingStorage.TradeType","name":"tradeType","type":"uint8"},{"internalType":"uint120","name":"collateralAmount","type":"uint120"},{"internalType":"uint64","name":"openPrice","type":"uint64"},{"internalType":"uint64","name":"tp","type":"uint64"},{"internalType":"uint64","name":"sl","type":"uint64"},{"internalType":"bool","name":"isCounterTrade","type":"bool"},{"internalType":"uint160","name":"positionSizeToken","type":"uint160"},{"internalType":"uint24","name":"__placeholder","type":"uint24"}],"internalType":"struct ITradingStorage.Trade","name":"_trade","type":"tuple"},{"internalType":"uint16","name":"_maxSlippageP","type":"uint16"},{"internalType":"address","name":"_referrer","type":"address"}],"name":"openTradeNative","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"removeTradingDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_accounts","type":"address[]"},{"internalType":"enum IAddressStore.Role[]","name":"_roles","type":"uint8[]"},{"internalType":"bool[]","name":"_values","type":"bool[]"}],"name":"setRoles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"}],"name":"setTradingDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_packed","type":"uint256"}],"name":"triggerOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_packed","type":"uint256"},{"components":[{"internalType":"uint8","name":"signerId","type":"uint8"},{"internalType":"uint32","name":"expiryTs","type":"uint32"},{"internalType":"bool","name":"isLookback","type":"bool"},{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint16[]","name":"pairIndices","type":"uint16[]"},{"components":[{"internalType":"uint56","name":"open","type":"uint56"},{"internalType":"uint56","name":"high","type":"uint56"},{"internalType":"uint56","name":"low","type":"uint56"},{"internalType":"uint56","name":"current","type":"uint56"},{"internalType":"uint32","name":"ts","type":"uint32"}],"internalType":"struct IPriceAggregator.OrderAnswer[]","name":"prices","type":"tuple[]"}],"internalType":"struct IPriceAggregator.SignedPairPrices[]","name":"_signedPairPrices","type":"tuple[]"}],"name":"triggerOrderWithSignatures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"bool[]","name":"_shouldByPass","type":"bool[]"}],"name":"updateByPassTriggerLink","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint24","name":"_newLeverage","type":"uint24"}],"name":"updateLeverage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint24","name":"_newLeverage","type":"uint24"}],"name":"updateLeverageNative","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_valueBlocks","type":"uint16"}],"name":"updateMarketOrdersTimeoutBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint16","name":"_maxSlippageP","type":"uint16"}],"name":"updateMaxClosingSlippageP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint64","name":"_triggerPrice","type":"uint64"},{"internalType":"uint64","name":"_tp","type":"uint64"},{"internalType":"uint64","name":"_sl","type":"uint64"},{"internalType":"uint16","name":"_maxSlippageP","type":"uint16"}],"name":"updateOpenOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint64","name":"_newSl","type":"uint64"}],"name":"updateSl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint64","name":"_newTp","type":"uint64"}],"name":"updateTp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_index","type":"uint32"},{"internalType":"uint120","name":"_amountCollateral","type":"uint120"}],"name":"withdrawPositivePnl","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b506200001c62000022565b620000e3565b600054610100900460ff16156200008f5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811614620000e1576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b612b2480620000f36000396000f3fe6080604052600436106102335760003560e01c806384e9334711610138578063a7cac572116100b0578063c267d6011161007f578063c7e2b2a911610064578063c7e2b2a9146106b1578063eb9359aa146106d1578063f401f2bb146106f157600080fd5b8063c267d6011461067e578063c4d66de81461069157600080fd5b8063a7cac572146105fe578063b5d9e9d01461061e578063b69195401461063e578063b9c972291461065e57600080fd5b80639bf1584e11610107578063a39fac12116100ec578063a39fac1214610524578063a4bb127e146105b6578063a4bdee80146105d657600080fd5b80639bf1584e146104e45780639e8433d01461050457600080fd5b806384e9334714610454578063858863331461048457806385898e08146104a457806395a8c58d146104c457600080fd5b80635179cecf116101cb5780635fa7f2ac1161019a578063731104ba1161017f578063731104ba146103f4578063737b84cd1461040757806373c96d131461043457600080fd5b80635fa7f2ac146103b4578063604755cf146103d457600080fd5b80635179cecf1461033457806352b3aaca1461035457806352d029d2146103745780635bfcc4f81461039457600080fd5b80631d9478b6116102075780631d9478b6146102a257806324058ad3146102d457806336ce736b146102f45780634aac64801461031457600080fd5b806265efc814610238578063031c722b1461024d5780630bce9aaa14610262578063101e650314610282575b600080fd5b61024b610246366004611afc565b610711565b005b34801561025957600080fd5b5061024b610797565b34801561026e57600080fd5b5061024b61027d366004611afc565b610805565b34801561028e57600080fd5b5061024b61029d366004611b7b565b610855565b3480156102ae57600080fd5b506102b7610a39565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102e057600080fd5b5061024b6102ef366004611c5f565b610a48565b34801561030057600080fd5b5061024b61030f366004611cc8565b610afc565b34801561032057600080fd5b506102b761032f366004611d12565b610b51565b34801561034057600080fd5b5061024b61034f366004611e43565b610bd5565b34801561036057600080fd5b5061024b61036f366004611d12565b610d23565b34801561038057600080fd5b5061024b61038f366004611e93565b610e36565b3480156103a057600080fd5b5061024b6103af366004611ee9565b610eab565b3480156103c057600080fd5b5061024b6103cf366004612043565b610f2b565b3480156103e057600080fd5b5061024b6103ef366004611d12565b610fca565b61024b610402366004611c5f565b611049565b34801561041357600080fd5b5061042761042236600461208c565b6110c2565b6040516102cb9190612135565b34801561044057600080fd5b5061024b61044f366004612168565b61114d565b34801561046057600080fd5b5061047461046f366004611d12565b6111a1565b60405190151581526020016102cb565b34801561049057600080fd5b5061024b61049f366004612192565b611221565b3480156104b057600080fd5b506104746104bf366004611d12565b611266565b3480156104d057600080fd5b506104746104df3660046121bc565b6112a9565b3480156104f057600080fd5b5061024b6104ff3660046121e8565b611300565b34801561051057600080fd5b5061024b61051f3660046122ac565b61137d565b34801561053057600080fd5b50610584604080516060810182526000808252602082018190529181019190915250604080516060810182526003546001600160a01b03908116825260045481166020830152600554169181019190915290565b6040805182516001600160a01b03908116825260208085015182169083015292820151909216908201526060016102cb565b3480156105c257600080fd5b5061024b6105d13660046122e3565b6113cc565b3480156105e257600080fd5b506105eb61143b565b60405161ffff90911681526020016102cb565b34801561060a57600080fd5b5061024b610619366004612320565b6114aa565b34801561062a57600080fd5b5061024b610639366004611cc8565b611552565b34801561064a57600080fd5b5061024b610659366004612192565b6115a7565b34801561066a57600080fd5b50610474610679366004612374565b6115ec565b61024b61068c366004611ee9565b61169a565b34801561069d57600080fd5b5061024b6106ac366004611d12565b6116dd565b3480156106bd57600080fd5b5061024b6106cc3660046123b0565b611813565b3480156106dd57600080fd5b5061024b6106ec3660046123fc565b611856565b3480156106fd57600080fd5b5061024b61070c366004611cc8565b611896565b6107196118eb565b604051620cbdf960e31b815263ffffffff8316600482015262ffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906265efc8906044015b60006040518083038186803b15801561077357600080fd5b505af4158015610787573d6000803e3d6000fd5b5050505061079361191a565b5050565b61079f6118eb565b73db4f120d7079e7ec16763aac52c3b6fe1cd6248963031c722b6040518163ffffffff1660e01b815260040160006040518083038186803b1580156107e357600080fd5b505af41580156107f7573d6000803e3d6000fd5b5050505061080361191a565b565b61080d6118eb565b6040516305e74d5560e11b815263ffffffff8316600482015262ffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd6248990630bce9aaa9060440161075b565b600060046108638282611925565b86851415806108725750868314155b1561089057604051637db491eb60e01b815260040160405180910390fd5b60005b87811015610a2e5760008787838181106108af576108af61242b565b90506020020160208101906108c49190612441565b60048111156108d5576108d5612415565b1480156109105750338989838181106108f0576108f061242b565b90506020020160208101906109059190611d12565b6001600160a01b0316145b8061098b5750600487878381811061092a5761092a61242b565b905060200201602081019061093f9190612441565b600481111561095057610950612415565b14801561098b57503389898381811061096b5761096b61242b565b90506020020160208101906109809190611d12565b6001600160a01b0316145b156109a957604051631eb49d6d60e11b815260040160405180910390fd5b610a268989838181106109be576109be61242b565b90506020020160208101906109d39190611d12565b8888848181106109e5576109e561242b565b90506020020160208101906109fa9190612441565b878785818110610a0c57610a0c61242b565b9050602002016020810190610a21919061245c565b61194d565b600101610893565b505050505050505050565b6000610a436119e2565b905090565b610a506118eb565b6040516324058ad360e01b815263ffffffff861660048201526001600160781b038516602482015262ffffff8416604482015267ffffffffffffffff8316606482015261ffff8216608482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906324058ad39060a4015b60006040518083038186803b158015610ad557600080fd5b505af4158015610ae9573d6000803e3d6000fd5b50505050610af561191a565b5050505050565b610b046118eb565b6040516336ce736b60e01b815263ffffffff8316600482015267ffffffffffffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906336ce736b9060440161075b565b604051629558c960e71b81526001600160a01b038216600482015260009073db4f120d7079e7ec16763aac52c3b6fe1cd6248990634aac648090602401602060405180830381865af4158015610bab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcf9190612479565b92915050565b600054600890610100900460ff16158015610bf7575060005460ff8083169116105b610c5f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805461ffff191660ff831617610100179055604051635179cecf60e01b815273db4f120d7079e7ec16763aac52c3b6fe1cd6248990635179cecf90610cac90869086906004016124db565b60006040518083038186803b158015610cc457600080fd5b505af4158015610cd8573d6000803e3d6000fd5b50506000805461ff0019169055505060405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a1505050565b600054601790610100900460ff16158015610d45575060005460ff8083169116105b610da85760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c56565b6000805461ffff191660ff8316176101001790556001600160a01b038216610de357604051632c1c702960e21b815260040160405180910390fd5b610df0826004600161194d565b6000805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b6001610e41816119ec565b60405163296814e960e11b815261ffff8316600482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906352d029d29060240160006040518083038186803b158015610e8f57600080fd5b505af4158015610ea3573d6000803e3d6000fd5b505050505050565b610eb36118eb565b604051631e21236d60e21b815273db4f120d7079e7ec16763aac52c3b6fe1cd62489906378848db490610eee9086908690869060040161250c565b60006040518083038186803b158015610f0657600080fd5b505af4158015610f1a573d6000803e3d6000fd5b50505050610f2661191a565b505050565b610f336118eb565b60016002610f418282611925565b6040516317e9fcab60e21b81526001600160a01b038616600482015263ffffffff85166024820152831515604482015273db4f120d7079e7ec16763aac52c3b6fe1cd6248990635fa7f2ac9060640160006040518083038186803b158015610fa857600080fd5b505af4158015610fbc573d6000803e3d6000fd5b505050505050610f2661191a565b610fd26118eb565b60405163604755cf60e01b81526001600160a01b038216600482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063604755cf906024015b60006040518083038186803b15801561102657600080fd5b505af415801561103a573d6000803e3d6000fd5b5050505061104661191a565b50565b6110516118eb565b604051633988825d60e11b815263ffffffff861660048201526001600160781b038516602482015262ffffff8416604482015267ffffffffffffffff8316606482015261ffff8216608482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063731104ba9060a401610abd565b60405163737b84cd60e01b815260609073db4f120d7079e7ec16763aac52c3b6fe1cd624899063737b84cd906111009087908790879060040161267a565b600060405180830381865af415801561111d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261114591908101906126a6565b949350505050565b6111556118eb565b6040516373c96d1360e01b815263ffffffff831660048201526001600160781b038216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906373c96d139060440161075b565b6040516384e9334760e01b81526001600160a01b038216600482015260009073db4f120d7079e7ec16763aac52c3b6fe1cd62489906384e93347906024015b602060405180830381865af41580156111fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcf919061273a565b6112296118eb565b604051638588633360e01b815263ffffffff8216600482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063858863339060240161100e565b6040516310b131c160e31b81526001600160a01b038216600482015260009073db4f120d7079e7ec16763aac52c3b6fe1cd62489906385898e08906024016111e0565b6001600160a01b0382166000908152600260205260408120818360048111156112d4576112d4612415565b60048111156112e5576112e5612415565b815260208101919091526040016000205460ff169392505050565b6000600361130e8282611925565b604051634df8ac2760e11b815273db4f120d7079e7ec16763aac52c3b6fe1cd6248990639bf1584e906113479087908790600401612757565b60006040518083038186803b15801561135f57600080fd5b505af4158015611373573d6000803e3d6000fd5b5050505050505050565b6113856118eb565b6040516309e8433d60e41b815263ffffffff8316600482015261ffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd6248990639e8433d09060440161075b565b6113d46118eb565b60405163525d893f60e11b815263ffffffff8616600482015267ffffffffffffffff808616602483015280851660448301528316606482015261ffff8216608482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063a4bb127e9060a401610abd565b600073db4f120d7079e7ec16763aac52c3b6fe1cd6248963a4bdee806040518163ffffffff1660e01b8152600401602060405180830381865af4158015611486573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4391906127b0565b6114b26118eb565b6040516353e562b960e11b815263ffffffff851660048201526001600160781b038416602482015262ffffff8316604482015267ffffffffffffffff8216606482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063a7cac5729060840160006040518083038186803b15801561152c57600080fd5b505af4158015611540573d6000803e3d6000fd5b5050505061154c61191a565b50505050565b61155a6118eb565b604051630b5d9e9d60e41b815263ffffffff8316600482015267ffffffffffffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063b5d9e9d09060440161075b565b6115af6118eb565b6040516302da465560e61b815263ffffffff8216600482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063b69195409060240161100e565b6001600160a01b03831660009081526002602052604081208184600481111561161757611617612415565b600481111561162857611628612415565b815260208101919091526040016000205460ff168061114557506001600160a01b03841660009081526002602052604081209083600481111561166d5761166d612415565b600481111561167e5761167e612415565b815260208101919091526040016000205460ff16949350505050565b6116a26118eb565b6040516304f6dc0760e31b815273db4f120d7079e7ec16763aac52c3b6fe1cd62489906327b6e03890610eee9086908690869060040161250c565b600054610100900460ff16158080156116fd5750600054600160ff909116105b806117175750303b158015611717575060005460ff166001145b61177a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c56565b6000805460ff19166001179055801561179d576000805461ff0019166101001790555b6001600160a01b0382166117c457604051632c1c702960e21b815260040160405180910390fd5b6117d1826000600161194d565b8015610793576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001610e2a565b61181b6118eb565b604051630b8c903d60e41b815273db4f120d7079e7ec16763aac52c3b6fe1cd624899063b8c903d090610eee90869086908690600401612984565b61185e6118eb565b6040516375c9acd560e11b81526004810182905273db4f120d7079e7ec16763aac52c3b6fe1cd624899063eb9359aa9060240161100e565b61189e6118eb565b60405163f401f2bb60e01b815263ffffffff8316600482015267ffffffffffffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063f401f2bb9060440161075b565b61028b5460010361190f57604051633ee5aeb560e01b815260040160405180910390fd5b610803600161028b55565b610803600061028b55565b6119303383836115ec565b61079357604051631b17ff5560e21b815260040160405180910390fd5b6001600160a01b0383166000908152600260205260408120829184600481111561197957611979612415565b600481111561198a5761198a612415565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f8d7fdec37f50c07219a6a0859420936836eb9254bf412035e3acede18b8b093d838383604051610d1693929190612ab8565b6000610a43611a13565b6119f633826112a9565b61104657604051631b17ff5560e21b815260040160405180910390fd5b600061a4b14603611a3757507382af49447d8a07e3bd95bd0d56f35241523fbab190565b6121054603611a59575073420000000000000000000000000000000000000690565b6181734603611a7657506e0f7e000644657dc9417b185962645a90565b60894603611a975750730d500b1d8e8ef31e21c99d1db9a6444d3adf127090565b62066eee4603611aba575073980b62da83eff3d4576c647993b0c1d7faf17c7390565b617a694603611aca57506101a590565b50600090565b803563ffffffff81168114611ae457600080fd5b919050565b803562ffffff81168114611ae457600080fd5b60008060408385031215611b0f57600080fd5b611b1883611ad0565b9150611b2660208401611ae9565b90509250929050565b60008083601f840112611b4157600080fd5b50813567ffffffffffffffff811115611b5957600080fd5b6020830191508360208260051b8501011115611b7457600080fd5b9250929050565b60008060008060008060608789031215611b9457600080fd5b863567ffffffffffffffff80821115611bac57600080fd5b611bb88a838b01611b2f565b90985096506020890135915080821115611bd157600080fd5b611bdd8a838b01611b2f565b90965094506040890135915080821115611bf657600080fd5b50611c0389828a01611b2f565b979a9699509497509295939492505050565b80356001600160781b0381168114611ae457600080fd5b803567ffffffffffffffff81168114611ae457600080fd5b61ffff8116811461104657600080fd5b8035611ae481611c44565b600080600080600060a08688031215611c7757600080fd5b611c8086611ad0565b9450611c8e60208701611c15565b9350611c9c60408701611ae9565b9250611caa60608701611c2c565b91506080860135611cba81611c44565b809150509295509295909350565b60008060408385031215611cdb57600080fd5b611ce483611ad0565b9150611b2660208401611c2c565b6001600160a01b038116811461104657600080fd5b8035611ae481611cf2565b600060208284031215611d2457600080fd5b8135611d2f81611cf2565b9392505050565b634e487b7160e01b600052604160045260246000fd5b6040516101e0810167ffffffffffffffff81118282101715611d7057611d70611d36565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715611d9f57611d9f611d36565b604052919050565b600067ffffffffffffffff821115611dc157611dc1611d36565b5060051b60200190565b600082601f830112611ddc57600080fd5b81356020611df1611dec83611da7565b611d76565b8083825260208201915060208460051b870101935086841115611e1357600080fd5b602086015b84811015611e38578035611e2b81611cf2565b8352918301918301611e18565b509695505050505050565b60008060408385031215611e5657600080fd5b8235611e6181611c44565b9150602083013567ffffffffffffffff811115611e7d57600080fd5b611e8985828601611dcb565b9150509250929050565b600060208284031215611ea557600080fd5b8135611d2f81611c44565b801515811461104657600080fd5b8035611ae481611eb0565b803560ff81168114611ae457600080fd5b803560038110611ae457600080fd5b6000806000838503610220811215611f0057600080fd5b6101e080821215611f1057600080fd5b611f18611d4c565b9150611f2386611d07565b8252611f3160208701611ad0565b6020830152611f4260408701611c54565b6040830152611f5360608701611ae9565b6060830152611f6460808701611ebe565b6080830152611f7560a08701611ebe565b60a0830152611f8660c08701611ec9565b60c0830152611f9760e08701611eda565b60e0830152610100611faa818801611c15565b90830152610120611fbc878201611c2c565b90830152610140611fce878201611c2c565b90830152610160611fe0878201611c2c565b90830152610180611ff2878201611ebe565b908301526101a0612004878201611d07565b908301526101c0612016878201611ae9565b8184015250819450612029818701611c54565b9350505061203a6102008501611d07565b90509250925092565b60008060006060848603121561205857600080fd5b833561206381611cf2565b925061207160208501611ad0565b9150604084013561208181611eb0565b809150509250925092565b6000806000604084860312156120a157600080fd5b83356120ac81611cf2565b9250602084013567ffffffffffffffff808211156120c957600080fd5b818601915086601f8301126120dd57600080fd5b8135818111156120ec57600080fd5b8760208285010111156120fe57600080fd5b6020830194508093505050509250925092565b60005b8381101561212c578181015183820152602001612114565b50506000910152565b6020815260008251806020840152612154816040850160208701612111565b601f01601f19169190910160400192915050565b6000806040838503121561217b57600080fd5b61218483611ad0565b9150611b2660208401611c15565b6000602082840312156121a457600080fd5b611d2f82611ad0565b803560058110611ae457600080fd5b600080604083850312156121cf57600080fd5b82356121da81611cf2565b9150611b26602084016121ad565b600080604083850312156121fb57600080fd5b823567ffffffffffffffff8082111561221357600080fd5b61221f86838701611dcb565b935060209150818501358181111561223657600080fd5b85019050601f8101861361224957600080fd5b8035612257611dec82611da7565b81815260059190911b8201830190838101908883111561227657600080fd5b928401925b8284101561229d57833561228e81611eb0565b8252928401929084019061227b565b80955050505050509250929050565b600080604083850312156122bf57600080fd5b6122c883611ad0565b915060208301356122d881611c44565b809150509250929050565b600080600080600060a086880312156122fb57600080fd5b61230486611ad0565b945061231260208701611c2c565b9350611c9c60408701611c2c565b6000806000806080858703121561233657600080fd5b61233f85611ad0565b935061234d60208601611c15565b925061235b60408601611ae9565b915061236960608601611c2c565b905092959194509250565b60008060006060848603121561238957600080fd5b833561239481611cf2565b92506123a2602085016121ad565b915061203a604085016121ad565b6000806000604084860312156123c557600080fd5b83359250602084013567ffffffffffffffff8111156123e357600080fd5b6123ef86828701611b2f565b9497909650939450505050565b60006020828403121561240e57600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561245357600080fd5b611d2f826121ad565b60006020828403121561246e57600080fd5b8135611d2f81611eb0565b60006020828403121561248b57600080fd5b8151611d2f81611cf2565b60008151808452602080850194506020840160005b838110156124d05781516001600160a01b0316875295820195908201906001016124ab565b509495945050505050565b61ffff831681526040602082015260006111456040830184612496565b6003811061250857612508612415565b9052565b83516001600160a01b0316815261022081016020850151612535602084018263ffffffff169052565b50604085015161254b604084018261ffff169052565b506060850151612562606084018262ffffff169052565b506080850151612576608084018215159052565b5060a085015161258a60a084018215159052565b5060c085015161259f60c084018260ff169052565b5060e08501516125b260e08401826124f8565b50610100858101516001600160781b0316908301526101208086015167ffffffffffffffff90811691840191909152610140808701518216908401526101608087015190911690830152610180808601511515908301526101a0808601516001600160a01b03908116918401919091526101c09586015162ffffff169583019590955261ffff939093166101e082015292166102009092019190915290565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b038416815260406020820152600061269d604083018486612651565b95945050505050565b6000602082840312156126b857600080fd5b815167ffffffffffffffff808211156126d057600080fd5b818401915084601f8301126126e457600080fd5b8151818111156126f6576126f6611d36565b612709601f8201601f1916602001611d76565b915080825285602082850101111561272057600080fd5b612731816020840160208601612111565b50949350505050565b60006020828403121561274c57600080fd5b8151611d2f81611eb0565b60408152600061276a6040830185612496565b82810360208481019190915284518083528582019282019060005b818110156127a3578451151583529383019391830191600101612785565b5090979650505050505050565b6000602082840312156127c257600080fd5b8151611d2f81611c44565b6000808335601e198436030181126127e457600080fd5b830160208101925035905067ffffffffffffffff81111561280457600080fd5b803603821315611b7457600080fd5b6000808335601e1984360301811261282a57600080fd5b830160208101925035905067ffffffffffffffff81111561284a57600080fd5b8060051b3603821315611b7457600080fd5b8183526000602080850194508260005b858110156124d057813561287f81611c44565b61ffff168752958201959082019060010161286c565b6000808335601e198436030181126128ac57600080fd5b830160208101925035905067ffffffffffffffff8111156128cc57600080fd5b60a081023603821315611b7457600080fd5b803566ffffffffffffff81168114611ae457600080fd5b8183526000602080850194508260005b858110156124d05766ffffffffffffff8061291f846128de565b1688528061292e8585016128de565b16848901526040816129418286016128de565b16908901526060816129548583016128de565b169089015250608063ffffffff61296c848301611ad0565b169088015260a0968701969190910190600101612905565b60006040808301868452602060406020860152818683526060925060608601905060608760051b870101886000805b8a811015612aa757898403605f190185528235368d900360de190181126129d8578283fd5b8c0160e060ff6129e783611ec9565b16865263ffffffff6129fa898401611ad0565b1688870152612a0a8a8301611ebe565b15158a870152612a1b828a01611ad0565b63ffffffff16898701526080612a33838201846127cd565b83838a0152612a45848a018284612651565b935050505060a0612a5881840184612813565b888403838a0152612a6a84828461285c565b935050505060c0612a7d81840184612895565b935087830382890152612a918385836128f5565b988a0198975050509387019350506001016129b3565b50919b9a5050505050505050505050565b6001600160a01b03841681526060810160058410612ad857612ad8612415565b836020830152821515604083015294935050505056fea2646970667358221220276ff7f59d764c35cb18b39771bf557edd5e83bcaaaf42ac47a7d6559493973064736f6c63430008170033
Deployed Bytecode
0x6080604052600436106102335760003560e01c806384e9334711610138578063a7cac572116100b0578063c267d6011161007f578063c7e2b2a911610064578063c7e2b2a9146106b1578063eb9359aa146106d1578063f401f2bb146106f157600080fd5b8063c267d6011461067e578063c4d66de81461069157600080fd5b8063a7cac572146105fe578063b5d9e9d01461061e578063b69195401461063e578063b9c972291461065e57600080fd5b80639bf1584e11610107578063a39fac12116100ec578063a39fac1214610524578063a4bb127e146105b6578063a4bdee80146105d657600080fd5b80639bf1584e146104e45780639e8433d01461050457600080fd5b806384e9334714610454578063858863331461048457806385898e08146104a457806395a8c58d146104c457600080fd5b80635179cecf116101cb5780635fa7f2ac1161019a578063731104ba1161017f578063731104ba146103f4578063737b84cd1461040757806373c96d131461043457600080fd5b80635fa7f2ac146103b4578063604755cf146103d457600080fd5b80635179cecf1461033457806352b3aaca1461035457806352d029d2146103745780635bfcc4f81461039457600080fd5b80631d9478b6116102075780631d9478b6146102a257806324058ad3146102d457806336ce736b146102f45780634aac64801461031457600080fd5b806265efc814610238578063031c722b1461024d5780630bce9aaa14610262578063101e650314610282575b600080fd5b61024b610246366004611afc565b610711565b005b34801561025957600080fd5b5061024b610797565b34801561026e57600080fd5b5061024b61027d366004611afc565b610805565b34801561028e57600080fd5b5061024b61029d366004611b7b565b610855565b3480156102ae57600080fd5b506102b7610a39565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102e057600080fd5b5061024b6102ef366004611c5f565b610a48565b34801561030057600080fd5b5061024b61030f366004611cc8565b610afc565b34801561032057600080fd5b506102b761032f366004611d12565b610b51565b34801561034057600080fd5b5061024b61034f366004611e43565b610bd5565b34801561036057600080fd5b5061024b61036f366004611d12565b610d23565b34801561038057600080fd5b5061024b61038f366004611e93565b610e36565b3480156103a057600080fd5b5061024b6103af366004611ee9565b610eab565b3480156103c057600080fd5b5061024b6103cf366004612043565b610f2b565b3480156103e057600080fd5b5061024b6103ef366004611d12565b610fca565b61024b610402366004611c5f565b611049565b34801561041357600080fd5b5061042761042236600461208c565b6110c2565b6040516102cb9190612135565b34801561044057600080fd5b5061024b61044f366004612168565b61114d565b34801561046057600080fd5b5061047461046f366004611d12565b6111a1565b60405190151581526020016102cb565b34801561049057600080fd5b5061024b61049f366004612192565b611221565b3480156104b057600080fd5b506104746104bf366004611d12565b611266565b3480156104d057600080fd5b506104746104df3660046121bc565b6112a9565b3480156104f057600080fd5b5061024b6104ff3660046121e8565b611300565b34801561051057600080fd5b5061024b61051f3660046122ac565b61137d565b34801561053057600080fd5b50610584604080516060810182526000808252602082018190529181019190915250604080516060810182526003546001600160a01b03908116825260045481166020830152600554169181019190915290565b6040805182516001600160a01b03908116825260208085015182169083015292820151909216908201526060016102cb565b3480156105c257600080fd5b5061024b6105d13660046122e3565b6113cc565b3480156105e257600080fd5b506105eb61143b565b60405161ffff90911681526020016102cb565b34801561060a57600080fd5b5061024b610619366004612320565b6114aa565b34801561062a57600080fd5b5061024b610639366004611cc8565b611552565b34801561064a57600080fd5b5061024b610659366004612192565b6115a7565b34801561066a57600080fd5b50610474610679366004612374565b6115ec565b61024b61068c366004611ee9565b61169a565b34801561069d57600080fd5b5061024b6106ac366004611d12565b6116dd565b3480156106bd57600080fd5b5061024b6106cc3660046123b0565b611813565b3480156106dd57600080fd5b5061024b6106ec3660046123fc565b611856565b3480156106fd57600080fd5b5061024b61070c366004611cc8565b611896565b6107196118eb565b604051620cbdf960e31b815263ffffffff8316600482015262ffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906265efc8906044015b60006040518083038186803b15801561077357600080fd5b505af4158015610787573d6000803e3d6000fd5b5050505061079361191a565b5050565b61079f6118eb565b73db4f120d7079e7ec16763aac52c3b6fe1cd6248963031c722b6040518163ffffffff1660e01b815260040160006040518083038186803b1580156107e357600080fd5b505af41580156107f7573d6000803e3d6000fd5b5050505061080361191a565b565b61080d6118eb565b6040516305e74d5560e11b815263ffffffff8316600482015262ffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd6248990630bce9aaa9060440161075b565b600060046108638282611925565b86851415806108725750868314155b1561089057604051637db491eb60e01b815260040160405180910390fd5b60005b87811015610a2e5760008787838181106108af576108af61242b565b90506020020160208101906108c49190612441565b60048111156108d5576108d5612415565b1480156109105750338989838181106108f0576108f061242b565b90506020020160208101906109059190611d12565b6001600160a01b0316145b8061098b5750600487878381811061092a5761092a61242b565b905060200201602081019061093f9190612441565b600481111561095057610950612415565b14801561098b57503389898381811061096b5761096b61242b565b90506020020160208101906109809190611d12565b6001600160a01b0316145b156109a957604051631eb49d6d60e11b815260040160405180910390fd5b610a268989838181106109be576109be61242b565b90506020020160208101906109d39190611d12565b8888848181106109e5576109e561242b565b90506020020160208101906109fa9190612441565b878785818110610a0c57610a0c61242b565b9050602002016020810190610a21919061245c565b61194d565b600101610893565b505050505050505050565b6000610a436119e2565b905090565b610a506118eb565b6040516324058ad360e01b815263ffffffff861660048201526001600160781b038516602482015262ffffff8416604482015267ffffffffffffffff8316606482015261ffff8216608482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906324058ad39060a4015b60006040518083038186803b158015610ad557600080fd5b505af4158015610ae9573d6000803e3d6000fd5b50505050610af561191a565b5050505050565b610b046118eb565b6040516336ce736b60e01b815263ffffffff8316600482015267ffffffffffffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906336ce736b9060440161075b565b604051629558c960e71b81526001600160a01b038216600482015260009073db4f120d7079e7ec16763aac52c3b6fe1cd6248990634aac648090602401602060405180830381865af4158015610bab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcf9190612479565b92915050565b600054600890610100900460ff16158015610bf7575060005460ff8083169116105b610c5f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805461ffff191660ff831617610100179055604051635179cecf60e01b815273db4f120d7079e7ec16763aac52c3b6fe1cd6248990635179cecf90610cac90869086906004016124db565b60006040518083038186803b158015610cc457600080fd5b505af4158015610cd8573d6000803e3d6000fd5b50506000805461ff0019169055505060405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a1505050565b600054601790610100900460ff16158015610d45575060005460ff8083169116105b610da85760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c56565b6000805461ffff191660ff8316176101001790556001600160a01b038216610de357604051632c1c702960e21b815260040160405180910390fd5b610df0826004600161194d565b6000805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b6001610e41816119ec565b60405163296814e960e11b815261ffff8316600482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906352d029d29060240160006040518083038186803b158015610e8f57600080fd5b505af4158015610ea3573d6000803e3d6000fd5b505050505050565b610eb36118eb565b604051631e21236d60e21b815273db4f120d7079e7ec16763aac52c3b6fe1cd62489906378848db490610eee9086908690869060040161250c565b60006040518083038186803b158015610f0657600080fd5b505af4158015610f1a573d6000803e3d6000fd5b50505050610f2661191a565b505050565b610f336118eb565b60016002610f418282611925565b6040516317e9fcab60e21b81526001600160a01b038616600482015263ffffffff85166024820152831515604482015273db4f120d7079e7ec16763aac52c3b6fe1cd6248990635fa7f2ac9060640160006040518083038186803b158015610fa857600080fd5b505af4158015610fbc573d6000803e3d6000fd5b505050505050610f2661191a565b610fd26118eb565b60405163604755cf60e01b81526001600160a01b038216600482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063604755cf906024015b60006040518083038186803b15801561102657600080fd5b505af415801561103a573d6000803e3d6000fd5b5050505061104661191a565b50565b6110516118eb565b604051633988825d60e11b815263ffffffff861660048201526001600160781b038516602482015262ffffff8416604482015267ffffffffffffffff8316606482015261ffff8216608482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063731104ba9060a401610abd565b60405163737b84cd60e01b815260609073db4f120d7079e7ec16763aac52c3b6fe1cd624899063737b84cd906111009087908790879060040161267a565b600060405180830381865af415801561111d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261114591908101906126a6565b949350505050565b6111556118eb565b6040516373c96d1360e01b815263ffffffff831660048201526001600160781b038216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd62489906373c96d139060440161075b565b6040516384e9334760e01b81526001600160a01b038216600482015260009073db4f120d7079e7ec16763aac52c3b6fe1cd62489906384e93347906024015b602060405180830381865af41580156111fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcf919061273a565b6112296118eb565b604051638588633360e01b815263ffffffff8216600482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063858863339060240161100e565b6040516310b131c160e31b81526001600160a01b038216600482015260009073db4f120d7079e7ec16763aac52c3b6fe1cd62489906385898e08906024016111e0565b6001600160a01b0382166000908152600260205260408120818360048111156112d4576112d4612415565b60048111156112e5576112e5612415565b815260208101919091526040016000205460ff169392505050565b6000600361130e8282611925565b604051634df8ac2760e11b815273db4f120d7079e7ec16763aac52c3b6fe1cd6248990639bf1584e906113479087908790600401612757565b60006040518083038186803b15801561135f57600080fd5b505af4158015611373573d6000803e3d6000fd5b5050505050505050565b6113856118eb565b6040516309e8433d60e41b815263ffffffff8316600482015261ffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd6248990639e8433d09060440161075b565b6113d46118eb565b60405163525d893f60e11b815263ffffffff8616600482015267ffffffffffffffff808616602483015280851660448301528316606482015261ffff8216608482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063a4bb127e9060a401610abd565b600073db4f120d7079e7ec16763aac52c3b6fe1cd6248963a4bdee806040518163ffffffff1660e01b8152600401602060405180830381865af4158015611486573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4391906127b0565b6114b26118eb565b6040516353e562b960e11b815263ffffffff851660048201526001600160781b038416602482015262ffffff8316604482015267ffffffffffffffff8216606482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063a7cac5729060840160006040518083038186803b15801561152c57600080fd5b505af4158015611540573d6000803e3d6000fd5b5050505061154c61191a565b50505050565b61155a6118eb565b604051630b5d9e9d60e41b815263ffffffff8316600482015267ffffffffffffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063b5d9e9d09060440161075b565b6115af6118eb565b6040516302da465560e61b815263ffffffff8216600482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063b69195409060240161100e565b6001600160a01b03831660009081526002602052604081208184600481111561161757611617612415565b600481111561162857611628612415565b815260208101919091526040016000205460ff168061114557506001600160a01b03841660009081526002602052604081209083600481111561166d5761166d612415565b600481111561167e5761167e612415565b815260208101919091526040016000205460ff16949350505050565b6116a26118eb565b6040516304f6dc0760e31b815273db4f120d7079e7ec16763aac52c3b6fe1cd62489906327b6e03890610eee9086908690869060040161250c565b600054610100900460ff16158080156116fd5750600054600160ff909116105b806117175750303b158015611717575060005460ff166001145b61177a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c56565b6000805460ff19166001179055801561179d576000805461ff0019166101001790555b6001600160a01b0382166117c457604051632c1c702960e21b815260040160405180910390fd5b6117d1826000600161194d565b8015610793576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001610e2a565b61181b6118eb565b604051630b8c903d60e41b815273db4f120d7079e7ec16763aac52c3b6fe1cd624899063b8c903d090610eee90869086908690600401612984565b61185e6118eb565b6040516375c9acd560e11b81526004810182905273db4f120d7079e7ec16763aac52c3b6fe1cd624899063eb9359aa9060240161100e565b61189e6118eb565b60405163f401f2bb60e01b815263ffffffff8316600482015267ffffffffffffffff8216602482015273db4f120d7079e7ec16763aac52c3b6fe1cd624899063f401f2bb9060440161075b565b61028b5460010361190f57604051633ee5aeb560e01b815260040160405180910390fd5b610803600161028b55565b610803600061028b55565b6119303383836115ec565b61079357604051631b17ff5560e21b815260040160405180910390fd5b6001600160a01b0383166000908152600260205260408120829184600481111561197957611979612415565b600481111561198a5761198a612415565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f8d7fdec37f50c07219a6a0859420936836eb9254bf412035e3acede18b8b093d838383604051610d1693929190612ab8565b6000610a43611a13565b6119f633826112a9565b61104657604051631b17ff5560e21b815260040160405180910390fd5b600061a4b14603611a3757507382af49447d8a07e3bd95bd0d56f35241523fbab190565b6121054603611a59575073420000000000000000000000000000000000000690565b6181734603611a7657506e0f7e000644657dc9417b185962645a90565b60894603611a975750730d500b1d8e8ef31e21c99d1db9a6444d3adf127090565b62066eee4603611aba575073980b62da83eff3d4576c647993b0c1d7faf17c7390565b617a694603611aca57506101a590565b50600090565b803563ffffffff81168114611ae457600080fd5b919050565b803562ffffff81168114611ae457600080fd5b60008060408385031215611b0f57600080fd5b611b1883611ad0565b9150611b2660208401611ae9565b90509250929050565b60008083601f840112611b4157600080fd5b50813567ffffffffffffffff811115611b5957600080fd5b6020830191508360208260051b8501011115611b7457600080fd5b9250929050565b60008060008060008060608789031215611b9457600080fd5b863567ffffffffffffffff80821115611bac57600080fd5b611bb88a838b01611b2f565b90985096506020890135915080821115611bd157600080fd5b611bdd8a838b01611b2f565b90965094506040890135915080821115611bf657600080fd5b50611c0389828a01611b2f565b979a9699509497509295939492505050565b80356001600160781b0381168114611ae457600080fd5b803567ffffffffffffffff81168114611ae457600080fd5b61ffff8116811461104657600080fd5b8035611ae481611c44565b600080600080600060a08688031215611c7757600080fd5b611c8086611ad0565b9450611c8e60208701611c15565b9350611c9c60408701611ae9565b9250611caa60608701611c2c565b91506080860135611cba81611c44565b809150509295509295909350565b60008060408385031215611cdb57600080fd5b611ce483611ad0565b9150611b2660208401611c2c565b6001600160a01b038116811461104657600080fd5b8035611ae481611cf2565b600060208284031215611d2457600080fd5b8135611d2f81611cf2565b9392505050565b634e487b7160e01b600052604160045260246000fd5b6040516101e0810167ffffffffffffffff81118282101715611d7057611d70611d36565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715611d9f57611d9f611d36565b604052919050565b600067ffffffffffffffff821115611dc157611dc1611d36565b5060051b60200190565b600082601f830112611ddc57600080fd5b81356020611df1611dec83611da7565b611d76565b8083825260208201915060208460051b870101935086841115611e1357600080fd5b602086015b84811015611e38578035611e2b81611cf2565b8352918301918301611e18565b509695505050505050565b60008060408385031215611e5657600080fd5b8235611e6181611c44565b9150602083013567ffffffffffffffff811115611e7d57600080fd5b611e8985828601611dcb565b9150509250929050565b600060208284031215611ea557600080fd5b8135611d2f81611c44565b801515811461104657600080fd5b8035611ae481611eb0565b803560ff81168114611ae457600080fd5b803560038110611ae457600080fd5b6000806000838503610220811215611f0057600080fd5b6101e080821215611f1057600080fd5b611f18611d4c565b9150611f2386611d07565b8252611f3160208701611ad0565b6020830152611f4260408701611c54565b6040830152611f5360608701611ae9565b6060830152611f6460808701611ebe565b6080830152611f7560a08701611ebe565b60a0830152611f8660c08701611ec9565b60c0830152611f9760e08701611eda565b60e0830152610100611faa818801611c15565b90830152610120611fbc878201611c2c565b90830152610140611fce878201611c2c565b90830152610160611fe0878201611c2c565b90830152610180611ff2878201611ebe565b908301526101a0612004878201611d07565b908301526101c0612016878201611ae9565b8184015250819450612029818701611c54565b9350505061203a6102008501611d07565b90509250925092565b60008060006060848603121561205857600080fd5b833561206381611cf2565b925061207160208501611ad0565b9150604084013561208181611eb0565b809150509250925092565b6000806000604084860312156120a157600080fd5b83356120ac81611cf2565b9250602084013567ffffffffffffffff808211156120c957600080fd5b818601915086601f8301126120dd57600080fd5b8135818111156120ec57600080fd5b8760208285010111156120fe57600080fd5b6020830194508093505050509250925092565b60005b8381101561212c578181015183820152602001612114565b50506000910152565b6020815260008251806020840152612154816040850160208701612111565b601f01601f19169190910160400192915050565b6000806040838503121561217b57600080fd5b61218483611ad0565b9150611b2660208401611c15565b6000602082840312156121a457600080fd5b611d2f82611ad0565b803560058110611ae457600080fd5b600080604083850312156121cf57600080fd5b82356121da81611cf2565b9150611b26602084016121ad565b600080604083850312156121fb57600080fd5b823567ffffffffffffffff8082111561221357600080fd5b61221f86838701611dcb565b935060209150818501358181111561223657600080fd5b85019050601f8101861361224957600080fd5b8035612257611dec82611da7565b81815260059190911b8201830190838101908883111561227657600080fd5b928401925b8284101561229d57833561228e81611eb0565b8252928401929084019061227b565b80955050505050509250929050565b600080604083850312156122bf57600080fd5b6122c883611ad0565b915060208301356122d881611c44565b809150509250929050565b600080600080600060a086880312156122fb57600080fd5b61230486611ad0565b945061231260208701611c2c565b9350611c9c60408701611c2c565b6000806000806080858703121561233657600080fd5b61233f85611ad0565b935061234d60208601611c15565b925061235b60408601611ae9565b915061236960608601611c2c565b905092959194509250565b60008060006060848603121561238957600080fd5b833561239481611cf2565b92506123a2602085016121ad565b915061203a604085016121ad565b6000806000604084860312156123c557600080fd5b83359250602084013567ffffffffffffffff8111156123e357600080fd5b6123ef86828701611b2f565b9497909650939450505050565b60006020828403121561240e57600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561245357600080fd5b611d2f826121ad565b60006020828403121561246e57600080fd5b8135611d2f81611eb0565b60006020828403121561248b57600080fd5b8151611d2f81611cf2565b60008151808452602080850194506020840160005b838110156124d05781516001600160a01b0316875295820195908201906001016124ab565b509495945050505050565b61ffff831681526040602082015260006111456040830184612496565b6003811061250857612508612415565b9052565b83516001600160a01b0316815261022081016020850151612535602084018263ffffffff169052565b50604085015161254b604084018261ffff169052565b506060850151612562606084018262ffffff169052565b506080850151612576608084018215159052565b5060a085015161258a60a084018215159052565b5060c085015161259f60c084018260ff169052565b5060e08501516125b260e08401826124f8565b50610100858101516001600160781b0316908301526101208086015167ffffffffffffffff90811691840191909152610140808701518216908401526101608087015190911690830152610180808601511515908301526101a0808601516001600160a01b03908116918401919091526101c09586015162ffffff169583019590955261ffff939093166101e082015292166102009092019190915290565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b038416815260406020820152600061269d604083018486612651565b95945050505050565b6000602082840312156126b857600080fd5b815167ffffffffffffffff808211156126d057600080fd5b818401915084601f8301126126e457600080fd5b8151818111156126f6576126f6611d36565b612709601f8201601f1916602001611d76565b915080825285602082850101111561272057600080fd5b612731816020840160208601612111565b50949350505050565b60006020828403121561274c57600080fd5b8151611d2f81611eb0565b60408152600061276a6040830185612496565b82810360208481019190915284518083528582019282019060005b818110156127a3578451151583529383019391830191600101612785565b5090979650505050505050565b6000602082840312156127c257600080fd5b8151611d2f81611c44565b6000808335601e198436030181126127e457600080fd5b830160208101925035905067ffffffffffffffff81111561280457600080fd5b803603821315611b7457600080fd5b6000808335601e1984360301811261282a57600080fd5b830160208101925035905067ffffffffffffffff81111561284a57600080fd5b8060051b3603821315611b7457600080fd5b8183526000602080850194508260005b858110156124d057813561287f81611c44565b61ffff168752958201959082019060010161286c565b6000808335601e198436030181126128ac57600080fd5b830160208101925035905067ffffffffffffffff8111156128cc57600080fd5b60a081023603821315611b7457600080fd5b803566ffffffffffffff81168114611ae457600080fd5b8183526000602080850194508260005b858110156124d05766ffffffffffffff8061291f846128de565b1688528061292e8585016128de565b16848901526040816129418286016128de565b16908901526060816129548583016128de565b169089015250608063ffffffff61296c848301611ad0565b169088015260a0968701969190910190600101612905565b60006040808301868452602060406020860152818683526060925060608601905060608760051b870101886000805b8a811015612aa757898403605f190185528235368d900360de190181126129d8578283fd5b8c0160e060ff6129e783611ec9565b16865263ffffffff6129fa898401611ad0565b1688870152612a0a8a8301611ebe565b15158a870152612a1b828a01611ad0565b63ffffffff16898701526080612a33838201846127cd565b83838a0152612a45848a018284612651565b935050505060a0612a5881840184612813565b888403838a0152612a6a84828461285c565b935050505060c0612a7d81840184612895565b935087830382890152612a918385836128f5565b988a0198975050509387019350506001016129b3565b50919b9a5050505050505050505050565b6001600160a01b03841681526060810160058410612ad857612ad8612415565b836020830152821515604083015294935050505056fea2646970667358221220276ff7f59d764c35cb18b39771bf557edd5e83bcaaaf42ac47a7d6559493973064736f6c63430008170033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.