@exponent-labs/market-three-math 0.9.18 → 0.9.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -87,6 +87,25 @@ describe("withdraw liquidity math", () => {
87
87
  })
88
88
  })
89
89
 
90
+ it("accepts live tick zero as a valid lower boundary", () => {
91
+ const ticks = makeTicks([
92
+ makeTick({
93
+ apyBasePoints: 0,
94
+ impliedRate: 1.0,
95
+ principalPt: 7n,
96
+ principalSy: 11n,
97
+ principalShareSupply: PRECISE_NUMBER_DENOM,
98
+ liquidityGross: 1n,
99
+ }),
100
+ makeTick({ apyBasePoints: 2, impliedRate: 2 }),
101
+ ])
102
+
103
+ expect(getPtAndSyOnWithdrawLiquidity(marketEmissions, ticks, makePosition(), 1n)).toEqual({
104
+ totalPtOut: 3n,
105
+ totalSyOut: 5n,
106
+ })
107
+ })
108
+
90
109
  it("returns full principal when burned shares cover the supply", () => {
91
110
  const ticks = makeTicks([
92
111
  makeTick({
@@ -1,5 +1,7 @@
1
1
  import type { LpPositionCLMM, MarketThree, Ticks } from "@exponent-labs/exponent-fetcher"
2
2
 
3
+ import { isLiveTick, successorTickIdx } from "./tickTree"
4
+
3
5
  const PRECISE_NUMBER_DENOM = 1_000_000_000_000n
4
6
  const SENTINEL_TICK_INDEX = 0xffffffff
5
7
 
@@ -7,7 +9,7 @@ const invariantError = (message: string) => new Error(`WithdrawLiquidityInvarian
7
9
 
8
10
  const getTickByIndex = (ticks: Ticks, tickIdx: number) => {
9
11
  const tick = ticks.ticksTree[tickIdx - 1]
10
- if (!tick || tick.apyBasePoints <= 0) {
12
+ if (!isLiveTick(tick)) {
11
13
  throw invariantError(`missing tick at index ${tickIdx}`)
12
14
  }
13
15
 
@@ -16,22 +18,7 @@ const getTickByIndex = (ticks: Ticks, tickIdx: number) => {
16
18
 
17
19
  const getTickKey = (ticks: Ticks, tickIdx: number) => getTickByIndex(ticks, tickIdx).apyBasePoints
18
20
 
19
- const successorIdx = (ticks: Ticks, tickIdx: number): number | null => {
20
- const tickKey = getTickKey(ticks, tickIdx)
21
- let best: { tickIdx: number; key: number } | null = null
22
-
23
- for (let index = 0; index < ticks.ticksTree.length; index++) {
24
- const key = ticks.ticksTree[index].apyBasePoints
25
- if (key <= tickKey) {
26
- continue
27
- }
28
- if (!best || key < best.key) {
29
- best = { tickIdx: index + 1, key }
30
- }
31
- }
32
-
33
- return best?.tickIdx ?? null
34
- }
21
+ const successorIdx = (ticks: Ticks, tickIdx: number): number | null => successorTickIdx(ticks, tickIdx)
35
22
 
36
23
  const splitSuccessorForEpoch = (ticks: Ticks, leftIdx: number, rightIdx: number, splitEpoch: bigint): number | null => {
37
24
  let cursor = successorIdx(ticks, leftIdx)
package/src/ytTrades.ts CHANGED
@@ -408,10 +408,10 @@ export function simulateSellYt(marketState: MarketThreeState, args: SellYtSimula
408
408
  throw new Error(`Trade too small: merging ${ytIn} YT yields 0 SY`)
409
409
  }
410
410
 
411
- // On-chain uses pt_constraint + 2 to account for rounding, so we match that behavior
412
- const ptNeeded = ytIn + 2
411
+ // On-chain SellYt forwards yt_in directly as the exact-out PT target.
412
+ const ptNeeded = ytIn
413
413
 
414
- // Match on-chain SellYt exactly: TradePtExactOut with pt_target = yt_in + 2 and SY budget = syFromMerge
414
+ // Match on-chain SellYt exactly: TradePtExactOut with pt_target = yt_in and SY budget = syFromMerge
415
415
  const swapResult = simulateSwapExactOut(marketState, {
416
416
  direction: SwapDirection.SyToPt,
417
417
  amountOut: ptNeeded,