@mysten/deepbook-v3 2.6.4 → 2.8.0

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.
Files changed (250) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/PREDICT.md +389 -61
  3. package/dist/contracts/account/account.d.mts +23 -23
  4. package/dist/contracts/account/account.d.mts.map +1 -1
  5. package/dist/contracts/account/account_registry.d.mts +13 -13
  6. package/dist/contracts/deepbook/account.d.mts +18 -18
  7. package/dist/contracts/deepbook/balances.d.mts +4 -4
  8. package/dist/contracts/deepbook/deep_price.d.mts +3 -3
  9. package/dist/contracts/deepbook/order.d.mts +12 -12
  10. package/dist/contracts/deepbook_predict/admin.d.mts +4 -4
  11. package/dist/contracts/deepbook_predict/builder_code.d.mts +11 -11
  12. package/dist/contracts/deepbook_predict/builder_code_events.d.mts +10 -10
  13. package/dist/contracts/deepbook_predict/config_events.d.mts +93 -68
  14. package/dist/contracts/deepbook_predict/config_events.d.mts.map +1 -1
  15. package/dist/contracts/deepbook_predict/config_events.mjs +45 -1
  16. package/dist/contracts/deepbook_predict/config_events.mjs.map +1 -1
  17. package/dist/contracts/deepbook_predict/deps/std/type_name.mjs +17 -0
  18. package/dist/contracts/deepbook_predict/deps/std/type_name.mjs.map +1 -0
  19. package/dist/contracts/deepbook_predict/expiry_market.d.mts +652 -235
  20. package/dist/contracts/deepbook_predict/expiry_market.d.mts.map +1 -1
  21. package/dist/contracts/deepbook_predict/expiry_market.mjs +652 -186
  22. package/dist/contracts/deepbook_predict/expiry_market.mjs.map +1 -1
  23. package/dist/contracts/deepbook_predict/lp_book.mjs +1 -1
  24. package/dist/contracts/deepbook_predict/market_lifecycle_cap.d.mts +5 -5
  25. package/dist/contracts/deepbook_predict/market_manager.d.mts +27 -27
  26. package/dist/contracts/deepbook_predict/order_events.d.mts +50 -50
  27. package/dist/contracts/deepbook_predict/order_events.d.mts.map +1 -1
  28. package/dist/contracts/deepbook_predict/order_events.mjs +3 -1
  29. package/dist/contracts/deepbook_predict/order_events.mjs.map +1 -1
  30. package/dist/contracts/deepbook_predict/pause_cap.d.mts +5 -5
  31. package/dist/contracts/deepbook_predict/plp.d.mts +184 -82
  32. package/dist/contracts/deepbook_predict/plp.d.mts.map +1 -1
  33. package/dist/contracts/deepbook_predict/plp.mjs +126 -7
  34. package/dist/contracts/deepbook_predict/plp.mjs.map +1 -1
  35. package/dist/contracts/deepbook_predict/pool_accounting.mjs +1 -1
  36. package/dist/contracts/deepbook_predict/pool_valuation_cap.d.mts +5 -5
  37. package/dist/contracts/deepbook_predict/predict_account.d.mts +14 -14
  38. package/dist/contracts/deepbook_predict/pricing.d.mts +91 -58
  39. package/dist/contracts/deepbook_predict/pricing.d.mts.map +1 -1
  40. package/dist/contracts/deepbook_predict/pricing.mjs +30 -3
  41. package/dist/contracts/deepbook_predict/pricing.mjs.map +1 -1
  42. package/dist/contracts/deepbook_predict/pricing_config.mjs.map +1 -1
  43. package/dist/contracts/deepbook_predict/protocol_config.d.mts +400 -92
  44. package/dist/contracts/deepbook_predict/protocol_config.d.mts.map +1 -1
  45. package/dist/contracts/deepbook_predict/protocol_config.mjs +451 -61
  46. package/dist/contracts/deepbook_predict/protocol_config.mjs.map +1 -1
  47. package/dist/contracts/deepbook_predict/range_codec.d.mts +2 -2
  48. package/dist/contracts/deepbook_predict/registry.d.mts +28 -28
  49. package/dist/contracts/deepbook_predict/strike_exposure.mjs +1 -1
  50. package/dist/contracts/deepbook_predict/strike_payout_tree.mjs +17 -12
  51. package/dist/contracts/deepbook_predict/strike_payout_tree.mjs.map +1 -1
  52. package/dist/contracts/deepbook_predict/vault_events.d.mts +154 -106
  53. package/dist/contracts/deepbook_predict/vault_events.d.mts.map +1 -1
  54. package/dist/contracts/deepbook_predict/vault_events.mjs +57 -1
  55. package/dist/contracts/deepbook_predict/vault_events.mjs.map +1 -1
  56. package/dist/contracts/deepbook_predict_math/config-arguments.d.mts +10 -0
  57. package/dist/contracts/deepbook_predict_math/config-arguments.d.mts.map +1 -0
  58. package/dist/contracts/deepbook_predict_math/lazer_price.d.mts +106 -0
  59. package/dist/contracts/deepbook_predict_math/lazer_price.d.mts.map +1 -0
  60. package/dist/contracts/deepbook_predict_math/lazer_price.mjs +125 -0
  61. package/dist/contracts/deepbook_predict_math/lazer_price.mjs.map +1 -0
  62. package/dist/contracts/deepbook_predict_math/math.d.mts +284 -0
  63. package/dist/contracts/deepbook_predict_math/math.d.mts.map +1 -0
  64. package/dist/contracts/deepbook_predict_math/math.mjs +364 -0
  65. package/dist/contracts/deepbook_predict_math/math.mjs.map +1 -0
  66. package/dist/contracts/deepbook_predict_orders/config-arguments.d.mts +13 -0
  67. package/dist/contracts/deepbook_predict_orders/config-arguments.d.mts.map +1 -0
  68. package/dist/contracts/deepbook_predict_orders/delayed_execution_config.d.mts +215 -0
  69. package/dist/contracts/deepbook_predict_orders/delayed_execution_config.d.mts.map +1 -0
  70. package/dist/contracts/deepbook_predict_orders/delayed_execution_config.mjs +215 -0
  71. package/dist/contracts/deepbook_predict_orders/delayed_execution_config.mjs.map +1 -0
  72. package/dist/contracts/deepbook_predict_orders/deps/deepbook_predict/expiry_market.mjs +87 -0
  73. package/dist/contracts/deepbook_predict_orders/deps/deepbook_predict/expiry_market.mjs.map +1 -0
  74. package/dist/contracts/deepbook_predict_orders/deps/deepbook_predict/pricing.mjs +52 -0
  75. package/dist/contracts/deepbook_predict_orders/deps/deepbook_predict/pricing.mjs.map +1 -0
  76. package/dist/contracts/deepbook_predict_orders/deps/fixed_math/i64.mjs +25 -0
  77. package/dist/contracts/deepbook_predict_orders/deps/fixed_math/i64.mjs.map +1 -0
  78. package/dist/contracts/deepbook_predict_orders/deps/sui/balance.mjs +21 -0
  79. package/dist/contracts/deepbook_predict_orders/deps/sui/balance.mjs.map +1 -0
  80. package/dist/contracts/deepbook_predict_orders/deps/sui/table.mjs +39 -0
  81. package/dist/contracts/deepbook_predict_orders/deps/sui/table.mjs.map +1 -0
  82. package/dist/contracts/deepbook_predict_orders/desk.d.mts +187 -0
  83. package/dist/contracts/deepbook_predict_orders/desk.d.mts.map +1 -0
  84. package/dist/contracts/deepbook_predict_orders/desk.mjs +261 -0
  85. package/dist/contracts/deepbook_predict_orders/desk.mjs.map +1 -0
  86. package/dist/contracts/deepbook_predict_orders/order_queue.d.mts +1042 -0
  87. package/dist/contracts/deepbook_predict_orders/order_queue.d.mts.map +1 -0
  88. package/dist/contracts/deepbook_predict_orders/order_queue.mjs +849 -0
  89. package/dist/contracts/deepbook_predict_orders/order_queue.mjs.map +1 -0
  90. package/dist/contracts/deepbook_predict_orders/queue.d.mts +683 -0
  91. package/dist/contracts/deepbook_predict_orders/queue.d.mts.map +1 -0
  92. package/dist/contracts/deepbook_predict_orders/queue.mjs +912 -0
  93. package/dist/contracts/deepbook_predict_orders/queue.mjs.map +1 -0
  94. package/dist/contracts/deepbook_predict_orders/queue_events.d.mts +195 -0
  95. package/dist/contracts/deepbook_predict_orders/queue_events.d.mts.map +1 -0
  96. package/dist/contracts/deepbook_predict_orders/queue_events.mjs +187 -0
  97. package/dist/contracts/deepbook_predict_orders/queue_events.mjs.map +1 -0
  98. package/dist/contracts/deepbook_sessions/session_config.d.mts +8 -8
  99. package/dist/contracts/deepbook_sessions/sessions.d.mts +163 -28
  100. package/dist/contracts/deepbook_sessions/sessions.d.mts.map +1 -1
  101. package/dist/contracts/deepbook_sessions/sessions.mjs +234 -5
  102. package/dist/contracts/deepbook_sessions/sessions.mjs.map +1 -1
  103. package/dist/deployments/mainnet.mjs +7 -4
  104. package/dist/deployments/mainnet.mjs.map +1 -1
  105. package/dist/deployments/testnet.mjs +7 -4
  106. package/dist/deployments/testnet.mjs.map +1 -1
  107. package/dist/predict/bindings/expiry-market.d.mts +77 -0
  108. package/dist/predict/bindings/expiry-market.d.mts.map +1 -0
  109. package/dist/predict/bindings/expiry-market.mjs +139 -0
  110. package/dist/predict/bindings/expiry-market.mjs.map +1 -0
  111. package/dist/predict/bindings/protocol-config.d.mts +39 -0
  112. package/dist/predict/bindings/protocol-config.d.mts.map +1 -0
  113. package/dist/predict/bindings/protocol-config.mjs +90 -0
  114. package/dist/predict/bindings/protocol-config.mjs.map +1 -0
  115. package/dist/predict/bindings/rename.mjs +9 -0
  116. package/dist/predict/bindings/rename.mjs.map +1 -0
  117. package/dist/predict/client.d.mts +595 -16
  118. package/dist/predict/client.d.mts.map +1 -1
  119. package/dist/predict/client.mjs +986 -10
  120. package/dist/predict/client.mjs.map +1 -1
  121. package/dist/predict/config/generated.d.mts +22 -1
  122. package/dist/predict/config/generated.d.mts.map +1 -1
  123. package/dist/predict/config/generated.mjs +30 -1
  124. package/dist/predict/config/generated.mjs.map +1 -1
  125. package/dist/predict/config/mainnet.mjs +11 -3
  126. package/dist/predict/config/mainnet.mjs.map +1 -1
  127. package/dist/predict/config/testnet.mjs +11 -3
  128. package/dist/predict/config/testnet.mjs.map +1 -1
  129. package/dist/predict/config/types.d.mts +50 -1
  130. package/dist/predict/config/types.d.mts.map +1 -1
  131. package/dist/predict/cost.d.mts +38 -11
  132. package/dist/predict/cost.d.mts.map +1 -1
  133. package/dist/predict/cost.mjs +37 -11
  134. package/dist/predict/cost.mjs.map +1 -1
  135. package/dist/predict/decode.d.mts +251 -1
  136. package/dist/predict/decode.d.mts.map +1 -1
  137. package/dist/predict/decode.mjs +342 -4
  138. package/dist/predict/decode.mjs.map +1 -1
  139. package/dist/predict/errors.d.mts +69 -6
  140. package/dist/predict/errors.d.mts.map +1 -1
  141. package/dist/predict/errors.mjs +277 -4
  142. package/dist/predict/errors.mjs.map +1 -1
  143. package/dist/predict/index.d.mts +21 -10
  144. package/dist/predict/index.mjs +24 -12
  145. package/dist/predict/queue-id.d.mts +13 -0
  146. package/dist/predict/queue-id.d.mts.map +1 -0
  147. package/dist/predict/queue-id.mjs +19 -0
  148. package/dist/predict/queue-id.mjs.map +1 -0
  149. package/dist/predict/queue.d.mts +505 -0
  150. package/dist/predict/queue.d.mts.map +1 -0
  151. package/dist/predict/queue.mjs +774 -0
  152. package/dist/predict/queue.mjs.map +1 -0
  153. package/dist/predict/reads/balances.mjs +1 -1
  154. package/dist/predict/reads/markets.mjs +46 -5
  155. package/dist/predict/reads/markets.mjs.map +1 -1
  156. package/dist/predict/reads/pool.mjs +1 -1
  157. package/dist/predict/reads/pricing.d.mts +1 -1
  158. package/dist/predict/reads/queue.d.mts +105 -0
  159. package/dist/predict/reads/queue.d.mts.map +1 -0
  160. package/dist/predict/reads/queue.mjs +445 -0
  161. package/dist/predict/reads/queue.mjs.map +1 -0
  162. package/dist/predict/ticks.d.mts +9 -1
  163. package/dist/predict/ticks.d.mts.map +1 -1
  164. package/dist/predict/ticks.mjs +18 -1
  165. package/dist/predict/ticks.mjs.map +1 -1
  166. package/dist/predict/tx/authed.mjs +1 -1
  167. package/dist/predict/tx/common.d.mts.map +1 -1
  168. package/dist/predict/tx/common.mjs +12 -1
  169. package/dist/predict/tx/common.mjs.map +1 -1
  170. package/dist/predict/tx/queue.d.mts +220 -0
  171. package/dist/predict/tx/queue.d.mts.map +1 -0
  172. package/dist/predict/tx/queue.mjs +399 -0
  173. package/dist/predict/tx/queue.mjs.map +1 -0
  174. package/dist/predict/tx/trade.d.mts.map +1 -1
  175. package/dist/predict/tx/trade.mjs +61 -43
  176. package/dist/predict/tx/trade.mjs.map +1 -1
  177. package/dist/sessions.d.mts +90 -2
  178. package/dist/sessions.d.mts.map +1 -1
  179. package/dist/sessions.mjs +107 -2
  180. package/dist/sessions.mjs.map +1 -1
  181. package/dist/transactions/balanceManager.d.mts +12 -12
  182. package/dist/transactions/balanceManager.d.mts.map +1 -1
  183. package/dist/transactions/deepbook.d.mts +20 -20
  184. package/dist/transactions/deepbook.d.mts.map +1 -1
  185. package/dist/transactions/deepbookAdmin.d.mts +4 -4
  186. package/dist/transactions/deepbookAdmin.d.mts.map +1 -1
  187. package/dist/transactions/marginAdmin.d.mts +7 -7
  188. package/dist/transactions/marginAdmin.d.mts.map +1 -1
  189. package/dist/transactions/marginLiquidations.d.mts +3 -3
  190. package/dist/transactions/marginMaintainer.d.mts +5 -5
  191. package/dist/transactions/marginMaintainer.d.mts.map +1 -1
  192. package/dist/transactions/marginManager.d.mts +32 -32
  193. package/dist/transactions/marginManager.d.mts.map +1 -1
  194. package/dist/transactions/marginPool.d.mts +18 -18
  195. package/dist/transactions/marginRegistry.d.mts +16 -16
  196. package/dist/transactions/marginTPSL.d.mts +10 -10
  197. package/dist/transactions/marginTPSL.d.mts.map +1 -1
  198. package/dist/transactions/poolProxy.d.mts +8 -8
  199. package/dist/transactions/poolProxy.d.mts.map +1 -1
  200. package/package.json +4 -4
  201. package/src/contracts/deepbook_predict/config_events.ts +39 -0
  202. package/src/contracts/deepbook_predict/deps/std/type_name.ts +24 -0
  203. package/src/contracts/deepbook_predict/expiry_market.ts +960 -169
  204. package/src/contracts/deepbook_predict/order_events.ts +3 -1
  205. package/src/contracts/deepbook_predict/plp.ts +150 -9
  206. package/src/contracts/deepbook_predict/pricing.ts +46 -7
  207. package/src/contracts/deepbook_predict/pricing_config.ts +3 -1
  208. package/src/contracts/deepbook_predict/protocol_config.ts +648 -33
  209. package/src/contracts/deepbook_predict/strike_payout_tree.ts +25 -21
  210. package/src/contracts/deepbook_predict/vault_events.ts +65 -0
  211. package/src/contracts/deepbook_predict_math/config-arguments.ts +6 -0
  212. package/src/contracts/deepbook_predict_math/lazer_price.ts +196 -0
  213. package/src/contracts/deepbook_predict_math/math.ts +553 -0
  214. package/src/contracts/deepbook_predict_orders/config-arguments.ts +11 -0
  215. package/src/contracts/deepbook_predict_orders/delayed_execution_config.ts +430 -0
  216. package/src/contracts/deepbook_predict_orders/deps/deepbook_predict/expiry_market.ts +108 -0
  217. package/src/contracts/deepbook_predict_orders/deps/deepbook_predict/pricing.ts +58 -0
  218. package/src/contracts/deepbook_predict_orders/deps/fixed_math/i64.ts +21 -0
  219. package/src/contracts/deepbook_predict_orders/deps/sui/balance.ts +19 -0
  220. package/src/contracts/deepbook_predict_orders/deps/sui/table.ts +37 -0
  221. package/src/contracts/deepbook_predict_orders/desk.ts +419 -0
  222. package/src/contracts/deepbook_predict_orders/order_flow.ts +19 -0
  223. package/src/contracts/deepbook_predict_orders/order_queue.ts +1670 -0
  224. package/src/contracts/deepbook_predict_orders/queue.ts +1394 -0
  225. package/src/contracts/deepbook_predict_orders/queue_events.ts +189 -0
  226. package/src/contracts/deepbook_sessions/sessions.ts +355 -4
  227. package/src/deployments/mainnet.ts +7 -2
  228. package/src/deployments/testnet.ts +7 -2
  229. package/src/deployments/types.ts +14 -1
  230. package/src/predict/bindings/expiry-market.ts +127 -0
  231. package/src/predict/bindings/protocol-config.ts +59 -0
  232. package/src/predict/bindings/rename.ts +14 -0
  233. package/src/predict/client.ts +2076 -13
  234. package/src/predict/config/generated.ts +50 -0
  235. package/src/predict/config/mainnet.ts +16 -2
  236. package/src/predict/config/testnet.ts +16 -2
  237. package/src/predict/config/types.ts +51 -1
  238. package/src/predict/cost.ts +62 -14
  239. package/src/predict/decode.ts +691 -2
  240. package/src/predict/errors.ts +391 -14
  241. package/src/predict/index.ts +87 -6
  242. package/src/predict/queue-id.ts +22 -0
  243. package/src/predict/queue.ts +1231 -0
  244. package/src/predict/reads/markets.ts +30 -0
  245. package/src/predict/reads/queue.ts +606 -0
  246. package/src/predict/ticks.ts +35 -0
  247. package/src/predict/tx/common.ts +25 -1
  248. package/src/predict/tx/queue.ts +525 -0
  249. package/src/predict/tx/trade.ts +80 -49
  250. package/src/sessions.ts +206 -1
@@ -3,6 +3,7 @@
3
3
  import { Transaction } from '@mysten/sui/transactions';
4
4
  import { type GeneratedConfig } from '../config/generated.js';
5
5
  import { type UnderlyingConfig } from '../config/index.js';
6
+ import { type FeePolicy } from '../cost.js';
6
7
  import { PredictMoveError } from '../errors.js';
7
8
  import { POS_INF_TICK } from '../ticks.js';
8
9
  import { loadLivePricer, type MarketFeeds } from '../tx/trade.js';
@@ -233,3 +234,32 @@ export async function currentNav(
233
234
  // current_nav is the last command; load_live_pricer precedes it.
234
235
  return parseU64LE(cmds[cmds.length - 1][0]);
235
236
  }
237
+
238
+ /**
239
+ * The fee and exposure policy a market snapshotted at creation, from its `ExpiryMarket` object:
240
+ * `strike_exposure.config` and `strike_exposure.inventory_impact_scale`. It is the policy the
241
+ * market charges for its whole life, whatever the protocol template says now, and the `cost`
242
+ * functions take it as `fees`. One object read, no simulate. The struct layout is fixed across
243
+ * package upgrades, so the v1 binding decodes every version's markets.
244
+ */
245
+ export async function marketFeePolicy(client: ReadClient, marketId: string): Promise<FeePolicy> {
246
+ const { object } = await client.core.getObject({
247
+ objectId: marketId,
248
+ include: { content: true },
249
+ });
250
+ if (!object.content) throw new Error(`ExpiryMarket ${marketId} returned no content`);
251
+ const { config, inventory_impact_scale } = expiryMarket.ExpiryMarket.parse(
252
+ object.content,
253
+ ).strike_exposure;
254
+ return {
255
+ baseFee: BigInt(config.base_fee),
256
+ minFee: BigInt(config.min_fee),
257
+ expiryFeeWindowMs: BigInt(config.expiry_fee_window_ms),
258
+ expiryFeeMaxMultiplier: BigInt(config.expiry_fee_max_multiplier),
259
+ minEntryProbability: BigInt(config.min_entry_probability),
260
+ maxEntryProbability: BigInt(config.max_entry_probability),
261
+ inventoryImpactMaxRate: BigInt(config.inventory_impact_max_rate),
262
+ inventoryImpactScale: BigInt(inventory_impact_scale),
263
+ backingBufferLambda: BigInt(config.backing_buffer_lambda),
264
+ };
265
+ }
@@ -0,0 +1,606 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // Delayed execution (DBU-885): chain reads for the order queue. A market's queue is a
4
+ // `MarketQueue` in the order-flow companion (`deepbook_predict_orders`), at an ID derived from the
5
+ // companion's `OrderDesk` and the market, so these reads take `OrdersGeneratedConfig` and the
6
+ // facade gates them on the config recording the companion. The market's cash figures stay
7
+ // Predict reads. A market whose queue doesn't exist yet can't be read: the facade checks that
8
+ // first.
9
+ import type { ClientWithCoreApi } from '@mysten/sui/client';
10
+ import { bcs } from '@mysten/sui/bcs';
11
+ import { Transaction, type TransactionResult } from '@mysten/sui/transactions';
12
+ import { normalizeSuiAddress } from '@mysten/sui/utils';
13
+ import type { GeneratedConfig, OrdersGeneratedConfig } from '../config/generated.js';
14
+ import {
15
+ DELAYED_EXECUTION_VERSION,
16
+ PREDICT_PACKAGE_VERSION,
17
+ policyFromBcs,
18
+ type DelayedExecutionPolicy,
19
+ type QueueHeads,
20
+ type QueuedOrder,
21
+ } from '../queue.js';
22
+ import { deriveAccountIdFrom, deriveAccountWrapperIdFrom } from '../tx/common.js';
23
+ import { queueIdOf, type QueueTarget } from '../tx/queue.js';
24
+ import { loadLivePricer, type MarketFeeds } from '../tx/trade.js';
25
+ import { accountMoveCalls as account } from '../../account.js';
26
+ import * as expiryMarket from '../../contracts/deepbook_predict/expiry_market.js';
27
+ import * as protocolConfig from '../../contracts/deepbook_predict/protocol_config.js';
28
+ import { DelayedExecutionPolicy as DelayedExecutionPolicyBcs } from '../../contracts/deepbook_predict_orders/delayed_execution_config.js';
29
+ import * as desk from '../../contracts/deepbook_predict_orders/desk.js';
30
+ import { OrderView as OrderViewBcs } from '../../contracts/deepbook_predict_orders/order_queue.js';
31
+ import * as queue from '../../contracts/deepbook_predict_orders/queue.js';
32
+ import { inspectReturns, type ReadClient } from './inspect.js';
33
+ import { parseOptionalU64, parseU64LE } from './parse.js';
34
+
35
+ const parseBool = (bytes: Uint8Array): boolean => (bytes[0] ?? 0) !== 0;
36
+ const OptionalOrderView = bcs.option(OrderViewBcs);
37
+
38
+ /** The companion's `order_flow::OrderFlow` witness type, which Predict's allowlist names. */
39
+ export function orderFlowWitnessType(
40
+ config: Pick<OrdersGeneratedConfig, 'predictOrdersPackageIdV1'>,
41
+ ) {
42
+ return `${config.predictOrdersPackageIdV1}::order_flow::OrderFlow`;
43
+ }
44
+
45
+ /** Everything the queued-order preflight and the app's queue view read, from one simulate. */
46
+ export interface MarketQueueState {
47
+ marketId: string;
48
+ /** The market's `MarketQueue`. */
49
+ queueId: string;
50
+ expiryMs: bigint;
51
+ mintPaused: boolean;
52
+ cashBalance: bigint;
53
+ requiredCash: bigint;
54
+ /**
55
+ * Cash above required cash, 0 when below: the largest cash need a new queued mint may have
56
+ * now (Predict's admission refuses a larger one).
57
+ */
58
+ spareCash: bigint;
59
+ /** Summed cash need of the admitted orders; `rebalance_expiry_cash` funds it. */
60
+ waitingCashNeed: bigint;
61
+ /** 1e9-scaled. The mint cash-need formulas take it. */
62
+ minEntryProbability: bigint;
63
+ /** 1e9-scaled λ. The sell cash-need formula takes it. */
64
+ backingBufferLambda: bigint;
65
+ /** Sizes a filler's resolve batch. */
66
+ payoutTreeNodeCount: bigint;
67
+ /** `queue_stuck`: enqueue would refuse as stuck right now ("pricing delayed"). */
68
+ stuck: boolean;
69
+ cohorts: {
70
+ count: bigint;
71
+ oldestUncommittedTauMs: bigint | null;
72
+ /** The cohort the stuck gate's first rule watches. */
73
+ oldestUncommittedTauAboveCommittedMs: bigint | null;
74
+ };
75
+ heads: QueueHeads;
76
+ /** Unfinished mints and sells, against the policy capacities. */
77
+ pending: { mints: bigint; sells: bigint };
78
+ /**
79
+ * The settlement payout walk (`settle_step`): it resumes at `cursor`, and `completed` is set
80
+ * by the call that emits `MarketPayoutsCompleted`.
81
+ */
82
+ payout: { cursor: bigint; nextId: bigint; completed: boolean };
83
+ /** The companion's `OrderDesk`: the policy every queue runs under, and its version floor. */
84
+ desk: {
85
+ id: string;
86
+ policy: DelayedExecutionPolicy;
87
+ versionWatermark: bigint;
88
+ };
89
+ protocol: {
90
+ noTradeWindowMs: bigint;
91
+ /** The admin-set mint fee subsidy rate, 1e9-scaled (20% when unset). */
92
+ feeIncentiveSubsidyRate: bigint;
93
+ tradingPaused: boolean;
94
+ frozen: boolean;
95
+ /** Predict's version floor. Enqueue needs it at `DELAYED_EXECUTION_VERSION`. */
96
+ versionWatermark: bigint;
97
+ /**
98
+ * Whether Predict allowlists this companion's `OrderFlow` witness
99
+ * (`protocol_config::set_order_flow`). Predict's admission, commit and fill refuse it until
100
+ * then (`EOrderFlowNotAllowed`).
101
+ */
102
+ orderFlowEnabled: boolean;
103
+ };
104
+ /** Present when an owner was given. */
105
+ account: {
106
+ accountId: string;
107
+ /**
108
+ * Unfinished orders the account holds in this market, against `per_account_cap`. Stale
109
+ * after expiry: the settlement drain refunds without lowering it.
110
+ */
111
+ waitingOrders: bigint;
112
+ /** `account::balance<USDC>`, unsettled accumulator funds included. */
113
+ availableRaw: bigint;
114
+ } | null;
115
+ /** One entry per requested record ID: the record, or `null` when missing or deleted. */
116
+ records: (QueuedOrder | null)[];
117
+ }
118
+
119
+ /**
120
+ * Read a market's queue state in one simulate: Predict's cash figures and gates, the queue's
121
+ * stuck gate, cohorts, heads and counters, and the desk's policy. With `owner`, also the
122
+ * account's waiting-order count and USDC balance (the owner must have an account: loading a
123
+ * missing wrapper aborts the read). With `recordIds`, also those records. The queue must exist.
124
+ */
125
+ export async function marketQueueState(
126
+ client: ReadClient,
127
+ config: OrdersGeneratedConfig,
128
+ marketId: string,
129
+ opts: {
130
+ owner?: string;
131
+ quoteCoinType?: string;
132
+ recordIds?: readonly bigint[];
133
+ queueId?: string;
134
+ } = {},
135
+ ): Promise<MarketQueueState> {
136
+ const queueId = queueIdOf(config, { expiryMarketId: marketId, queueId: opts.queueId });
137
+ const tx = new Transaction();
138
+ let next = 0;
139
+ const results: TransactionResult[] = [];
140
+ const add = (command: (tx: Transaction) => TransactionResult): number => {
141
+ results.push(tx.add(command));
142
+ return next++;
143
+ };
144
+ const market = { market: marketId };
145
+ const onQueue = { queue: queueId };
146
+ const i = {
147
+ expiry: add(expiryMarket.expiry({ config, arguments: market })),
148
+ mintPaused: add(expiryMarket.mintPaused({ config, arguments: market })),
149
+ cashBalance: add(expiryMarket.cashBalance({ config, arguments: market })),
150
+ requiredCash: add(expiryMarket.requiredCash({ config, arguments: market })),
151
+ backingBufferLambda: add(expiryMarket.backingBufferLambda({ config, arguments: market })),
152
+ orderFlow: add(expiryMarket.orderFlowState({ config, arguments: market })),
153
+ stuck: add(queue.queueStuck({ config, arguments: onQueue })),
154
+ cohorts: add(queue.waitingCohorts({ config, arguments: onQueue })),
155
+ heads: add(queue.queueHeads({ config, arguments: onQueue })),
156
+ pending: add(queue.pendingCounts({ config, arguments: onQueue })),
157
+ payout: add(queue.payoutProgress({ config, arguments: onQueue })),
158
+ policy: add(desk.policy({ config })),
159
+ deskWatermark: add(desk.versionWatermark({ config })),
160
+ noTradeWindowMs: add(protocolConfig.noTradeWindowMs({ config })),
161
+ subsidyRate: add(protocolConfig.feeIncentiveSubsidyRate({ config })),
162
+ tradingPaused: add(protocolConfig.tradingPaused({ config })),
163
+ frozen: add(protocolConfig.frozen({ config })),
164
+ watermark: add(protocolConfig.versionWatermark({ config })),
165
+ orderFlowEnabled: add(
166
+ protocolConfig.isOrderFlow({ config, typeArguments: [orderFlowWitnessType(config)] }),
167
+ ),
168
+ };
169
+ let accountIdx: { waiting: number; balance: number; accountId: string } | null = null;
170
+ if (opts.owner) {
171
+ if (!opts.quoteCoinType) throw new Error('marketQueueState: owner reads need quoteCoinType');
172
+ const accountId = deriveAccountIdFrom(config, opts.owner);
173
+ const waiting = add(queue.waitingOrders({ config, arguments: { ...onQueue, accountId } }));
174
+ const loaded = add(
175
+ account.loadAccount({
176
+ config,
177
+ arguments: { self: deriveAccountWrapperIdFrom(config, opts.owner) },
178
+ }),
179
+ );
180
+ const balance = add(
181
+ account.balance({
182
+ config,
183
+ typeArguments: [opts.quoteCoinType],
184
+ arguments: { self: results[loaded] },
185
+ }),
186
+ );
187
+ accountIdx = { waiting, balance, accountId };
188
+ }
189
+ const recordIdx = (opts.recordIds ?? []).map((recordId) =>
190
+ add(queue.order({ config, arguments: { ...onQueue, recordId } })),
191
+ );
192
+
193
+ const cmds = await inspectReturns(client, tx);
194
+ const u64 = (idx: number, value = 0) => parseU64LE(cmds[idx][value]);
195
+ const cashBalance = u64(i.cashBalance);
196
+ const requiredCash = u64(i.requiredCash);
197
+ return {
198
+ marketId: normalizeSuiAddress(marketId),
199
+ queueId: normalizeSuiAddress(queueId),
200
+ expiryMs: u64(i.expiry),
201
+ mintPaused: parseBool(cmds[i.mintPaused][0]),
202
+ cashBalance,
203
+ requiredCash,
204
+ spareCash: cashBalance > requiredCash ? cashBalance - requiredCash : 0n,
205
+ waitingCashNeed: u64(i.orderFlow, 0),
206
+ payoutTreeNodeCount: u64(i.orderFlow, 1),
207
+ minEntryProbability: u64(i.orderFlow, 2),
208
+ backingBufferLambda: u64(i.backingBufferLambda),
209
+ stuck: parseBool(cmds[i.stuck][0]),
210
+ cohorts: {
211
+ count: u64(i.cohorts, 0),
212
+ oldestUncommittedTauMs: parseOptionalU64(cmds[i.cohorts][1]),
213
+ oldestUncommittedTauAboveCommittedMs: parseOptionalU64(cmds[i.cohorts][2]),
214
+ },
215
+ heads: {
216
+ resolveHead: u64(i.heads, 0),
217
+ nextId: u64(i.heads, 1),
218
+ lastTauMs: u64(i.heads, 2),
219
+ lastCommittedTauMs: u64(i.heads, 3),
220
+ },
221
+ pending: { mints: u64(i.pending, 0), sells: u64(i.pending, 1) },
222
+ payout: {
223
+ cursor: u64(i.payout, 0),
224
+ nextId: u64(i.payout, 1),
225
+ completed: parseBool(cmds[i.payout][2]),
226
+ },
227
+ desk: {
228
+ id: normalizeSuiAddress(config.orderDesk),
229
+ policy: policyFromBcs(DelayedExecutionPolicyBcs.parse(cmds[i.policy][0])),
230
+ versionWatermark: u64(i.deskWatermark),
231
+ },
232
+ protocol: {
233
+ noTradeWindowMs: u64(i.noTradeWindowMs),
234
+ feeIncentiveSubsidyRate: u64(i.subsidyRate),
235
+ tradingPaused: parseBool(cmds[i.tradingPaused][0]),
236
+ frozen: parseBool(cmds[i.frozen][0]),
237
+ versionWatermark: u64(i.watermark),
238
+ orderFlowEnabled: parseBool(cmds[i.orderFlowEnabled][0]),
239
+ },
240
+ account: accountIdx
241
+ ? {
242
+ accountId: accountIdx.accountId,
243
+ waitingOrders: u64(accountIdx.waiting),
244
+ availableRaw: u64(accountIdx.balance),
245
+ }
246
+ : null,
247
+ records: recordIdx.map((idx) => OptionalOrderView.parse(cmds[idx][0]) ?? null),
248
+ };
249
+ }
250
+
251
+ /** Queue records by record ID (`queue::order`), `null` for a missing or cleaned-up one. One simulate. */
252
+ export async function queuedOrders(
253
+ client: ReadClient,
254
+ config: OrdersGeneratedConfig,
255
+ target: QueueTarget,
256
+ recordIds: readonly bigint[],
257
+ ): Promise<(QueuedOrder | null)[]> {
258
+ if (recordIds.length === 0) return [];
259
+ const queueId = queueIdOf(config, target);
260
+ const tx = new Transaction();
261
+ for (const recordId of recordIds) {
262
+ tx.add(queue.order({ config, arguments: { queue: queueId, recordId } }));
263
+ }
264
+ const cmds = await inspectReturns(client, tx);
265
+ return cmds.map((rv) => OptionalOrderView.parse(rv[0]) ?? null);
266
+ }
267
+
268
+ /**
269
+ * `ProtocolConfig.version_watermark`, read from the object's own BCS rather than a getter, so it
270
+ * works against any package version: the getter only exists from the delayed-execution package
271
+ * on, while the struct layout is frozen since v1.
272
+ */
273
+ export async function versionWatermark(
274
+ client: ClientWithCoreApi,
275
+ protocolConfigId: string,
276
+ ): Promise<bigint> {
277
+ const { object } = await client.core.getObject({
278
+ objectId: protocolConfigId,
279
+ include: { content: true },
280
+ });
281
+ if (!object.content) throw new Error(`ProtocolConfig ${protocolConfigId} returned no content`);
282
+ return protocolConfig.ProtocolConfig.parse(object.content).version_watermark;
283
+ }
284
+
285
+ /**
286
+ * Which trade path a network is on:
287
+ * - `'immediate'`: the watermark isn't raised and this SDK has no delayed-execution record, so
288
+ * its call target is a pre-v4 Predict package. Use the immediate `mint*` / `redeem` builders.
289
+ * - `'awaiting-cutover'`: the config records delayed execution but the watermark isn't raised.
290
+ * Nothing trades: enqueue aborts `ECutoverNotReached`, and the immediate builders abort in the
291
+ * v4 package the config calls (`EDelayedExecutionRequired`). Trading stays paused through the
292
+ * cutover.
293
+ * - `'delayed'`: the watermark is raised. Use the `enqueue*` builders; the immediate ones abort
294
+ * `EDelayedExecutionRequired`.
295
+ * - `'unsupported'`: the watermark is raised but this SDK's config has no delayed-execution
296
+ * record for the network. Upgrade the SDK, or pass a config that records it.
297
+ * - `'retired'`: the watermark is above the Predict code version this SDK calls
298
+ * (`PREDICT_PACKAGE_VERSION`), so every version-gated call aborts `EPackageVersionDisabled`.
299
+ * Upgrade the SDK.
300
+ */
301
+ export type ExecutionMode =
302
+ 'immediate' | 'awaiting-cutover' | 'delayed' | 'unsupported' | 'retired';
303
+
304
+ /**
305
+ * The {@link ExecutionMode} for a watermark and what the config records.
306
+ * `recordsDelayedExecution`: the Predict upgrade, the order-flow companion and its desk, which
307
+ * queued orders need. `recordsPredictUpgrade`: the Predict upgrade alone
308
+ * (`packages.predictDelayedExecution`), which makes the config's Predict call target the package
309
+ * that retired the immediate trades. It defaults to `recordsDelayedExecution`.
310
+ */
311
+ export function executionModeFor(
312
+ watermark: bigint,
313
+ recordsDelayedExecution: boolean,
314
+ recordsPredictUpgrade: boolean = recordsDelayedExecution,
315
+ ): ExecutionMode {
316
+ if (watermark >= DELAYED_EXECUTION_VERSION) {
317
+ if (!recordsDelayedExecution) return 'unsupported';
318
+ return watermark > PREDICT_PACKAGE_VERSION ? 'retired' : 'delayed';
319
+ }
320
+ return recordsDelayedExecution || recordsPredictUpgrade ? 'awaiting-cutover' : 'immediate';
321
+ }
322
+
323
+ /**
324
+ * USDC sent to an account's wrapper address with `send_funds` that the account hasn't settled
325
+ * yet: refunds, sell proceeds and settled payouts land here. `account::balance` already counts
326
+ * them, so this is for display ("pending funds"), not for sizing.
327
+ */
328
+ export async function pendingFunds(
329
+ client: ClientWithCoreApi,
330
+ wrapperId: string,
331
+ coinType: string,
332
+ ): Promise<bigint> {
333
+ const { balance } = await client.core.getBalance({ owner: wrapperId, coinType });
334
+ return BigInt(balance.addressBalance);
335
+ }
336
+
337
+ // The prefix of `pyth_lazer::state::State` up to the upgrade cap, identical in the testnet and
338
+ // mainnet vendored sources. Parsing a prefix ignores the trailing `governance` field.
339
+ const LazerStatePrefix = bcs.struct('State', {
340
+ id: bcs.Address,
341
+ trusted_signers: bcs.vector(
342
+ bcs.struct('TrustedSignerInfo', { public_key: bcs.vector(bcs.u8()), expires_at: bcs.u64() }),
343
+ ),
344
+ upgrade_cap: bcs.struct('UpgradeCap', {
345
+ id: bcs.Address,
346
+ package: bcs.Address,
347
+ version: bcs.u64(),
348
+ policy: bcs.u8(),
349
+ }),
350
+ });
351
+
352
+ /**
353
+ * The current Pyth Lazer package (from `State.upgrade_cap.package`) and the original ID that
354
+ * types `update::Update` (from the State's own type). Read it per filler batch and never cache
355
+ * it: a Lazer upgrade retires the old package at once.
356
+ */
357
+ export async function lazerPackages(
358
+ client: ClientWithCoreApi,
359
+ stateId: string,
360
+ ): Promise<{ stateId: string; packageId: string; originalId: string }> {
361
+ const { object } = await client.core.getObject({ objectId: stateId, include: { content: true } });
362
+ if (!object.content) throw new Error(`Lazer State ${stateId} returned no content`);
363
+ const [originalId, module, name] = object.type.split('::');
364
+ if (module !== 'state' || name !== 'State') {
365
+ throw new Error(`${stateId} is a ${object.type}, not a Pyth Lazer State`);
366
+ }
367
+ return {
368
+ stateId: normalizeSuiAddress(stateId),
369
+ packageId: normalizeSuiAddress(LazerStatePrefix.parse(object.content).upgrade_cap.package),
370
+ originalId: normalizeSuiAddress(originalId),
371
+ };
372
+ }
373
+
374
+ /** `expiry_market::RedeemQuote` with camelCase keys. `proceeds` is before the order fee. */
375
+ export interface RedeemOpenQuoteRaw {
376
+ closeQuantity: bigint;
377
+ probability: bigint;
378
+ proceeds: bigint;
379
+ tradingFee: bigint;
380
+ builderFee: bigint;
381
+ inventoryImpactRebate: bigint;
382
+ }
383
+
384
+ /**
385
+ * Quote an early sell of an Open record at a fresh live pricer (`load_live_pricer`, then the
386
+ * queue's `quote_redeem_open`, which prices through Predict's `quote_close`), and read the desk's
387
+ * policy for the order fee. `proceeds` is before the order fee, with no congestion penalty.
388
+ * Aborts `ERecordNotOpen` when the record isn't Open. It doesn't check that the wrapper's account
389
+ * owns the record: the wrapper only supplies the builder code.
390
+ */
391
+ export async function quoteRedeemOpen(
392
+ client: ReadClient,
393
+ config: OrdersGeneratedConfig,
394
+ args: QueueTarget & {
395
+ wrapperId: string;
396
+ recordId: bigint;
397
+ closeQuantityRaw: bigint;
398
+ } & MarketFeeds,
399
+ ): Promise<{ quote: RedeemOpenQuoteRaw; policy: DelayedExecutionPolicy }> {
400
+ const tx = new Transaction();
401
+ const pricer = tx.add(loadLivePricer(config, args));
402
+ tx.add(
403
+ queue.quoteRedeemOpen({
404
+ config,
405
+ arguments: {
406
+ queue: queueIdOf(config, args),
407
+ market: args.expiryMarketId,
408
+ wrapper: args.wrapperId,
409
+ pricer,
410
+ recordId: args.recordId,
411
+ closeQuantity: args.closeQuantityRaw,
412
+ },
413
+ }),
414
+ );
415
+ tx.add(desk.policy({ config }));
416
+ const cmds = await inspectReturns(client, tx);
417
+ const q = expiryMarket.RedeemQuote.parse(cmds[1][0]);
418
+ return {
419
+ quote: {
420
+ closeQuantity: q.close_quantity,
421
+ probability: q.probability,
422
+ proceeds: q.proceeds,
423
+ tradingFee: q.trading_fee,
424
+ builderFee: q.builder_fee,
425
+ inventoryImpactRebate: q.inventory_impact_rebate,
426
+ },
427
+ policy: policyFromBcs(DelayedExecutionPolicyBcs.parse(cmds[2][0])),
428
+ };
429
+ }
430
+
431
+ /**
432
+ * `expiry_market::MintQuote` with camelCase keys. `allInCost` includes `penaltyFee`, which the
433
+ * delayed-execution package always quotes as 0: its quotes price like a queued fill.
434
+ */
435
+ export interface MintQuoteRaw {
436
+ quantity: bigint;
437
+ entryProbability: bigint;
438
+ premium: bigint;
439
+ tradingFee: bigint;
440
+ feeIncentiveSubsidy: bigint;
441
+ builderFee: bigint;
442
+ penaltyFee: bigint;
443
+ inventoryImpactCharge: bigint;
444
+ allInCost: bigint;
445
+ }
446
+
447
+ /** What to quote: an exact quantity, a premium budget, or an all-in budget. */
448
+ export type MintQuoteRequest =
449
+ | { shape: 'exact-quantity'; quantityRaw: bigint }
450
+ | { shape: 'exact-amount'; maxPremiumRaw: bigint; minQuantityRaw: bigint }
451
+ | { shape: 'exact-cost'; maxCostRaw: bigint; minQuantityRaw: bigint };
452
+
453
+ /**
454
+ * Quote a mint for one account without placing it: Predict's `quote_mint_for_account` (exact
455
+ * quantity and premium budget) or `quote_mint_exact_cost_for_account` (all-in budget), at a fresh
456
+ * live pricer. Both read the account's builder code and cap a budget at its balance. From the
457
+ * delayed-execution package on they price like a queued fill at the clock: no congestion penalty,
458
+ * and no trade-window or Pyth-staleness abort. They abort `EOrderFailsLimits` when the mint would
459
+ * be refused at the clock. With `ordersConfig`, also reads the order desk's policy.
460
+ */
461
+ export async function quoteMintForAccount(
462
+ client: ReadClient,
463
+ config: GeneratedConfig,
464
+ args: {
465
+ expiryMarketId: string;
466
+ wrapperId: string;
467
+ lowerTick: bigint;
468
+ higherTick: bigint;
469
+ request: MintQuoteRequest;
470
+ /** Also read the order desk's policy, in the same simulate. */
471
+ ordersConfig?: OrdersGeneratedConfig;
472
+ } & MarketFeeds,
473
+ ): Promise<{ quote: MintQuoteRaw; policy: DelayedExecutionPolicy | null }> {
474
+ const tx = new Transaction();
475
+ const pricer = tx.add(loadLivePricer(config, args));
476
+ const base = {
477
+ market: args.expiryMarketId,
478
+ wrapper: args.wrapperId,
479
+ pricer,
480
+ lowerTick: args.lowerTick,
481
+ higherTick: args.higherTick,
482
+ };
483
+ const r = args.request;
484
+ if (r.shape === 'exact-cost') {
485
+ tx.add(
486
+ expiryMarket.quoteMintExactCostForAccount({
487
+ config,
488
+ arguments: { ...base, maxCost: r.maxCostRaw, minQuantity: r.minQuantityRaw },
489
+ }),
490
+ );
491
+ } else {
492
+ tx.add(
493
+ expiryMarket.quoteMintForAccount({
494
+ config,
495
+ arguments:
496
+ r.shape === 'exact-quantity'
497
+ ? { ...base, maxPremium: 0n, minQuantity: r.quantityRaw, exactQuantity: true }
498
+ : {
499
+ ...base,
500
+ maxPremium: r.maxPremiumRaw,
501
+ minQuantity: r.minQuantityRaw,
502
+ exactQuantity: false,
503
+ },
504
+ }),
505
+ );
506
+ }
507
+ if (args.ordersConfig) tx.add(desk.policy({ config: args.ordersConfig }));
508
+ const cmds = await inspectReturns(client, tx);
509
+ const q = expiryMarket.MintQuote.parse(cmds[1][0]);
510
+ const quote: MintQuoteRaw = {
511
+ quantity: q.quantity,
512
+ entryProbability: q.entry_probability,
513
+ premium: q.premium,
514
+ tradingFee: q.trading_fee,
515
+ feeIncentiveSubsidy: q.fee_incentive_subsidy,
516
+ builderFee: q.builder_fee,
517
+ penaltyFee: q.penalty_fee,
518
+ inventoryImpactCharge: q.inventory_impact_charge,
519
+ allInCost: q.all_in_cost,
520
+ };
521
+ const policy = args.ordersConfig
522
+ ? policyFromBcs(DelayedExecutionPolicyBcs.parse(cmds[2][0]))
523
+ : null;
524
+ return { quote, policy };
525
+ }
526
+
527
+ /** What `quote_mint` quotes: an exact quantity, or the largest quantity a premium budget buys. */
528
+ export type AnonymousMintQuoteRequest = Exclude<MintQuoteRequest, { shape: 'exact-cost' }>;
529
+
530
+ /**
531
+ * Quote a mint for no particular account: Predict's `quote_mint`, at a fresh live pricer. It
532
+ * prices like a queued fill at the clock with no builder fee, so it previews a mint for a visitor
533
+ * without an account or a funded balance. It has no all-in budget mode. It aborts
534
+ * `EOrderFailsLimits` when the mint would be refused at the clock, for example below the 1 USDC
535
+ * minimum premium. With `ordersConfig`, also reads the order desk's policy.
536
+ */
537
+ export async function quoteMintAnonymous(
538
+ client: ReadClient,
539
+ config: GeneratedConfig,
540
+ args: {
541
+ expiryMarketId: string;
542
+ lowerTick: bigint;
543
+ higherTick: bigint;
544
+ request: AnonymousMintQuoteRequest;
545
+ ordersConfig?: OrdersGeneratedConfig;
546
+ } & MarketFeeds,
547
+ ): Promise<{ quote: MintQuoteRaw; policy: DelayedExecutionPolicy | null }> {
548
+ const tx = new Transaction();
549
+ const pricer = tx.add(loadLivePricer(config, args));
550
+ const r = args.request;
551
+ tx.add(
552
+ expiryMarket.quoteMint({
553
+ config,
554
+ arguments: {
555
+ market: args.expiryMarketId,
556
+ pricer,
557
+ lowerTick: args.lowerTick,
558
+ higherTick: args.higherTick,
559
+ ...(r.shape === 'exact-quantity'
560
+ ? { maxPremium: 0n, minQuantity: r.quantityRaw, exactQuantity: true }
561
+ : { maxPremium: r.maxPremiumRaw, minQuantity: r.minQuantityRaw, exactQuantity: false }),
562
+ },
563
+ }),
564
+ );
565
+ if (args.ordersConfig) tx.add(desk.policy({ config: args.ordersConfig }));
566
+ const cmds = await inspectReturns(client, tx);
567
+ const q = expiryMarket.MintQuote.parse(cmds[1][0]);
568
+ return {
569
+ quote: {
570
+ quantity: q.quantity,
571
+ entryProbability: q.entry_probability,
572
+ premium: q.premium,
573
+ tradingFee: q.trading_fee,
574
+ feeIncentiveSubsidy: q.fee_incentive_subsidy,
575
+ builderFee: q.builder_fee,
576
+ penaltyFee: q.penalty_fee,
577
+ inventoryImpactCharge: q.inventory_impact_charge,
578
+ allInCost: q.all_in_cost,
579
+ },
580
+ policy: args.ordersConfig ? policyFromBcs(DelayedExecutionPolicyBcs.parse(cmds[2][0])) : null,
581
+ };
582
+ }
583
+
584
+ /**
585
+ * The order desk's policy and an owner's USDC balance, in one simulate. The queued exact-cost
586
+ * quote needs both: enqueue escrows `min(max_cost, available − fee)`, while the chain quote caps
587
+ * at the whole balance.
588
+ */
589
+ export async function orderFeeAndBalance(
590
+ client: ReadClient,
591
+ config: OrdersGeneratedConfig,
592
+ owner: string,
593
+ coinType: string,
594
+ ): Promise<{ policy: DelayedExecutionPolicy; availableRaw: bigint }> {
595
+ const tx = new Transaction();
596
+ tx.add(desk.policy({ config }));
597
+ const loaded = tx.add(
598
+ account.loadAccount({ config, arguments: { self: deriveAccountWrapperIdFrom(config, owner) } }),
599
+ );
600
+ tx.add(account.balance({ config, typeArguments: [coinType], arguments: { self: loaded } }));
601
+ const cmds = await inspectReturns(client, tx);
602
+ return {
603
+ policy: policyFromBcs(DelayedExecutionPolicyBcs.parse(cmds[0][0])),
604
+ availableRaw: parseU64LE(cmds[2][0]),
605
+ };
606
+ }
@@ -40,3 +40,38 @@ export function binaryRangeTicks(
40
40
  higherTick: isUp ? POS_INF_TICK : tick,
41
41
  };
42
42
  }
43
+
44
+ /**
45
+ * Snap a strike in USD to a market's admission grid (`MarketSummary.admissionTickSize`), the step
46
+ * every new mint boundary must be a whole multiple of. Use it on a form's bound inputs: `'down'`
47
+ * for a lower bound and `'up'` for an upper bound keep a range from shrinking past what was typed,
48
+ * and `'nearest'` (the default) suits a single strike. Exact: it rounds in the 1e9-scaled integers
49
+ * the chain uses, not in floating point.
50
+ */
51
+ export function snapStrike(
52
+ price: number,
53
+ admissionTickSize: number,
54
+ mode: 'down' | 'up' | 'nearest' = 'nearest',
55
+ ): number {
56
+ if (!(Number.isFinite(price) && price > 0)) {
57
+ throw new PredictInputError(`strike must be a positive number, got ${price}`);
58
+ }
59
+ if (!(Number.isFinite(admissionTickSize) && admissionTickSize > 0)) {
60
+ throw new PredictInputError(`admissionTickSize must be positive, got ${admissionTickSize}`);
61
+ }
62
+ const step = BigInt(Math.round(admissionTickSize * 1e9));
63
+ const raw = BigInt(Math.round(price * 1e9));
64
+ const below = (raw / step) * step;
65
+ const snapped =
66
+ mode === 'down' || raw === below
67
+ ? below
68
+ : mode === 'up'
69
+ ? below + step
70
+ : raw - below < below + step - raw
71
+ ? below
72
+ : below + step;
73
+ if (snapped === 0n) {
74
+ throw new PredictInputError(`strike ${price} snaps below the first ${admissionTickSize} step`);
75
+ }
76
+ return Number(snapped) / 1e9;
77
+ }