@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.
- package/README.md +14 -11
- package/dist/constants.d.ts +0 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +6 -24
- package/dist/constants.js.map +1 -1
- package/dist/conversion/estimate.d.ts +0 -6
- package/dist/conversion/estimate.d.ts.map +1 -1
- package/dist/conversion/estimate.js +1 -1
- package/dist/conversion/estimate.js.map +1 -1
- package/dist/conversion/maturity.d.ts +1 -1
- package/dist/conversion/maturity.js +13 -12
- package/dist/conversion/maturity.js.map +1 -1
- package/dist/conversion/plans.d.ts.map +1 -1
- package/dist/conversion/plans.js +6 -6
- package/dist/conversion/plans.js.map +1 -1
- package/dist/conversion/projection.d.ts +5 -6
- package/dist/conversion/projection.d.ts.map +1 -1
- package/dist/conversion/projection.js +11 -8
- package/dist/conversion/projection.js.map +1 -1
- package/dist/conversion/types.d.ts +29 -17
- package/dist/conversion/types.d.ts.map +1 -1
- package/dist/conversion/withdrawal_ring.d.ts +1 -3
- package/dist/conversion/withdrawal_ring.d.ts.map +1 -1
- package/dist/conversion/withdrawal_ring.js +4 -4
- package/dist/conversion/withdrawal_ring.js.map +1 -1
- package/dist/dao.d.ts +19 -5
- package/dist/dao.d.ts.map +1 -1
- package/dist/dao.js +24 -12
- package/dist/dao.js.map +1 -1
- package/dist/index.d.ts +9 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/logic.d.ts +10 -14
- package/dist/logic.d.ts.map +1 -1
- package/dist/logic.js +28 -39
- package/dist/logic.js.map +1 -1
- package/dist/order/cells.d.ts +10 -49
- package/dist/order/cells.d.ts.map +1 -1
- package/dist/order/cells.js +28 -146
- package/dist/order/cells.js.map +1 -1
- package/dist/order/conversion.d.ts +3 -9
- package/dist/order/conversion.d.ts.map +1 -1
- package/dist/order/conversion.js +8 -11
- package/dist/order/conversion.js.map +1 -1
- package/dist/order/fill.d.ts +2 -2
- package/dist/order/fill.js +3 -3
- package/dist/order/fill.js.map +1 -1
- package/dist/order/info.d.ts.map +1 -1
- package/dist/order/info.js +3 -0
- package/dist/order/info.js.map +1 -1
- package/dist/order/matcher.d.ts +1 -1
- package/dist/order/matcher.d.ts.map +1 -1
- package/dist/order/matcher.js +3 -2
- package/dist/order/matcher.js.map +1 -1
- package/dist/order/order.d.ts +6 -2
- package/dist/order/order.d.ts.map +1 -1
- package/dist/order/order.js +32 -37
- package/dist/order/order.js.map +1 -1
- package/dist/order/order_data.d.ts.map +1 -1
- package/dist/order/order_data.js +3 -0
- package/dist/order/order_data.js.map +1 -1
- package/dist/order/ratio.d.ts +0 -2
- package/dist/order/ratio.d.ts.map +1 -1
- package/dist/order/ratio.js +3 -11
- package/dist/order/ratio.js.map +1 -1
- package/dist/order/relative.d.ts.map +1 -1
- package/dist/order/relative.js +3 -0
- package/dist/order/relative.js.map +1 -1
- package/dist/owned_owner.d.ts +10 -15
- package/dist/owned_owner.d.ts.map +1 -1
- package/dist/owned_owner.js +27 -47
- package/dist/owned_owner.js.map +1 -1
- package/dist/sdk.d.ts +74 -31
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +81 -59
- package/dist/sdk.js.map +1 -1
- package/dist/send/sign_and_send_transaction.d.ts +32 -17
- package/dist/send/sign_and_send_transaction.d.ts.map +1 -1
- package/dist/send/sign_and_send_transaction.js +128 -28
- package/dist/send/sign_and_send_transaction.js.map +1 -1
- package/dist/send/wait_transaction.d.ts +7 -6
- package/dist/send/wait_transaction.d.ts.map +1 -1
- package/dist/send/wait_transaction.js +29 -61
- package/dist/send/wait_transaction.js.map +1 -1
- package/dist/udt.d.ts +20 -35
- package/dist/udt.d.ts.map +1 -1
- package/dist/udt.js +39 -103
- package/dist/udt.js.map +1 -1
- package/dist/utils/chain.d.ts +0 -35
- package/dist/utils/chain.d.ts.map +1 -1
- package/dist/utils/chain.js +1 -19
- package/dist/utils/chain.js.map +1 -1
- package/dist/utils/codec.d.ts.map +1 -1
- package/dist/utils/codec.js +3 -0
- package/dist/utils/codec.js.map +1 -1
- package/dist/utils/utils.d.ts +19 -46
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +53 -74
- package/dist/utils/utils.js.map +1 -1
- package/dist/utils/verified_client.d.ts +19 -0
- package/dist/utils/verified_client.d.ts.map +1 -0
- package/dist/utils/verified_client.js +92 -0
- package/dist/utils/verified_client.js.map +1 -0
- package/package.json +2 -2
- package/src/constants.ts +6 -25
- package/src/conversion/estimate.ts +1 -3
- package/src/conversion/maturity.ts +13 -14
- package/src/conversion/plans.ts +6 -6
- package/src/conversion/projection.ts +15 -7
- package/src/conversion/types.ts +32 -17
- package/src/conversion/withdrawal_ring.ts +4 -8
- package/src/dao.ts +38 -14
- package/src/index.ts +8 -8
- package/src/logic.ts +40 -52
- package/src/order/cells.ts +37 -174
- package/src/order/conversion.ts +8 -19
- package/src/order/fill.ts +3 -3
- package/src/order/info.ts +3 -0
- package/src/order/matcher.ts +3 -3
- package/src/order/order.ts +38 -52
- package/src/order/order_data.ts +3 -0
- package/src/order/ratio.ts +3 -20
- package/src/order/relative.ts +3 -0
- package/src/owned_owner.ts +47 -77
- package/src/sdk.ts +95 -108
- package/src/send/sign_and_send_transaction.ts +164 -28
- package/src/send/wait_transaction.ts +44 -88
- package/src/udt.ts +56 -124
- package/src/utils/chain.ts +0 -41
- package/src/utils/codec.ts +4 -0
- package/src/utils/utils.ts +76 -105
- package/src/utils/verified_client.ts +136 -0
- package/dist/utils/transaction_header.d.ts +0 -17
- package/dist/utils/transaction_header.d.ts.map +0 -1
- package/dist/utils/transaction_header.js +0 -44
- package/dist/utils/transaction_header.js.map +0 -1
- package/src/utils/transaction_header.ts +0 -66
package/src/order/cells.ts
CHANGED
|
@@ -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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
|
385
|
-
public readonly blockNumber: ccc.Num
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
460
|
-
this.
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
}
|
package/src/order/conversion.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
|
-
import {
|
|
3
|
-
|
|
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
|
-
|
|
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 -
|
|
56
|
-
:
|
|
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
|
-
|
|
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 =
|
|
139
|
+
term = ccc.numMin(term, (maxTerm - previousP) / p);
|
|
151
140
|
}
|
|
152
141
|
if (q !== 0n) {
|
|
153
|
-
term =
|
|
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
|
|
79
|
-
*
|
|
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
|
|
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 {
|
package/src/order/matcher.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ceilDiv
|
|
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:
|
|
170
|
+
bMinMatch: ccc.numMin(bMinMatch, bMaxMatch),
|
|
171
171
|
bMaxMatch,
|
|
172
172
|
bMaxOut,
|
|
173
173
|
};
|
package/src/order/order.ts
CHANGED
|
@@ -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
|
-
|
|
128
|
-
if (selected.length === 0) {
|
|
119
|
+
if (groups.length === 0) {
|
|
129
120
|
return tx;
|
|
130
121
|
}
|
|
131
|
-
for (const group of
|
|
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
|
|
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
|
-
|
|
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) =>
|
|
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
|
|
209
|
-
const order =
|
|
210
|
-
if (
|
|
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,
|
|
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
|
|
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,
|
|
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<
|
|
231
|
+
): Promise<OrderCell | undefined> {
|
|
231
232
|
const { txHash, index: masterIndex } = master;
|
|
232
|
-
|
|
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
|
|
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
|
|
274
|
-
|
|
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
|
|
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
|
-
}
|
package/src/order/order_data.ts
CHANGED
|
@@ -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 {
|
package/src/order/ratio.ts
CHANGED
|
@@ -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. */
|
package/src/order/relative.ts
CHANGED
|
@@ -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 {
|