# Jing v6: `reprice-or-swap` bypasses the new maker-margin gate **Severity: Medium — stale-price option / maker-versus-taker guard bypass; no direct theft demonstrated.** ## Finding The Sep. 19 maker-margin change does not enforce its 50-bps crossing buffer on either `reprice-or-swap` path. An existing maker can use an accepted but stale oracle update to move an order to within the buffer while remaining just outside the crossing test at that stale price. The non-cross branch records the new limit and returns without checking the buffer or charging the age-based taker rebate. A later settlement with a fresh price can then fill the order as a maker. The new entry gate therefore does not prevent the stale-print path it was added to close. The affected code is current `master` commit [`3dee04d`](https://github.com/Rapha-btc/jing-contracts-v3/commit/3dee04dffee34dfb38ccbd3800cd515a139e1f47), which includes the maker-margin change from [`0231ece`](https://github.com/Rapha-btc/jing-contracts-v3/commit/0231ece387c263f10284c720e6da3fad8c1c40a9). The audited source file at `3dee04d` was byte-for-byte compared with the public GitHub contents endpoint. ## Source path - `MAKER_MARGIN_BPS` is 50 bps, and `widen-down` subtracts that buffer for token-y bids ([source, lines 18–37](https://github.com/Rapha-btc/jing-contracts-v3/blob/3dee04dffee34dfb38ccbd3800cd515a139e1f47/contracts/markets-sbtc-stx-jing-v6.clar#L18-L37)). - The ordinary token-y deposit and `set-token-y-limit` paths reject a bid that would cross at the widened-down price ([deposit gate, lines 1368–1377](https://github.com/Rapha-btc/jing-contracts-v3/blob/3dee04dffee34dfb38ccbd3800cd515a139e1f47/contracts/markets-sbtc-stx-jing-v6.clar#L1368-L1377); [limit gate, lines 1770–1781](https://github.com/Rapha-btc/jing-contracts-v3/blob/3dee04dffee34dfb38ccbd3800cd515a139e1f47/contracts/markets-sbtc-stx-jing-v6.clar#L1770-L1781)). - `reprice-or-swap-token-y` writes the new order limit first, then tests `would-take-as-y` against the un-widened price. If that test is false, it returns a zero-result without calling `fresh-classification-price-aged` or charging a rebate ([source, lines 1909–1964](https://github.com/Rapha-btc/jing-contracts-v3/blob/3dee04dffee34dfb38ccbd3800cd515a139e1f47/contracts/markets-sbtc-stx-jing-v6.clar#L1909-L1964)). `reprice-or-swap-token-x` has the symmetric gap ([source, lines 1975–2030](https://github.com/Rapha-btc/jing-contracts-v3/blob/3dee04dffee34dfb38ccbd3800cd515a139e1f47/contracts/markets-sbtc-stx-jing-v6.clar#L1975-L2030)). - The new maker-margin commit says reprice-or-swap is not widened because it “already takes and pays.” That is true only when the supplied print already crosses the new limit; the non-cross branch above neither takes nor pays. ## Reproduction by source-level branch trace Preconditions: the caller has a nonzero token-y deposit in the current cycle, the token-x side is nonempty, and the submitted Lazer update is still within the contract’s 80-second freshness window. 1. Let the accepted update’s mid be `1,000,000`; choose a fixed token-y bid limit of `997,000` (30 bps below that print). At age 60 seconds, the new rebate schedule would charge 38 bps if the call entered its taker branch. 2. `reprice-or-swap-token-y` stores `997,000`, then evaluates `would-take-as-y(1,000,000, 997,000)`. The price is greater than the bid, so the test is false. The call returns successfully with no rebate. It never evaluates the margin-adjusted condition. 3. `set-token-y-limit` with the same price would evaluate `widen-down(1,000,000) = 995,000`; `would-take-as-y(995,000, 997,000)` is true, so its negated assertion returns `ERR_MUST_USE_SWAP`. 4. If a later fresh price reaches `996,900` while matching token-x liquidity is present, the stored `997,000` bid is now crossing and can be included in settlement. The reprice call did not pay the age-based taker rebate. The token-x ask path is symmetric: with a `1,000,000` print, an ask at `1,003,000` bypasses its margin in reprice-or-swap, while `set-token-x-limit` rejects it at `widen-up = 1,005,000`. This is not a claim that every such order fills or that the maker is guaranteed profit. It is a reproducible bypass of the new 50-bps guard and its stated stale-print protection; an opposing order and a later price move are required for a fill. It allows repricing at maker treatment in a band where the paired limit-setting route rejects the same order and where the change says taker handling is required. ## Fix and limits Apply the same widened crossing check to the non-crossing reprice path before storing the updated limit; preserve the current taker branch for orders that actually cross and pay. Apply the mirrored check to x and y. A regression case should cover the numerical sequence above for each side, alongside an actually crossing reprice that still charges the rebate. This is a source-only confirmation against the exact current source. I did not run a Clarinet or Stxer simulation, and I do not claim direct asset theft. The public submissions on this bounty were all created before the maker-margin commit; none evaluated this new path.