Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MarketUpdated event #1697

Merged
merged 10 commits into from
Jul 5, 2023
5 changes: 5 additions & 0 deletions markets/perps-market/cannonfile.test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ artifact = "PerpsMarketFactoryModule"
[contract.AsyncOrderModule]
artifact = "AsyncOrderModule"

[contract.AsyncOrderSettlementModule]
artifact = "AsyncOrderSettlementModule"

[contract.AtomicOrderModule]
artifact = "AtomicOrderModule"

Expand Down Expand Up @@ -73,6 +76,7 @@ contracts = [
"PerpsMarketModule",
"AtomicOrderModule",
"AsyncOrderModule",
"AsyncOrderSettlementModule",
"FeatureFlagModule",
"LimitOrderModule",
"LiquidationModule",
Expand All @@ -85,6 +89,7 @@ depends = [
"contract.PerpsMarketFactoryModule",
"contract.AtomicOrderModule",
"contract.AsyncOrderModule",
"contract.AsyncOrderSettlementModule",
"contract.PerpsAccountModule",
"contract.PerpsMarketModule",
"contract.FeatureFlagModule",
Expand Down
5 changes: 5 additions & 0 deletions markets/perps-market/cannonfile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ artifact = "PerpsMarketFactoryModule"
[contract.AsyncOrderModule]
artifact = "AsyncOrderModule"

[contract.AsyncOrderSettlementModule]
artifact = "AsyncOrderSettlementModule"

[contract.AtomicOrderModule]
artifact = "AtomicOrderModule"

Expand Down Expand Up @@ -73,6 +76,7 @@ contracts = [
"PerpsMarketModule",
"AtomicOrderModule",
"AsyncOrderModule",
"AsyncOrderSettlementModule",
"FeatureFlagModule",
"LimitOrderModule",
"LiquidationModule",
Expand All @@ -85,6 +89,7 @@ depends = [
"contract.PerpsMarketFactoryModule",
"contract.AtomicOrderModule",
"contract.AsyncOrderModule",
"contract.AsyncOrderSettlementModule",
"contract.PerpsAccountModule",
"contract.PerpsMarketModule",
"contract.FeatureFlagModule",
Expand Down
36 changes: 2 additions & 34 deletions markets/perps-market/contracts/interfaces/IAsyncOrderModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,6 @@ interface IAsyncOrderModule {
address sender
);

event OrderSettled(
uint128 indexed marketId,
uint128 indexed accountId,
uint256 fillPrice,
int256 accountPnlRealized,
int128 newSize,
uint256 collectedFees,
uint256 settelementReward,
bytes32 indexed trackingCode,
address settler
);

event OrderCanceled(
uint128 indexed marketId,
uint128 indexed accountId,
Expand All @@ -40,35 +28,15 @@ interface IAsyncOrderModule {
);

error OrderAlreadyCommitted(uint128 marketId, uint128 accountId);
error SettlementStrategyNotFound(SettlementStrategy.Type strategyType);
error OffchainLookup(
address sender,
string[] urls,
bytes callData,
bytes4 callbackFunction,
bytes extraData
);

function commitOrder(
AsyncOrder.OrderCommitmentRequest memory commitment
) external returns (AsyncOrder.Data memory retOrder, uint fees);

function cancelOrder(uint128 marketId, uint128 accountId) external;

// only used due to stack too deep during settlement
struct SettleOrderRuntime {
uint128 marketId;
uint128 accountId;
int128 newPositionSize;
int256 pnl;
uint256 pnlUint;
uint256 amountToDeposit;
uint256 settlementReward;
bytes32 trackingCode;
}

function getOrder(
uint128 marketId,
uint128 accountId
) external returns (AsyncOrder.Data memory);

function cancelOrder(uint128 marketId, uint128 accountId) external;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;
import {SettlementStrategy} from "../storage/SettlementStrategy.sol";

interface IAsyncOrderSettlementModule {
error SettlementStrategyNotFound(SettlementStrategy.Type strategyType);
error OffchainLookup(
address sender,
string[] urls,
bytes callData,
bytes4 callbackFunction,
bytes extraData
);

event OrderSettled(
uint128 indexed marketId,
uint128 indexed accountId,
uint256 fillPrice,
int256 accountPnlRealized,
int128 newSize,
uint256 collectedFees,
uint256 settelementReward,
bytes32 indexed trackingCode,
address settler
);

// only used due to stack too deep during settlement
struct SettleOrderRuntime {
uint128 marketId;
uint128 accountId;
int128 newPositionSize;
int256 pnl;
uint256 pnlUint;
uint256 amountToDeposit;
uint256 settlementReward;
bytes32 trackingCode;
}

function settle(uint128 marketId, uint128 accountId) external view;

function settlePythOrder(bytes calldata result, bytes calldata extraData) external payable;
}
13 changes: 13 additions & 0 deletions markets/perps-market/contracts/interfaces/IMarketEvents.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

interface IMarketEvents {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any others around that we can throw in here maybe? 👀

Copy link
Contributor Author

@0xjocke 0xjocke Jul 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most modules will have their events defined in their own interface. I had a look, didn't see any clear ones to move

event MarketUpdated(
uint128 marketId,
int256 skew,
uint256 size,
int256 sizeDelta,
int256 currentFundingRate,
int256 currentFundingVelocity
);
}
167 changes: 0 additions & 167 deletions markets/perps-market/contracts/modules/AsyncOrderModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ contract AsyncOrderModule is IAsyncOrderModule {
using SafeCastU256 for uint256;
using SafeCastI256 for int256;

int256 public constant PRECISION = 18;

function commitOrder(
AsyncOrder.OrderCommitmentRequest memory commitment
) external override returns (AsyncOrder.Data memory retOrder, uint fees) {
Expand Down Expand Up @@ -86,46 +84,6 @@ contract AsyncOrderModule is IAsyncOrderModule {
return (order, feesAccrued);
}

function settle(uint128 marketId, uint128 accountId) external view {
GlobalPerpsMarket.load().checkLiquidation(accountId);
(
AsyncOrder.Data storage order,
SettlementStrategy.Data storage settlementStrategy
) = _performOrderValidityChecks(marketId, accountId);

_settleOffchain(order, settlementStrategy);
}

function settlePythOrder(bytes calldata result, bytes calldata extraData) external payable {
(uint128 marketId, uint128 asyncOrderId) = abi.decode(extraData, (uint128, uint128));
(
AsyncOrder.Data storage order,
SettlementStrategy.Data storage settlementStrategy
) = _performOrderValidityChecks(marketId, asyncOrderId);

bytes32[] memory priceIds = new bytes32[](1);
priceIds[0] = settlementStrategy.feedId;

bytes[] memory updateData = new bytes[](1);
updateData[0] = result;

IPythVerifier.PriceFeed[] memory priceFeeds = IPythVerifier(
settlementStrategy.priceVerificationContract
).parsePriceFeedUpdates{value: msg.value}(
updateData,
priceIds,
order.settlementTime.to64(),
(order.settlementTime + settlementStrategy.priceWindowDuration).to64()
);

IPythVerifier.PriceFeed memory pythData = priceFeeds[0];
uint offchainPrice = _getScaledPrice(pythData.price.price, pythData.price.expo).toUint();

settlementStrategy.checkPriceDeviation(offchainPrice, PerpsPrice.getCurrentPrice(marketId));

_settleOrder(offchainPrice, order, settlementStrategy);
}

function getOrder(
uint128 marketId,
uint128 accountId
Expand All @@ -143,129 +101,4 @@ contract AsyncOrderModule is IAsyncOrderModule {
order.reset();
emit OrderCanceled(marketId, accountId, order.settlementTime, order.acceptablePrice);
}

function _settleOffchain(
AsyncOrder.Data storage asyncOrder,
SettlementStrategy.Data storage settlementStrategy
) private view returns (uint, int256, uint256) {
string[] memory urls = new string[](1);
urls[0] = settlementStrategy.url;

bytes4 selector;
if (settlementStrategy.strategyType == SettlementStrategy.Type.PYTH) {
selector = AsyncOrderModule.settlePythOrder.selector;
} else {
revert SettlementStrategyNotFound(settlementStrategy.strategyType);
}

// see EIP-3668: https://eips.ethereum.org/EIPS/eip-3668
revert OffchainLookup(
address(this),
urls,
abi.encodePacked(settlementStrategy.feedId, _getTimeInBytes(asyncOrder.settlementTime)),
selector,
abi.encode(asyncOrder.marketId, asyncOrder.accountId) // extraData that gets sent to callback for validation
);
}

function _settleOrder(
uint256 price,
AsyncOrder.Data storage asyncOrder,
SettlementStrategy.Data storage settlementStrategy
) private {
SettleOrderRuntime memory runtime;

runtime.accountId = asyncOrder.accountId;
runtime.marketId = asyncOrder.marketId;

// check if account is flagged
GlobalPerpsMarket.load().checkLiquidation(runtime.accountId);
(
Position.Data memory newPosition,
uint totalFees,
uint fillPrice,
Position.Data storage oldPosition
) = asyncOrder.validateOrder(settlementStrategy, price);

runtime.newPositionSize = newPosition.size;

PerpsMarketFactory.Data storage factory = PerpsMarketFactory.load();
PerpsAccount.Data storage perpsAccount = PerpsAccount.load(runtime.accountId);
// use fill price to calculate realized pnl
(runtime.pnl, , , ) = oldPosition.getPnl(fillPrice);

runtime.pnlUint = MathUtil.abs(runtime.pnl);
if (runtime.pnl > 0) {
factory.synthetix.withdrawMarketUsd(runtime.marketId, address(this), runtime.pnlUint);
perpsAccount.addCollateralAmount(SNX_USD_MARKET_ID, runtime.pnlUint);
} else if (runtime.pnl < 0) {
perpsAccount.deductFromAccount(runtime.pnlUint);
runtime.amountToDeposit = runtime.pnlUint;
// all gets deposited below with fees
}

// after pnl is realized, update position
PerpsMarket.loadValid(runtime.marketId).updatePositionData(runtime.accountId, newPosition);

perpsAccount.updatePositionMarkets(runtime.marketId, runtime.newPositionSize);
perpsAccount.deductFromAccount(totalFees);

runtime.settlementReward = settlementStrategy.settlementReward;
runtime.amountToDeposit += totalFees - runtime.settlementReward;
if (runtime.settlementReward > 0) {
// pay keeper
factory.usdToken.transfer(msg.sender, runtime.settlementReward);
}

if (runtime.amountToDeposit > 0) {
// deposit into market manager
factory.depositToMarketManager(runtime.marketId, runtime.amountToDeposit);
}

// exctracted from asyncOrder before order is reset
bytes32 trackingCode = asyncOrder.trackingCode;

asyncOrder.reset();

// emit event
emit OrderSettled(
runtime.marketId,
runtime.accountId,
fillPrice,
runtime.pnl,
runtime.newPositionSize,
totalFees,
runtime.settlementReward,
trackingCode,
msg.sender
);
}

function _performOrderValidityChecks(
uint128 marketId,
uint128 accountId
) private view returns (AsyncOrder.Data storage, SettlementStrategy.Data storage) {
AsyncOrder.Data storage order = PerpsMarket.loadValid(marketId).asyncOrders[accountId];
SettlementStrategy.Data storage settlementStrategy = PerpsMarketConfiguration
.load(marketId)
.settlementStrategies[order.settlementStrategyId];

order.checkValidity();
order.checkWithinSettlementWindow(settlementStrategy);

return (order, settlementStrategy);
}

function _getTimeInBytes(uint256 settlementTime) private pure returns (bytes8) {
bytes32 settlementTimeBytes = bytes32(abi.encode(settlementTime));

// get last 8 bytes
return bytes8(settlementTimeBytes << 192);
}

// borrowed from PythNode.sol
function _getScaledPrice(int64 price, int32 expo) private pure returns (int256) {
int256 factor = PRECISION + expo;
return factor > 0 ? price.upscale(factor.toUint()) : price.downscale((-factor).toUint());
}
}
Loading
Loading