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

Add order expired event #1767

Merged
merged 1 commit into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions markets/perps-market/contracts/interfaces/IAsyncOrderModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ interface IAsyncOrderModule {
address sender
);

/**
* @notice Gets fired when a new order is committed while a previous one was expired.
* @param marketId Id of the market used for the trade.
* @param accountId Id of the account used for the trade.
* @param sizeDelta requested change in size of the order sent by the user.
* @param acceptablePrice maximum or minimum, depending on the sizeDelta direction, accepted price to settle the order, set by the user.
* @param settlementTime Time at which the order can be settled.
* @param trackingCode Optional code for integrator tracking purposes.
*/
event PreviousOrderExpired(
uint128 indexed marketId,
uint128 indexed accountId,
int128 sizeDelta,
uint256 acceptablePrice,
uint256 settlementTime,
bytes32 indexed trackingCode
);

/**
* @notice Commit an async order via this function
* @param commitment Order commitment data (see AsyncOrder.OrderCommitmentRequest struct).
Expand Down
21 changes: 20 additions & 1 deletion markets/perps-market/contracts/modules/AsyncOrderModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,26 @@ contract AsyncOrderModule is IAsyncOrderModule {
SettlementStrategy.Data storage strategy = PerpsMarketConfiguration
.loadValidSettlementStrategy(commitment.marketId, commitment.settlementStrategyId);

AsyncOrder.Data storage order = AsyncOrder.createValid(commitment, strategy);
// pull order from storage and check if it is valid (not pending unexpired order)
AsyncOrder.Data storage order = AsyncOrder.checkPendingOrder(commitment.accountId);

// if order (previous) sizeDelta is not zero and didn't revert while checking, it means the previous order expired
if (order.request.sizeDelta != 0) {
// @notice not including the expiration time since it requires the previous settlement strategy to be loaded and enabled, otherwise loading it will revert and will prevent new orders to be committed
emit PreviousOrderExpired(
order.request.marketId,
order.request.accountId,
order.request.sizeDelta,
order.request.acceptablePrice,
order.settlementTime,
order.request.trackingCode
);
}

// Replace previous (or empty) order with the commitment request
order.settlementTime = block.timestamp + strategy.settlementDelay;
order.request = commitment;

(, uint feesAccrued, , ) = order.validateRequest(
strategy,
PerpsPrice.getCurrentPrice(commitment.marketId)
Expand Down
16 changes: 1 addition & 15 deletions markets/perps-market/contracts/storage/AsyncOrder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -151,23 +151,9 @@ library AsyncOrder {
checkWithinSettlementWindow(order, strategy);
}

/**
* @dev Reverts if the order does not belongs to the market or not exists. Otherwise, returns the order.
* @dev non-existent order is considered an order with sizeDelta == 0.
*/
function createValid(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not useful anymore since we need the intermediate order to get the event information

OrderCommitmentRequest memory newRequest,
SettlementStrategy.Data storage strategy
) internal returns (Data storage order) {
order = checkPendingOrder(newRequest.accountId);

order.settlementTime = block.timestamp + strategy.settlementDelay;
order.request = newRequest;
}

/**
* @dev Reverts if there is a pending order.
* @dev A pending order is one that has a sizeDelta or isn't expired yet.
* @dev A pending order is one that has a sizeDelta and isn't expired yet.
*/
function checkPendingOrder(uint128 accountId) internal view returns (Data storage order) {
order = load(accountId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ describe('Commit Offchain Async Order test', () => {
startTime = await getTxTime(provider(), tx);
});

const restoreToSettle = snapshotCheckpoint(provider);

it('emit event', async () => {
await assertEvent(
tx,
Expand Down Expand Up @@ -270,6 +272,7 @@ describe('Commit Offchain Async Order test', () => {
});

describe('can settle order', () => {
before(restoreToSettle);
before('settle', async () => {
const settlementTime = startTime + DEFAULT_SETTLEMENT_STRATEGY.settlementDelay + 1;
await fastForwardTo(settlementTime, provider());
Expand Down Expand Up @@ -330,6 +333,74 @@ describe('Commit Offchain Async Order test', () => {
});
});
});

describe('when order expired', () => {
let expirationTime: number;

before('move after expiration', async () => {
expirationTime =
startTime +
DEFAULT_SETTLEMENT_STRATEGY.settlementDelay +
DEFAULT_SETTLEMENT_STRATEGY.settlementWindowDuration +
1;
await fastForwardTo(expirationTime, provider());
});

it('reverts if attempt to settle', async () => {
await assertRevert(
settleOrder({
systems,
keeper: keeper(),
accountId: 2,
feedId: DEFAULT_SETTLEMENT_STRATEGY.feedId,
settlementTime: expirationTime,
offChainPrice: 1000,
}),
'SettlementWindowExpired'
);
});

describe('can commit another order after expiration', () => {
let tx: ethers.ContractTransaction;
let secondOrderStartTime: number;
before('commit the order', async () => {
tx = await systems()
.PerpsMarket.connect(trader1())
.commitOrder({
marketId: ethMarketId,
accountId: 2,
sizeDelta: bn(1),
settlementStrategyId: 0,
acceptablePrice: bn(1050), // 5% slippage
referrer: ethers.constants.AddressZero,
trackingCode: ethers.constants.HashZero,
});
secondOrderStartTime = await getTxTime(provider(), tx);
});

it('emit the order commited event', async () => {
await assertEvent(
tx,
`OrderCommitted(${ethMarketId}, 2, ${DEFAULT_SETTLEMENT_STRATEGY.strategyType}, ${bn(
1
)}, ${bn(1050)}, ${secondOrderStartTime + 5}, ${secondOrderStartTime + 5 + 120}, "${
ethers.constants.HashZero
}", "${await trader1().getAddress()}")`,
systems().PerpsMarket
);
});

it('emit the order expired event', async () => {
await assertEvent(
tx,
`PreviousOrderExpired(${ethMarketId}, 2, ${bn(1)}, ${bn(1050)}, ${startTime + 5}, "${
ethers.constants.HashZero
}")`,
systems().PerpsMarket
);
});
});
});
});
}
});
Loading