Skip to content

Commit

Permalink
Merge pull request #180 from Flocqst/kip-122
Browse files Browse the repository at this point in the history
imposeOrderFlowFee Tests
  • Loading branch information
JaredBorders authored May 3, 2024
2 parents 8a5e934 + ed10aab commit 3401779
Show file tree
Hide file tree
Showing 8 changed files with 464 additions and 70 deletions.
77 changes: 56 additions & 21 deletions src/Account.sol
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,12 @@ contract Account is IAccount, Auth, OpsReady {
public
view
override
returns (uint256)
returns (uint256 sUSDmarketRate, uint256 orderFlowFee)
{
return _calculateOrderFlowFee(_market, _sizeDelta);
// fetch current sUSD exchange rate for market
(sUSDmarketRate,) = _sUSDRate(IPerpsV2MarketConsolidated(_market));

orderFlowFee = _calculateOrderFlowFee(_market, _sizeDelta);
}

/*//////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -608,14 +611,21 @@ contract Account is IAccount, Auth, OpsReady {
/// @notice impose an order flow fee on the account
/// @param _market: address of market
/// @param _sizeDelta: size delta of order
/// @param _desiredFillPrice: desiredFillPrice if this is a delayed order, 0 otherwise
/// @dev will attempt to deduct fee from account's idle margin first
/// @dev if fee exceeds idle margin, fee will be deducted from market's margin
/// if possible. If not possible, the transaction will revert.
/// @dev if fee is deducted from market's margin, then the following order
/// may be rejected if the account has insufficient available market margin
function _imposeOrderFlowFee(address _market, int256 _sizeDelta) internal {
/// @dev _desiredFillPrice is used to calculate orderFlowFee with correct price for Delayed orders
function _imposeOrderFlowFee(
address _market,
int256 _sizeDelta,
uint256 _desiredFillPrice
) internal {
// calculate order flow fee
uint256 fee = _calculateOrderFlowFee(_market, _sizeDelta);
uint256 fee =
_calculateOrderFlowFee(_market, _sizeDelta, _desiredFillPrice);

if (fee != 0) {
uint256 idleMargin = freeMargin();
Expand All @@ -635,24 +645,44 @@ contract Account is IAccount, Auth, OpsReady {
MARGIN_ASSET.transfer(SETTINGS.TREASURY(), fee);
}

/// @custom:todo add event emission for order flow fee imposed
EVENTS.emitOrderFlowFeeImposed({amount: fee});
}

/// @notice impose an order flow fee on the account
/// @param _market: address of market
/// @param _sizeDelta: size delta of order
/// @dev will attempt to deduct fee from account's idle margin first
/// @dev if fee exceeds idle margin, fee will be deducted from market's margin
/// if possible. If not possible, the transaction will revert.
/// @dev if fee is deducted from market's margin, then the following order
/// may be rejected if the account has insufficient available market margin
function _imposeOrderFlowFee(address _market, int256 _sizeDelta) internal {
_imposeOrderFlowFee(_market, _sizeDelta, 0);
}

/// @notice calculate order flow fee for a given market and size delta
/// @param _market: address of market
/// @param _sizeDelta: size delta of order
/// @param _desiredFillPrice desiredFillPrice in case of a delayed Order
/// @return fee: order flow fee to impose
function _calculateOrderFlowFee(address _market, int256 _sizeDelta)
internal
view
returns (uint256)
{
/// @dev _desiredFillPrice is used to calculate orderFlowFee for Delayed Orders
function _calculateOrderFlowFee(
address _market,
int256 _sizeDelta,
uint256 _desiredFillPrice
) internal view returns (uint256) {
// fetch order flow fee from settings
uint256 orderFlowFee = SETTINGS.orderFlowFee();

// fetch current sUSD exchange rate for market
IPerpsV2MarketConsolidated market = IPerpsV2MarketConsolidated(_market);
(uint256 price,) = _sUSDRate(market);
uint256 price;

// if desiredFillPrice is specified then use it
if (_desiredFillPrice != 0) {
price = _desiredFillPrice;
} else {
// fetch current sUSD exchange rate for market
(price,) = _sUSDRate(IPerpsV2MarketConsolidated(_market));
}

// calculate notional value of order
uint256 notionalValue = _abs(_sizeDelta) * price;
Expand All @@ -661,6 +691,14 @@ contract Account is IAccount, Auth, OpsReady {
return notionalValue * orderFlowFee / SETTINGS.MAX_ORDER_FLOW_FEE();
}

function _calculateOrderFlowFee(address _market, int256 _sizeDelta)
internal
view
returns (uint256 orderflowFee)
{
orderflowFee = _calculateOrderFlowFee(_market, _sizeDelta, 0);
}

/*//////////////////////////////////////////////////////////////
ATOMIC ORDERS
//////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -720,7 +758,7 @@ contract Account is IAccount, Auth, OpsReady {
uint256 _desiredTimeDelta,
uint256 _desiredFillPrice
) internal {
_imposeOrderFlowFee(_market, _sizeDelta);
_imposeOrderFlowFee(_market, _sizeDelta, _desiredFillPrice);

IPerpsV2MarketConsolidated(_market).submitDelayedOrderWithTracking({
sizeDelta: _sizeDelta,
Expand Down Expand Up @@ -774,8 +812,7 @@ contract Account is IAccount, Auth, OpsReady {
int256 _sizeDelta,
uint256 _desiredFillPrice
) internal {
/// @custom:todo use _desiredFillPrice
_imposeOrderFlowFee(_market, _sizeDelta);
_imposeOrderFlowFee(_market, _sizeDelta, _desiredFillPrice);

IPerpsV2MarketConsolidated(_market)
.submitOffchainDelayedOrderWithTracking({
Expand All @@ -800,12 +837,12 @@ contract Account is IAccount, Auth, OpsReady {
address _market,
uint256 _desiredFillPrice
) internal {
/// @custom:todo use _desiredFillPrice
_imposeOrderFlowFee(
_market,
IPerpsV2MarketConsolidated(_market).positions({
account: address(this)
}).size
}).size,
_desiredFillPrice
);

// close position (i.e. reduce size to zero)
Expand Down Expand Up @@ -886,7 +923,7 @@ contract Account is IAccount, Auth, OpsReady {
execAddress: address(this),
execData: abi.encodeCall(
this.executeConditionalOrder, conditionalOrderId
),
),
moduleData: moduleData,
feeToken: ETH
});
Expand Down Expand Up @@ -1021,8 +1058,6 @@ contract Account is IAccount, Auth, OpsReady {
_amount: conditionalOrder.marginDelta
});

_imposeOrderFlowFee(address(market), conditionalOrder.sizeDelta);

_perpsV2SubmitOffchainDelayedOrder({
_market: address(market),
_sizeDelta: conditionalOrder.sizeDelta,
Expand Down
9 changes: 8 additions & 1 deletion src/Events.sol
Original file line number Diff line number Diff line change
Expand Up @@ -171,5 +171,12 @@ contract Events is IEvents {
emit DelegatedAccountRemoved({caller: caller, delegate: delegate});
}

/// @custom:todo add event for order flow fee imposed
/// @inheritdoc IEvents
function emitOrderFlowFeeImposed(uint256 amount)
external
override
onlyAccounts
{
emit OrderFlowFeeImposed({account: msg.sender, amount: amount});
}
}
5 changes: 3 additions & 2 deletions src/interfaces/IAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,12 @@ interface IAccount {
/// @notice get the expected order flow fee for a given market and size delta
/// @param _market: address of market
/// @param _sizeDelta: size delta of order
/// @return order flow fee expected for the given market and size delta
/// @return sUSDmarketRate sUSD exchange rate for market
/// @return orderFlowFee order flow fee expected for the given market and size delta
function getExpectedOrderFlowFee(address _market, int256 _sizeDelta)
external
view
returns (uint256);
returns (uint256 sUSDmarketRate, uint256 orderFlowFee);

/*//////////////////////////////////////////////////////////////
MUTATIVE
Expand Down
7 changes: 5 additions & 2 deletions src/interfaces/IEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ interface IEvents {
address indexed caller, address indexed delegate
);

/// @custom:todo add event function for order flow fee imposed
/// @custom:todo add event for order flow fee imposed
/// @notice emitted after order flow fee is imposed
/// @param amount: amount of the imposed order flow fee
function emitOrderFlowFeeImposed(uint256 amount) external;

event OrderFlowFeeImposed(address indexed account, uint256 amount);
}
Loading

0 comments on commit 3401779

Please sign in to comment.