@ickb/sdk 9000.0.1 → 9001.0.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 (138) hide show
  1. package/README.md +14 -11
  2. package/dist/constants.d.ts +0 -4
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +6 -24
  5. package/dist/constants.js.map +1 -1
  6. package/dist/conversion/estimate.d.ts +0 -6
  7. package/dist/conversion/estimate.d.ts.map +1 -1
  8. package/dist/conversion/estimate.js +1 -1
  9. package/dist/conversion/estimate.js.map +1 -1
  10. package/dist/conversion/maturity.d.ts +1 -1
  11. package/dist/conversion/maturity.js +13 -12
  12. package/dist/conversion/maturity.js.map +1 -1
  13. package/dist/conversion/plans.d.ts.map +1 -1
  14. package/dist/conversion/plans.js +6 -6
  15. package/dist/conversion/plans.js.map +1 -1
  16. package/dist/conversion/projection.d.ts +5 -6
  17. package/dist/conversion/projection.d.ts.map +1 -1
  18. package/dist/conversion/projection.js +11 -8
  19. package/dist/conversion/projection.js.map +1 -1
  20. package/dist/conversion/types.d.ts +29 -17
  21. package/dist/conversion/types.d.ts.map +1 -1
  22. package/dist/conversion/withdrawal_ring.d.ts +1 -3
  23. package/dist/conversion/withdrawal_ring.d.ts.map +1 -1
  24. package/dist/conversion/withdrawal_ring.js +4 -4
  25. package/dist/conversion/withdrawal_ring.js.map +1 -1
  26. package/dist/dao.d.ts +19 -5
  27. package/dist/dao.d.ts.map +1 -1
  28. package/dist/dao.js +24 -12
  29. package/dist/dao.js.map +1 -1
  30. package/dist/index.d.ts +9 -11
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +2 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/logic.d.ts +10 -14
  35. package/dist/logic.d.ts.map +1 -1
  36. package/dist/logic.js +28 -39
  37. package/dist/logic.js.map +1 -1
  38. package/dist/order/cells.d.ts +10 -49
  39. package/dist/order/cells.d.ts.map +1 -1
  40. package/dist/order/cells.js +28 -146
  41. package/dist/order/cells.js.map +1 -1
  42. package/dist/order/conversion.d.ts +3 -9
  43. package/dist/order/conversion.d.ts.map +1 -1
  44. package/dist/order/conversion.js +8 -11
  45. package/dist/order/conversion.js.map +1 -1
  46. package/dist/order/fill.d.ts +2 -2
  47. package/dist/order/fill.js +3 -3
  48. package/dist/order/fill.js.map +1 -1
  49. package/dist/order/info.d.ts.map +1 -1
  50. package/dist/order/info.js +3 -0
  51. package/dist/order/info.js.map +1 -1
  52. package/dist/order/matcher.d.ts +1 -1
  53. package/dist/order/matcher.d.ts.map +1 -1
  54. package/dist/order/matcher.js +3 -2
  55. package/dist/order/matcher.js.map +1 -1
  56. package/dist/order/order.d.ts +6 -2
  57. package/dist/order/order.d.ts.map +1 -1
  58. package/dist/order/order.js +32 -37
  59. package/dist/order/order.js.map +1 -1
  60. package/dist/order/order_data.d.ts.map +1 -1
  61. package/dist/order/order_data.js +3 -0
  62. package/dist/order/order_data.js.map +1 -1
  63. package/dist/order/ratio.d.ts +0 -2
  64. package/dist/order/ratio.d.ts.map +1 -1
  65. package/dist/order/ratio.js +3 -11
  66. package/dist/order/ratio.js.map +1 -1
  67. package/dist/order/relative.d.ts.map +1 -1
  68. package/dist/order/relative.js +3 -0
  69. package/dist/order/relative.js.map +1 -1
  70. package/dist/owned_owner.d.ts +10 -15
  71. package/dist/owned_owner.d.ts.map +1 -1
  72. package/dist/owned_owner.js +27 -47
  73. package/dist/owned_owner.js.map +1 -1
  74. package/dist/sdk.d.ts +74 -31
  75. package/dist/sdk.d.ts.map +1 -1
  76. package/dist/sdk.js +81 -59
  77. package/dist/sdk.js.map +1 -1
  78. package/dist/send/sign_and_send_transaction.d.ts +32 -17
  79. package/dist/send/sign_and_send_transaction.d.ts.map +1 -1
  80. package/dist/send/sign_and_send_transaction.js +128 -28
  81. package/dist/send/sign_and_send_transaction.js.map +1 -1
  82. package/dist/send/wait_transaction.d.ts +7 -6
  83. package/dist/send/wait_transaction.d.ts.map +1 -1
  84. package/dist/send/wait_transaction.js +29 -61
  85. package/dist/send/wait_transaction.js.map +1 -1
  86. package/dist/udt.d.ts +20 -35
  87. package/dist/udt.d.ts.map +1 -1
  88. package/dist/udt.js +39 -103
  89. package/dist/udt.js.map +1 -1
  90. package/dist/utils/chain.d.ts +0 -35
  91. package/dist/utils/chain.d.ts.map +1 -1
  92. package/dist/utils/chain.js +1 -19
  93. package/dist/utils/chain.js.map +1 -1
  94. package/dist/utils/codec.d.ts.map +1 -1
  95. package/dist/utils/codec.js +3 -0
  96. package/dist/utils/codec.js.map +1 -1
  97. package/dist/utils/utils.d.ts +19 -46
  98. package/dist/utils/utils.d.ts.map +1 -1
  99. package/dist/utils/utils.js +53 -74
  100. package/dist/utils/utils.js.map +1 -1
  101. package/dist/utils/verified_client.d.ts +19 -0
  102. package/dist/utils/verified_client.d.ts.map +1 -0
  103. package/dist/utils/verified_client.js +92 -0
  104. package/dist/utils/verified_client.js.map +1 -0
  105. package/package.json +2 -2
  106. package/src/constants.ts +6 -25
  107. package/src/conversion/estimate.ts +1 -3
  108. package/src/conversion/maturity.ts +13 -14
  109. package/src/conversion/plans.ts +6 -6
  110. package/src/conversion/projection.ts +15 -7
  111. package/src/conversion/types.ts +32 -17
  112. package/src/conversion/withdrawal_ring.ts +4 -8
  113. package/src/dao.ts +38 -14
  114. package/src/index.ts +8 -8
  115. package/src/logic.ts +40 -52
  116. package/src/order/cells.ts +37 -174
  117. package/src/order/conversion.ts +8 -19
  118. package/src/order/fill.ts +3 -3
  119. package/src/order/info.ts +3 -0
  120. package/src/order/matcher.ts +3 -3
  121. package/src/order/order.ts +38 -52
  122. package/src/order/order_data.ts +3 -0
  123. package/src/order/ratio.ts +3 -20
  124. package/src/order/relative.ts +3 -0
  125. package/src/owned_owner.ts +47 -77
  126. package/src/sdk.ts +95 -108
  127. package/src/send/sign_and_send_transaction.ts +164 -28
  128. package/src/send/wait_transaction.ts +44 -88
  129. package/src/udt.ts +56 -124
  130. package/src/utils/chain.ts +0 -41
  131. package/src/utils/codec.ts +4 -0
  132. package/src/utils/utils.ts +76 -105
  133. package/src/utils/verified_client.ts +136 -0
  134. package/dist/utils/transaction_header.d.ts +0 -17
  135. package/dist/utils/transaction_header.d.ts.map +0 -1
  136. package/dist/utils/transaction_header.js +0 -44
  137. package/dist/utils/transaction_header.js.map +0 -1
  138. package/src/utils/transaction_header.ts +0 -66
@@ -2,10 +2,6 @@ import type { ccc } from "@ckb-ccc/core";
2
2
  import type { ValueComponents } from "../utils/utils.ts";
3
3
  import { OrderData } from "./order_data.ts";
4
4
 
5
- // The resolver's own group, keyed by its order cell: matching and transactions use this
6
- // parse, so a caller's own wrapper is only ever a key into it.
7
- const resolvedOrderGroups = new WeakMap<ccc.Cell, OrderGroup>();
8
-
9
5
  /**
10
6
  * Represents a parsed order cell on the blockchain.
11
7
  *
@@ -114,15 +110,13 @@ export class OrderCell implements ValueComponents {
114
110
  : 0n;
115
111
 
116
112
  const absTotal = absoluteOrderTotal(ckb2UdtValue, udt2CkbValue, data.info);
117
- const absProgress = absoluteOrderProgress({
118
- absTotal,
119
- ckbUnoccupied,
120
- isCkb2Udt,
121
- isDualRatio,
122
- udtScale: ckbToUdt.udtScale,
123
- udtValue,
124
- udtToCkbCkbScale: udtToCkb.ckbScale,
125
- });
113
+ // What the order already holds of its output side; a dual-ratio order is complete.
114
+ let absProgress = ckbUnoccupied * udtToCkb.ckbScale;
115
+ if (isDualRatio) {
116
+ absProgress = absTotal;
117
+ } else if (isCkb2Udt) {
118
+ absProgress = udtValue * ckbToUdt.udtScale;
119
+ }
126
120
 
127
121
  return new OrderCell({ cell, data, ckbUnoccupied, absTotal, absProgress });
128
122
  }
@@ -168,56 +162,25 @@ export class OrderCell implements ValueComponents {
168
162
  }
169
163
 
170
164
  /**
171
- * Validates a descendant order against this origin order.
165
+ * Whether `descendant` can be this origin order's live continuation.
172
166
  *
173
167
  * @remarks
174
168
  * This confusion-attack guard requires the same order script, UDT type,
175
169
  * master, and info, while forbidding lower total value or lower progress.
176
170
  * See {@link https://github.com/ickb/whitepaper/issues/19}.
177
171
  */
178
- public validate(descendant: OrderCell): void {
179
- if (!this.data.isMint()) {
180
- throw new Error("Origin is not a mint order");
181
- }
182
-
183
- if (!this.cell.cellOutput.lock.eq(descendant.cell.cellOutput.lock)) {
184
- throw new Error("Order script different");
185
- }
186
-
187
- const udt = this.cell.cellOutput.type;
188
- if (udt === undefined || descendant.cell.cellOutput.type?.eq(udt) !== true) {
189
- throw new Error("UDT type is different");
190
- }
191
-
192
- if (!descendant.getMaster().eq(this.getMaster())) {
193
- throw new Error("Master is different");
194
- }
195
-
196
- if (!this.data.info.eq(descendant.data.info)) {
197
- throw new Error("Info is different");
198
- }
199
-
200
- if (this.absTotal > descendant.absTotal) {
201
- throw new Error("Total value is lower than the original one");
202
- }
203
-
204
- if (this.absProgress > descendant.absProgress) {
205
- throw new Error("Progress is lower than the original one");
206
- }
207
- }
208
-
209
- /**
210
- * Checks if the descendant order is valid against this order.
211
- * @param descendant - The descendant order to validate.
212
- * @returns True if the descendant is valid, otherwise false.
213
- */
214
172
  public isValid(descendant: OrderCell): boolean {
215
- try {
216
- this.validate(descendant);
217
- return true;
218
- } catch {
219
- return false;
220
- }
173
+ const udt = this.cell.cellOutput.type;
174
+ return (
175
+ this.data.isMint() &&
176
+ this.cell.cellOutput.lock.eq(descendant.cell.cellOutput.lock) &&
177
+ udt !== undefined &&
178
+ descendant.cell.cellOutput.type?.eq(udt) === true &&
179
+ descendant.getMaster().eq(this.getMaster()) &&
180
+ this.data.info.eq(descendant.data.info) &&
181
+ this.absTotal <= descendant.absTotal &&
182
+ this.absProgress <= descendant.absProgress
183
+ );
221
184
  }
222
185
 
223
186
  /**
@@ -272,24 +235,6 @@ function absoluteOrderTotal(
272
235
  );
273
236
  }
274
237
 
275
- function absoluteOrderProgress(options: {
276
- absTotal: bigint;
277
- ckbUnoccupied: bigint;
278
- isCkb2Udt: boolean;
279
- isDualRatio: boolean;
280
- udtScale: bigint;
281
- udtToCkbCkbScale: bigint;
282
- udtValue: bigint;
283
- }): bigint {
284
- if (options.isDualRatio) {
285
- return options.absTotal;
286
- }
287
- if (options.isCkb2Udt) {
288
- return options.udtValue * options.udtScale;
289
- }
290
- return options.ckbUnoccupied * options.udtToCkbCkbScale;
291
- }
292
-
293
238
  function betterOrderResolution(
294
239
  current: OrderResolution | undefined,
295
240
  candidate: OrderCell,
@@ -325,19 +270,10 @@ function compareOrderScore(left: OrderCell, right: OrderCell): number {
325
270
  /**
326
271
  * Represents a master cell
327
272
  */
328
- export class MasterCell implements ValueComponents {
273
+ export class MasterCell {
329
274
  /** Raw live master cell that anchors an order group. */
330
275
  public readonly cell: ccc.Cell;
331
276
 
332
- /**
333
- * Gets the UDT value of the cell.
334
- *
335
- * For a Master Cell, the UDT amount is always zero.
336
- *
337
- * @returns The UDT amount as a `ccc.FixedPoint` (0n).
338
- */
339
- public readonly udtValue = 0n;
340
-
341
277
  /**
342
278
  * Creates an instance of MasterCell.
343
279
  * @param cell - The ccc.Cell instance to be wrapped by the MasterCell.
@@ -346,28 +282,12 @@ export class MasterCell implements ValueComponents {
346
282
  this.cell = cell;
347
283
  }
348
284
 
349
- /**
350
- * Gets the CKB value of the cell.
351
- *
352
- * @returns The total CKB amount as a `ccc.FixedPoint`.
353
- */
354
- public get ckbValue(): ccc.FixedPoint {
355
- return this.cell.cellOutput.capacity;
356
- }
357
-
358
- /**
359
- * Validates the MasterCell against an OrderCell.
360
- * @param order - The OrderCell to validate against.
361
- * @throws Error if the order script is different or if the master is different.
362
- */
363
- public validate(order: OrderCell): void {
364
- if (this.cell.cellOutput.type?.eq(order.cell.cellOutput.lock) !== true) {
365
- throw new Error("Order script different");
366
- }
367
-
368
- if (!order.getMaster().eq(this.cell.outPoint)) {
369
- throw new Error("Master is different");
370
- }
285
+ /** Whether this master anchors `order`: its type is the order script and the order names it. */
286
+ public anchors(order: OrderCell): boolean {
287
+ return (
288
+ this.cell.cellOutput.type?.eq(order.cell.cellOutput.lock) === true &&
289
+ order.getMaster().eq(this.cell.outPoint)
290
+ );
371
291
  }
372
292
  }
373
293
 
@@ -381,21 +301,21 @@ export class OrderGroup implements ValueComponents {
381
301
  public readonly order: OrderCell;
382
302
  /** Origin order used to validate descendant progress and identity. */
383
303
  public readonly origin: OrderCell;
384
- /** The block that committed the origin, when known: how long the order has been on the book. */
385
- public readonly blockNumber: ccc.Num | undefined;
304
+ /** The block that committed the origin and its master: how long the order has been on the book. */
305
+ public readonly blockNumber: ccc.Num;
386
306
 
387
307
  /**
388
308
  * Creates an instance of OrderGroup.
389
309
  * @param master - The master cell associated with the order group.
390
310
  * @param order - The order within the group.
391
311
  * @param origin - The original order associated with the group.
392
- * @param blockNumber - The block that committed the origin, if committed.
312
+ * @param blockNumber - The block that committed the origin and its master.
393
313
  */
394
314
  constructor(
395
315
  master: MasterCell,
396
316
  order: OrderCell,
397
317
  origin: OrderCell,
398
- blockNumber?: ccc.Num,
318
+ blockNumber: ccc.Num,
399
319
  ) {
400
320
  this.master = master;
401
321
  this.order = order;
@@ -432,7 +352,7 @@ export class OrderGroup implements ValueComponents {
432
352
  master: MasterCell,
433
353
  order: OrderCell,
434
354
  origin: OrderCell,
435
- blockNumber?: ccc.Num,
355
+ blockNumber: ccc.Num,
436
356
  ): OrderGroup | undefined {
437
357
  const og = new OrderGroup(master, order, origin, blockNumber);
438
358
  if (og.isValid()) {
@@ -441,27 +361,13 @@ export class OrderGroup implements ValueComponents {
441
361
  return undefined;
442
362
  }
443
363
 
444
- /**
445
- * Validates the order group against its master and origin orders.
446
- * @throws Will throw an error if validation fails.
447
- */
448
- public validate(): void {
449
- this.master.validate(this.origin);
450
- this.master.validate(this.order);
451
- this.origin.validate(this.order);
452
- }
453
-
454
- /**
455
- * Checks if the order group is valid.
456
- * @returns True if the order group is valid, otherwise false.
457
- */
364
+ /** Whether the master anchors both orders and the order continues its origin. */
458
365
  public isValid(): boolean {
459
- try {
460
- this.validate();
461
- return true;
462
- } catch {
463
- return false;
464
- }
366
+ return (
367
+ this.master.anchors(this.origin) &&
368
+ this.master.anchors(this.order) &&
369
+ this.origin.isValid(this.order)
370
+ );
465
371
  }
466
372
 
467
373
  /**
@@ -475,46 +381,3 @@ export class OrderGroup implements ValueComponents {
475
381
  return locks.some((l) => lock.eq(l));
476
382
  }
477
383
  }
478
-
479
- /**
480
- * Records canonical resolver output without exposing forgeable provenance data. The
481
- * group and its parsed parts are frozen, since matching and transactions build on them.
482
- */
483
- export function attestResolvedOrderGroup(group: OrderGroup): OrderGroup {
484
- group.validate();
485
- for (const order of [group.order, group.origin]) {
486
- const { info } = order.data;
487
- for (const part of [
488
- order,
489
- order.data,
490
- order.data.master,
491
- info,
492
- info.ckbToUdt,
493
- info.udtToCkb,
494
- ]) {
495
- Object.freeze(part);
496
- }
497
- }
498
- Object.freeze(group.master);
499
- resolvedOrderGroups.set(group.order.cell, Object.freeze(group));
500
- return group;
501
- }
502
-
503
- /** Returns the resolver's group at a matching or transaction boundary. */
504
- export function validatedOrderGroup(group: OrderGroup): OrderGroup {
505
- if (!(group instanceof OrderGroup)) {
506
- throw new TypeError("Matching requires resolved OrderGroups from findOrders()");
507
- }
508
-
509
- const attested = resolvedOrderGroups.get(group.order.cell);
510
- if (attested === undefined) {
511
- throw new TypeError("OrderGroup was not produced by the order resolver");
512
- }
513
- if (
514
- group.master.cell !== attested.master.cell ||
515
- group.origin.cell !== attested.origin.cell
516
- ) {
517
- throw new TypeError("OrderGroup does not match its resolver attestation");
518
- }
519
- return attested;
520
- }
@@ -1,10 +1,6 @@
1
1
  import { ccc } from "@ckb-ccc/core";
2
- import {
3
- ceilDiv,
4
- minBigInt,
5
- type ExchangeRatio,
6
- type ValueComponents,
7
- } from "../utils/utils.ts";
2
+ import { ckbFromUdt } from "../udt.ts";
3
+ import { ceilDiv, type ExchangeRatio, type ValueComponents } from "../utils/utils.ts";
8
4
  import { Info } from "./info.ts";
9
5
  import { Ratio } from "./ratio.ts";
10
6
 
@@ -35,15 +31,8 @@ export function quoteConversion(
35
31
  isCkb2Udt: boolean,
36
32
  midpoint: ExchangeRatio,
37
33
  amounts: ValueComponents,
38
- options?: {
39
- fee?: ccc.Num;
40
- feeBase?: ccc.Num;
41
- },
34
+ { fee, feeBase }: { fee: ccc.Num; feeBase: ccc.Num },
42
35
  ): { convertedAmount: ccc.FixedPoint; ckbFee: ccc.FixedPoint; info: Info } {
43
- const fee = options?.fee ?? 0n;
44
- // Generic denominator for callers that pass a fee without a scale; Stack's
45
- // own default pair is owned publicly by the SDK, not by this entity.
46
- const feeBase = options?.feeBase ?? 100000n;
47
36
  const base = Ratio.from(midpoint);
48
37
  const amount = isCkb2Udt ? amounts.ckbValue : amounts.udtValue;
49
38
  const { aScale, bScale } = feeAdjustedScales(base, isCkb2Udt, fee, feeBase);
@@ -52,8 +41,8 @@ export function quoteConversion(
52
41
 
53
42
  if (amount > 0n && fee !== 0n) {
54
43
  ckbFee = isCkb2Udt
55
- ? amount - base.convert(false, convertedAmount, false)
56
- : base.convert(false, amount, false) - convertedAmount;
44
+ ? amount - ckbFromUdt(convertedAmount, base)
45
+ : ckbFromUdt(amount, base) - convertedAmount;
57
46
  }
58
47
 
59
48
  // Every order this SDK creates carries the default minimum match, the one the bot's
@@ -99,7 +88,7 @@ function feeAdjustedScales(
99
88
  * The ratio that reproduces the quote exactly: the greatest Uint64 fraction at most
100
89
  * `quotedOutput / inputAmount`, which must still round the input up to the quoted output.
101
90
  */
102
- export function quotePreservingRatio(
91
+ function quotePreservingRatio(
103
92
  inputAmount: ccc.FixedPoint,
104
93
  quotedOutput: ccc.FixedPoint,
105
94
  isCkb2Udt: boolean,
@@ -147,10 +136,10 @@ export function greatestBoundedFractionAtMost(
147
136
  const quotient = n / d;
148
137
  let term = quotient;
149
138
  if (p !== 0n) {
150
- term = minBigInt(term, (maxTerm - previousP) / p);
139
+ term = ccc.numMin(term, (maxTerm - previousP) / p);
151
140
  }
152
141
  if (q !== 0n) {
153
- term = minBigInt(term, (maxTerm - previousQ) / q);
142
+ term = ccc.numMin(term, (maxTerm - previousQ) / q);
154
143
  }
155
144
  const candidate = {
156
145
  numerator: term * p + previousP,
package/src/order/fill.ts CHANGED
@@ -75,9 +75,9 @@ export const STALE_ORDER_BLOCKS = (30n * 24n * 60n * 60n) / 8n;
75
75
  /**
76
76
  * Whether the order has sat on the book for thirty days: whatever the reason (dust under
77
77
  * the fill's cost, a remainder another matcher left, an ask above the market), it is
78
- * collected and its funds returned. Age from the origin's block, so an uncommitted origin
79
- * is as fresh as an order gets. The block count assumes eight-second blocks, not wall time.
78
+ * collected and its funds returned. Age from the origin's block. The block count assumes
79
+ * eight-second blocks, not wall time.
80
80
  */
81
81
  export function isStale(group: OrderGroup, tip: ccc.ClientBlockHeader): boolean {
82
- return (group.blockNumber ?? tip.number) + STALE_ORDER_BLOCKS <= tip.number;
82
+ return group.blockNumber + STALE_ORDER_BLOCKS <= tip.number;
83
83
  }
package/src/order/info.ts CHANGED
@@ -66,6 +66,9 @@ export interface Info {
66
66
  // cannot fill it whole. Fee changes and an aging buy can still make a remainder unprofitable.
67
67
  export const CKB_MIN_MATCH_LOG_DEFAULT = 36;
68
68
 
69
+ // An interface, this class expression and a typed const rather than one plain class: a
70
+ // class extending `ccc.Entity.Base<...>()` emits an unexported `*_base` declaration, which
71
+ // API Extractor's ae-forgotten-export rejects (tried 2026-10-30).
69
72
  // eslint-disable-next-line @typescript-eslint/no-shadow -- Preserve the runtime constructor name.
70
73
  const InfoImplementation = class Info extends InfoBase {
71
74
  static {
@@ -1,5 +1,5 @@
1
- import type { ccc } from "@ckb-ccc/core";
2
- import { ceilDiv, minBigInt } from "../utils/utils.ts";
1
+ import { ccc } from "@ckb-ccc/core";
2
+ import { ceilDiv } from "../utils/utils.ts";
3
3
  import type { OrderCell, OrderGroup } from "./cells.ts";
4
4
 
5
5
  /**
@@ -167,7 +167,7 @@ function orderMatcherTerms(
167
167
  return {
168
168
  ...terms,
169
169
  // An order under its own minimum can only be completed.
170
- bMinMatch: minBigInt(bMinMatch, bMaxMatch),
170
+ bMinMatch: ccc.numMin(bMinMatch, bMaxMatch),
171
171
  bMaxMatch,
172
172
  bMaxOut,
173
173
  };
@@ -1,19 +1,11 @@
1
1
  import { ccc } from "@ckb-ccc/core";
2
2
  import { findCells, type ScriptDeps, type ValueComponents } from "../utils/utils.ts";
3
- import {
4
- attestResolvedOrderGroup,
5
- MasterCell,
6
- OrderCell,
7
- OrderGroup,
8
- validatedOrderGroup,
9
- } from "./cells.ts";
3
+ import { MasterCell, OrderCell, OrderGroup } from "./cells.ts";
10
4
  import { Info, type InfoLike } from "./info.ts";
11
5
  import type { Match } from "./matcher.ts";
12
6
  import { OrderData } from "./order_data.ts";
13
7
  import { Relative } from "./relative.ts";
14
8
 
15
- type TransactionResponse = Awaited<ReturnType<ccc.Client["getTransaction"]>>;
16
-
17
9
  /**
18
10
  * Builds and scans iCKB Stack order cells for one order script deployment.
19
11
  */
@@ -124,11 +116,10 @@ export class OrderManager implements ScriptDeps {
124
116
  */
125
117
  public melt(txLike: ccc.TransactionLike, groups: OrderGroup[]): ccc.Transaction {
126
118
  const tx = ccc.Transaction.from(txLike);
127
- const selected = groups.map(validatedOrderGroup);
128
- if (selected.length === 0) {
119
+ if (groups.length === 0) {
129
120
  return tx;
130
121
  }
131
- for (const group of selected) {
122
+ for (const group of groups) {
132
123
  if (!this.isOrder(group.order.cell)) {
133
124
  throw new Error(
134
125
  `Melt order ${group.order.cell.outPoint.toHex()} does not match this order manager`,
@@ -136,7 +127,7 @@ export class OrderManager implements ScriptDeps {
136
127
  }
137
128
  }
138
129
  tx.addCellDeps(this.cellDeps);
139
- for (const group of selected) {
130
+ for (const group of groups) {
140
131
  tx.addInput(group.order.cell);
141
132
  tx.addInput(group.master.cell);
142
133
  }
@@ -153,9 +144,9 @@ export class OrderManager implements ScriptDeps {
153
144
  this.findAllMasters(client),
154
145
  ]);
155
146
  const rawGroups = new Map(
156
- masters.map((master) => [
147
+ masters.map(({ master, blockNumber }) => [
157
148
  master.cell.outPoint.toHex(),
158
- { master, orders: new Array<OrderCell>() },
149
+ { master, blockNumber, orders: new Array<OrderCell>() },
159
150
  ]),
160
151
  );
161
152
  for (const order of orders) {
@@ -167,8 +158,8 @@ export class OrderManager implements ScriptDeps {
167
158
  const groups = await Promise.all(
168
159
  [...rawGroups.values()]
169
160
  .filter((rawGroup) => rawGroup.orders.length > 0)
170
- .map(async ({ master, orders: candidates }) =>
171
- this.resolveOrderGroup(client, master, candidates),
161
+ .map(async ({ master, blockNumber, orders: candidates }) =>
162
+ this.resolveOrderGroup(client, master, blockNumber, candidates),
172
163
  ),
173
164
  );
174
165
  return groups.filter((group) => group !== undefined);
@@ -182,13 +173,20 @@ export class OrderManager implements ScriptDeps {
182
173
  scriptSearchMode: "exact",
183
174
  withData: true,
184
175
  });
185
- return cells.flatMap((cell) => {
176
+ return cells.flatMap(({ cell }) => {
186
177
  const order = OrderCell.tryFrom(cell);
187
178
  return order !== undefined && this.isOrder(cell) ? [order] : [];
188
179
  });
189
180
  }
190
181
 
191
- private async findAllMasters(client: ccc.Client): Promise<MasterCell[]> {
182
+ /**
183
+ * Every master with the block that committed it, which is its mint order's block too:
184
+ * a master is created beside its mint order and never changes until melted, so this
185
+ * dates how long the order has been on the book.
186
+ */
187
+ private async findAllMasters(
188
+ client: ccc.Client,
189
+ ): Promise<Array<{ master: MasterCell; blockNumber: ccc.Num }>> {
192
190
  // An exact type search returns master cells only, so nothing re-filters here.
193
191
  const cells = await findCells(client, {
194
192
  script: this.script,
@@ -196,40 +194,47 @@ export class OrderManager implements ScriptDeps {
196
194
  scriptSearchMode: "exact",
197
195
  withData: true,
198
196
  });
199
- return cells.map((cell) => new MasterCell(cell));
197
+ return cells.map(({ cell, blockNumber }) => ({
198
+ master: new MasterCell(cell),
199
+ blockNumber,
200
+ }));
200
201
  }
201
202
 
202
203
  /** Resolves one master and its descendant orders into a validated order group. */
203
204
  private async resolveOrderGroup(
204
205
  client: ccc.Client,
205
206
  master: MasterCell,
207
+ blockNumber: ccc.Num,
206
208
  orders: OrderCell[],
207
209
  ): Promise<OrderGroup | undefined> {
208
- const found = await this.findOrigin(client, master.cell.outPoint);
209
- const order = found?.origin.resolve(orders);
210
- if (found === undefined || order === undefined) {
210
+ const origin = await this.findOrigin(client, master.cell.outPoint);
211
+ const order = origin?.resolve(orders);
212
+ if (origin === undefined || order === undefined) {
211
213
  return undefined;
212
214
  }
213
- const group = OrderGroup.tryFrom(master, order, found.origin, found.blockNumber);
215
+ const group = OrderGroup.tryFrom(master, order, origin, blockNumber);
214
216
  // A dual-ratio order, valid on chain but placed by nothing in the stack, is left to
215
217
  // whoever placed it: neither matched, estimated, shown nor melted here.
216
218
  if (group === undefined || group.order.data.info.isDualRatio()) {
217
219
  return undefined;
218
220
  }
219
- return attestResolvedOrderGroup(group);
221
+ return group;
220
222
  }
221
223
 
222
224
  /**
223
225
  * The mint order created beside the master, read from the master's transaction: the
224
- * client cache first, then the node, recording the response for the next read. The
225
- * response's block number dates the order; an uncommitted origin has none.
226
+ * client cache first, then the node, which records the response for the next read.
226
227
  */
227
228
  private async findOrigin(
228
229
  client: ccc.Client,
229
230
  master: ccc.OutPoint,
230
- ): Promise<{ origin: OrderCell; blockNumber: ccc.Num | undefined } | undefined> {
231
+ ): Promise<OrderCell | undefined> {
231
232
  const { txHash, index: masterIndex } = master;
232
- const response = await cachedTransactionResponse(client, txHash);
233
+ // A cached body is trusted like the scan that found the master: an order is a node's
234
+ // claim until the send checks every cell it spends against its creating transaction.
235
+ const response =
236
+ (await client.cache.getTransactionResponse(txHash)) ??
237
+ (await client.getTransaction(txHash));
233
238
  if (response === undefined) {
234
239
  return undefined;
235
240
  }
@@ -260,9 +265,7 @@ export class OrderManager implements ScriptDeps {
260
265
  }
261
266
  origin = order;
262
267
  }
263
- return origin === undefined
264
- ? undefined
265
- : { origin, blockNumber: response.blockNumber };
268
+ return origin;
266
269
  }
267
270
 
268
271
  private validatedPartial({
@@ -270,9 +273,8 @@ export class OrderManager implements ScriptDeps {
270
273
  ckbOut,
271
274
  udtOut,
272
275
  }: Match["partials"][number]): Match["partials"][number] {
273
- const validated = validatedOrderGroup(group);
274
- const outPoint = validated.order.cell.outPoint.toHex();
275
- if (!this.isOrder(validated.order.cell)) {
276
+ const outPoint = group.order.cell.outPoint.toHex();
277
+ if (!this.isOrder(group.order.cell)) {
276
278
  throw new Error(`Match order ${outPoint} does not match this order manager`);
277
279
  }
278
280
  if (ckbOut < 0n) {
@@ -281,22 +283,6 @@ export class OrderManager implements ScriptDeps {
281
283
  if (udtOut < 0n) {
282
284
  throw new Error(`Match order ${outPoint} has negative UDT output`);
283
285
  }
284
- return { group: validated, ckbOut, udtOut };
286
+ return { group, ckbOut, udtOut };
285
287
  }
286
288
  }
287
-
288
- async function cachedTransactionResponse(
289
- client: ccc.Client,
290
- txHash: ccc.Hex,
291
- ): Promise<TransactionResponse | undefined> {
292
- // As CCC's own readers do, trust the cache only once the transaction is in a block: the
293
- // cache also holds what CCC records at broadcast and what a lagging node answered, and a
294
- // long-lived client would otherwise keep that answer for good.
295
- const cached = await client.cache.getTransactionResponse(txHash);
296
- if (cached?.blockNumber !== undefined) {
297
- return cached;
298
- }
299
-
300
- // CCC's read records a found response in the cache itself.
301
- return client.getTransaction(txHash);
302
- }
@@ -59,6 +59,9 @@ export interface OrderData {
59
59
  validate(): void;
60
60
  }
61
61
 
62
+ // An interface, this class expression and a typed const rather than one plain class: a
63
+ // class extending `ccc.Entity.Base<...>()` emits an unexported `*_base` declaration, which
64
+ // API Extractor's ae-forgotten-export rejects (tried 2026-10-30).
62
65
  // eslint-disable-next-line @typescript-eslint/no-shadow -- Preserve the runtime constructor name.
63
66
  const OrderDataImplementation = class OrderData extends OrderBase {
64
67
  static {
@@ -23,8 +23,6 @@ export interface Ratio extends ExchangeRatio {
23
23
  clone(): Ratio;
24
24
  /** Compares effective CKB-to-UDT prices, returning their numeric ordering. */
25
25
  compare(other: Ratio): number;
26
- /** Converts an amount in the selected direction, rounding up only when requested. */
27
- convert(isCkb2Udt: boolean, amount: ccc.FixedPoint, mustCeil: boolean): ccc.FixedPoint;
28
26
  /** Returns whether another value has the same scales. */
29
27
  eq(other: ExchangeRatio): boolean;
30
28
  /** Returns the CKB hash of the serialized ratio. */
@@ -41,6 +39,9 @@ export interface Ratio extends ExchangeRatio {
41
39
  validate(): void;
42
40
  }
43
41
 
42
+ // An interface, this class expression and a typed const rather than one plain class: a
43
+ // class extending `ccc.Entity.Base<...>()` emits an unexported `*_base` declaration, which
44
+ // API Extractor's ae-forgotten-export rejects (tried 2026-10-30).
44
45
  // eslint-disable-next-line @typescript-eslint/no-shadow -- Preserve the runtime constructor name.
45
46
  const RatioImplementation = class Ratio extends RatioBase {
46
47
  static {
@@ -111,24 +112,6 @@ const RatioImplementation = class Ratio extends RatioBase {
111
112
 
112
113
  return compareBigInt(this.ckbScale * other.udtScale, other.ckbScale * this.udtScale);
113
114
  }
114
-
115
- /** Converts an amount in the requested direction with optional ceiling. */
116
- public convert(
117
- isCkb2Udt: boolean,
118
- amount: ccc.FixedPoint,
119
- mustCeil: boolean,
120
- ): ccc.FixedPoint {
121
- if (!this.isPopulated()) {
122
- throw new Error("Invalid midpoint ExchangeRatio");
123
- }
124
-
125
- let { ckbScale: aScale, udtScale: bScale } = this;
126
- if (!isCkb2Udt) {
127
- [aScale, bScale] = [bScale, aScale];
128
- }
129
-
130
- return (amount * aScale + (mustCeil ? bScale - 1n : 0n)) / bScale;
131
- }
132
115
  };
133
116
 
134
117
  /** CCC-backed ratio constructor and codec. */
@@ -43,6 +43,9 @@ export interface Relative {
43
43
  validate(): void;
44
44
  }
45
45
 
46
+ // An interface, this class expression and a typed const rather than one plain class: a
47
+ // class extending `ccc.Entity.Base<...>()` emits an unexported `*_base` declaration, which
48
+ // API Extractor's ae-forgotten-export rejects (tried 2026-10-30).
46
49
  // eslint-disable-next-line @typescript-eslint/no-shadow -- Preserve the runtime constructor name.
47
50
  const RelativeImplementation = class Relative extends RelativeBase {
48
51
  static {