Skip to content

Block same-timestamp Predict mint and redeem (DBU-472)#1085

Merged
sdelo merged 4 commits into
MystenLabs:mainfrom
sdelo:dpu-472-prevent-atomic-mint-oracle-redeem
Jun 25, 2026
Merged

Block same-timestamp Predict mint and redeem (DBU-472)#1085
sdelo merged 4 commits into
MystenLabs:mainfrom
sdelo:dpu-472-prevent-atomic-mint-oracle-redeem

Conversation

@sdelo

@sdelo sdelo commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Record each open position's on-chain open time (clock.timestamp_ms()) at mint, stored on the per-account Position row in predict_account.
  • Reject a live redeem (expiry_market::redeem_live -> redeem_live_internal) whose timestamp equals the position's open time (EMintRedeemSameTimestamp). A single transaction reads one Clock, so equal timestamps mean the mint and redeem are in the same atomic transaction.
  • This closes the atomic mint -> update oracle -> redeem arbitrage: a freshly minted order can no longer be round-tripped against a price the same transaction just pushed.

Key decisions

  • Ownership split: predict_account owns the open-time state and exposes a position_opened_at_ms getter; expiry_market::redeem_live_internal owns the redeem policy and does the != assertion (EMintRedeemSameTimestamp).
  • The open time is carried forward unchanged across partial-close replacements (not reset to the close time). The same-timestamp guard runs before any replacement is created, so a freshly minted order can never produce a replacement in its own mint tx; carry-forward only ensures a seasoned position stays closable in multiple steps within one later tx (an "orders always closable" liveness property), with no security difference vs. resetting.
  • The guard applies only to the live-priced close. The liquidated-tombstone and settled-redeem paths are not gated: liquidation requires the order to be underwater (a freshly minted order passes the at-entry barrier) and pays zero, and settled redeem is post-expiry, so neither can be the same-tx counterpart of a mint.
  • Comparison is != (the clock is monotonic non-decreasing, so this is equivalent to "the clock must have advanced").

Test plan

  • sui move build --path packages/predict --warnings-are-errors is green.
  • sui move test --path packages/predict --gas-limit 100000000000 is green (238 tests).
  • predict_account_tests: position_opened_at_ms round-trips the stored open time (including carry-forward across a partial-close replacement) and aborts EPositionNotFound for an unknown order.
  • Integration coverage in tests/flows/mint_redeem_guard_tests.move.disabled (same-tx redeem aborts EMintRedeemSameTimestamp; redeem succeeds once the clock advances). It is committed .disabled to match the rest of the root-dependent flow suite, which cannot run on the stable framework (no AccumulatorRoot test constructor); it should be re-enabled with them on the nightly framework.

Record each position's open time (clock.timestamp_ms()) at mint and reject
a live redeem whose timestamp equals it. A single transaction reads one
Clock, so this closes the atomic mint -> oracle-update -> redeem arbitrage
that would round-trip a freshly minted order against a price the same tx
pushed. The open time is carried forward across partial-close replacements,
so seasoned positions stay closable in multiple steps.

DPU-472

Co-authored-by: Cursor <cursoragent@cursor.com>
@sdelo sdelo marked this pull request as draft June 25, 2026 14:58
Move the open-time comparison into a factual predict_account assertion
(assert_not_opened_at, owning the open-time state) so the abort path is
covered by running unit tests instead of only the disabled flow test:
the same-timestamp abort (ESameTimestampRedeem), the missing-position
abort, and the pass case that reads back the stored open time.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sdelo

sdelo commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator Author

@claude review

@sdelo sdelo marked this pull request as ready for review June 25, 2026 15:34
@sdelo sdelo changed the title Block same-timestamp Predict mint and redeem (DPU-472) Block same-timestamp Predict mint and redeem (DBU-472) Jun 25, 2026
sdelo and others added 2 commits June 25, 2026 12:44
predict_account is position state only: expose a position_opened_at_ms
getter and move the same-timestamp redeem assertion back into
expiry_market::redeem_live_internal (EMintRedeemSameTimestamp), where the
redeem flow owns the policy. The getter keeps its missing-position unit
coverage; the abort path is covered by the disabled flow test.

Co-authored-by: Cursor <cursoragent@cursor.com>
…c-mint-oracle-redeem

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	packages/predict/sources/expiry_market.move
@sdelo sdelo merged commit 5744ec3 into MystenLabs:main Jun 25, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants