@ickb/sdk 1000.0.40 → 1000.0.52

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/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ccc, type Epoch } from "@ckb-ccc/core";
1
+ import { ccc } from "@ckb-ccc/core";
2
2
  import {
3
3
  collect,
4
4
  epochCompare,
@@ -10,6 +10,8 @@ import {
10
10
  epochAdd,
11
11
  asyncBinarySearch,
12
12
  RestrictedTransaction,
13
+ type ExchangeRatio,
14
+ binarySearch,
13
15
  } from "@ickb/utils";
14
16
  import {
15
17
  convert,
@@ -20,197 +22,288 @@ import {
20
22
  OwnedOwnerManager,
21
23
  type IckbDepositCell,
22
24
  type ReceiptCell,
23
- type WithdrawalGroups,
25
+ type WithdrawalGroup,
24
26
  } from "@ickb/core";
25
27
  import { DaoManager, type DaoCell } from "@ickb/dao";
26
28
  import {
27
29
  Info,
28
30
  OrderManager,
29
- Ratio,
30
31
  type OrderCell,
31
32
  type OrderGroup,
32
- type RatioLike,
33
33
  } from "@ickb/order";
34
34
 
35
+ /**
36
+ * Controller for managing iCKB operations.
37
+ */
35
38
  export class IckbController {
39
+ /**
40
+ * Creates an instance of IckbController.
41
+ * @param ickbUdtManager - The manager for iCKB UDT operations.
42
+ * @param logicManager - The logic manager for handling business logic.
43
+ * @param ownedOwnerManager - The manager for owned owner operations.
44
+ * @param orderManager - The manager for order operations.
45
+ * @param daoManager - The manager for DAO operations.
46
+ */
36
47
  constructor(
37
48
  public readonly ickbUdtManager: IckbUdtManager,
38
49
  public readonly logicManager: LogicManager,
39
50
  public readonly ownedOwnerManager: OwnedOwnerManager,
40
51
  public readonly orderManager: OrderManager,
41
52
  public readonly daoManager: DaoManager,
53
+ public readonly bots: ccc.Script[],
42
54
  ) {}
43
55
 
44
- static fromDeps({
45
- xudt,
46
- dao,
47
- ickbLogic,
48
- ownedOwner,
49
- order,
50
- }: {
51
- xudt: ScriptDeps;
52
- dao: ScriptDeps;
53
- ickbLogic: ScriptDeps;
54
- ownedOwner: ScriptDeps;
55
- order: ScriptDeps;
56
- }): IckbController {
57
- const daoManager = DaoManager.fromDeps(dao);
58
-
56
+ /**
57
+ * Creates an instance of IckbController from script dependencies.
58
+ * @param deps - The script dependencies.
59
+ * @param deps.udt - The script dependencies for UDT.
60
+ * @param deps.dao - The script dependencies for DAO.
61
+ * @param deps.ickbLogic - The script dependencies for iCKB logic.
62
+ * @param deps.ownedOwner - The script dependencies for owned owner.
63
+ * @param deps.order - The script dependencies for order.
64
+ * @returns An instance of IckbController.
65
+ */
66
+ static fromDeps(
67
+ deps: {
68
+ udt: ScriptDeps;
69
+ dao: ScriptDeps;
70
+ ickbLogic: ScriptDeps;
71
+ ownedOwner: ScriptDeps;
72
+ order: ScriptDeps;
73
+ },
74
+ bots: ccc.Script[],
75
+ ): IckbController {
59
76
  const {
60
- script: { codeHash, hashType },
61
- cellDeps,
62
- } = xudt;
63
- const ickbXudt = {
77
+ udt: {
78
+ script: { codeHash, hashType },
79
+ cellDeps,
80
+ },
81
+ ickbLogic: { script: ickbLogicScript },
82
+ } = deps;
83
+
84
+ const daoManager = DaoManager.fromDeps(deps);
85
+ const udt = {
64
86
  script: new ccc.Script(
65
87
  codeHash,
66
88
  hashType,
67
- [ickbLogic.script.hash(), "00000080"].join("") as ccc.Hex,
89
+ [ickbLogicScript.hash(), "00000080"].join("") as ccc.Hex,
68
90
  ),
69
91
  cellDeps,
70
92
  };
71
- const ickbUdtManager = IckbUdtManager.fromDeps(ickbXudt, daoManager);
93
+ const ickbUdtManager = IckbUdtManager.fromDeps({ udt }, daoManager);
72
94
 
73
95
  return new IckbController(
74
96
  ickbUdtManager,
75
- LogicManager.fromDeps(ickbLogic, daoManager, ickbUdtManager),
76
- OwnedOwnerManager.fromDeps(ownedOwner, daoManager, ickbUdtManager),
77
- OrderManager.fromDeps(order, ickbUdtManager),
97
+ LogicManager.fromDeps(deps, daoManager, ickbUdtManager),
98
+ OwnedOwnerManager.fromDeps(deps, daoManager, ickbUdtManager),
99
+ OrderManager.fromDeps(deps, ickbUdtManager),
78
100
  daoManager,
101
+ bots,
79
102
  );
80
103
  }
81
104
 
82
- previewConversion(
83
- isCkb2Udt: boolean,
84
- amount: ccc.FixedPoint,
85
- rate: RatioLike | ccc.ClientBlockHeader,
86
- fee?: {
87
- numerator: ccc.Num;
88
- denominator: ccc.Num;
89
- },
90
- ): ccc.FixedPoint {
91
- let convertedAmount = convert(isCkb2Udt, amount, rate);
105
+ /**
106
+ * Previews the conversion between CKB and UDT.
107
+ * @param conversion - The conversion parameters.
108
+ * @param conversion.isCkb2Udt - Indicates if the conversion is from CKB to UDT.
109
+ * @param conversion.amount - The amount to convert.
110
+ * @param conversion.rate - The exchange rate for conversion.
111
+ * @param conversion.fee - The fee for the conversion.
112
+ * @returns The rate and converted amount.
113
+ *
114
+ * Fee meaning examples:
115
+ * - 100000n => 0.001%
116
+ * - 10000n => 0.01%
117
+ * - 1000n => 0.1%
118
+ * - 100n => 1%
119
+ * - 10n => 10%
120
+ * - 1n => 100%
121
+ * - less than 1n => 0%
122
+ */
123
+ static previewConversion(conversion: {
124
+ isCkb2Udt: boolean;
125
+ amount: ccc.FixedPoint;
126
+ rate: ExchangeRatio | ccc.ClientBlockHeader;
127
+ fee: ccc.Num;
128
+ }): { rate: ExchangeRatio; convertedAmount: ccc.FixedPoint } {
129
+ const { isCkb2Udt, amount, fee } = conversion;
130
+ let rate = conversion.rate;
131
+ rate = "dao" in rate ? ickbExchangeRatio(rate) : rate;
92
132
 
93
- if (fee) {
94
- const { numerator, denominator } = fee;
95
- convertedAmount -= (convertedAmount * numerator) / denominator;
133
+ if (fee > ccc.Zero) {
134
+ const { ckbScale, udtScale } = rate;
135
+ rate = {
136
+ ckbScale,
137
+ // Worst case scenario where fee applies to the full amount
138
+ udtScale: udtScale + (isCkb2Udt ? 1n : -1n) * (udtScale / fee),
139
+ };
96
140
  }
97
141
 
98
- return convertedAmount;
142
+ const convertedAmount =
143
+ amount > ccc.Zero ? convert(isCkb2Udt, amount, rate) : ccc.Zero;
144
+
145
+ return { rate, convertedAmount };
99
146
  }
100
147
 
101
- // TODO streamline parameters with previewConversion
148
+ /**
149
+ * Converts between CKB and UDT based on the provided options.
150
+ * @param options - The conversion options.
151
+ * @param options.conversion - The conversion parameters.
152
+ * @param options.conversion.isCkb2Udt - Indicates if the conversion is from CKB to UDT.
153
+ * @param options.conversion.amount - The amount to convert.
154
+ * @param options.conversion.rate - The exchange rate for conversion.
155
+ * @param options.conversion.ckbMinMatchLog - Minimum CKB match log (optional).
156
+ * @param options.conversion.ckbMinFee - Minimum CKB fee (optional).
157
+ * @param options.signer - The signer for the transaction.
158
+ * @param options.userCells - The user's cells.
159
+ * @param options.system - The current system state.
160
+ * @param options.filters - The filters for the conversion.
161
+ * @param options.filters.isReadyOnly - Indicates if only ready conversions should be considered.
162
+ * @param options.filters.isFulfilledOnly - Indicates if only fulfilled conversions should be considered.
163
+ * @returns A promise that resolves to the conversion result or undefined.
164
+ */
102
165
  async convert(options: {
166
+ conversion: Parameters<typeof IckbController.previewConversion>[0] & {
167
+ ckbMinMatchLog?: number;
168
+ ckbMinFee?: ccc.Num;
169
+ };
103
170
  signer: ccc.Signer;
104
- isCkb2Udt: boolean;
105
- amount: ccc.FixedPoint;
106
171
  userCells: UserCells;
107
- minCkbChange: ccc.FixedPoint;
108
172
  system: SystemState;
109
- balance: Balance;
110
- fee?: {
111
- numerator: ccc.Num;
112
- denominator: ccc.Num;
173
+ filters: {
174
+ isReadyOnly: boolean;
175
+ isFulfilledOnly: boolean;
113
176
  };
114
177
  }): Promise<Conversion | undefined> {
115
- const { isCkb2Udt, amount, system, signer } = options;
116
- // TODO: RatioLike and specify fee!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
117
- // Or let outside specify adjusted rate...
118
- const info = Info.create(isCkb2Udt, Ratio.from(system.exchangeRatio));
119
- const address = await signer.getRecommendedAddressObj();
178
+ // Get user lock
179
+ const { system, signer } = options;
180
+ const lock = (await signer.getRecommendedAddressObj()).script;
181
+
182
+ // Normalize rate
183
+ const { rate } = IckbController.previewConversion({
184
+ ...options.conversion,
185
+ amount: ccc.Zero,
186
+ });
187
+ const { isCkb2Udt, amount, ckbMinMatchLog } = options.conversion;
188
+ const info = Info.create(isCkb2Udt, rate, ckbMinMatchLog);
189
+ // feeRate is a good approximation of the bot tx size matching the order
190
+ const ckbMinFee = options.conversion.ckbMinFee ?? 10n * system.feeRate;
191
+ const conversion = {
192
+ isCkb2Udt,
193
+ amount,
194
+ rate,
195
+ info,
196
+ ckbMinFee,
197
+ lock,
198
+ };
199
+
120
200
  const N = isCkb2Udt
121
201
  ? Number(amount / system.ckbDepositCap)
122
- : system.deposits.slice(0, 30).length;
202
+ : system.depositPool.slice(0, 30).length;
203
+
123
204
  const cache = Array<ReturnType<IckbController["convert"]>>(N);
124
- const attempt = (n: number): ReturnType<IckbController["convert"]> => {
205
+ const cachedAttempt = (
206
+ n: number,
207
+ ): ReturnType<IckbController["convert"]> => {
125
208
  n = N - n;
126
- return (cache[n] =
127
- cache[n] ?? this.attemptConversion(n, info, address, options));
209
+ return (cache[n] = cache[n] ?? this.attempt(n, conversion, options));
128
210
  };
129
- return attempt(
211
+
212
+ return cachedAttempt(
130
213
  await asyncBinarySearch(
131
214
  N,
132
- async (n: number) => (await attempt(n)) !== undefined,
215
+ async (n: number) => (await cachedAttempt(n)) !== undefined,
133
216
  ),
134
217
  );
135
218
  }
136
219
 
137
- private async attemptConversion(
220
+ /**
221
+ * Attempts to perform a conversion based on the provided parameters.
222
+ * @param n - The size of deposit to make or withdraw from.
223
+ * @param conversion - The conversion parameters.
224
+ * @param conversion.isCkb2Udt - Indicates if the conversion is from CKB to UDT.
225
+ * @param conversion.amount - The amount to convert.
226
+ * @param conversion.rate - The exchange rate for conversion.
227
+ * @param conversion.info - Additional information for the conversion.
228
+ * @param conversion.ckbMinFee - Minimum CKB fee.
229
+ * @param conversion.lock - The script lock for the transaction.
230
+ * @param options - Additional options for the conversion.
231
+ * @returns A promise that resolves to the conversion result or undefined.
232
+ */
233
+ private async attempt(
138
234
  n: number,
139
- info: Info,
140
- address: ccc.Address,
141
- {
142
- signer,
143
- isCkb2Udt,
144
- amount,
145
- userCells,
146
- system: { feeRate, tip, deposits, ckbDepositCap, exchangeRatio },
147
- }: Parameters<IckbController["convert"]>[0],
235
+ conversion: {
236
+ isCkb2Udt: boolean;
237
+ amount: bigint;
238
+ rate: ExchangeRatio;
239
+ info: Info;
240
+ ckbMinFee: ccc.Num;
241
+ lock: ccc.Script;
242
+ },
243
+ options: Omit<Parameters<IckbController["convert"]>[0], "conversion">,
148
244
  ): ReturnType<IckbController["convert"]> {
149
245
  try {
246
+ const { isCkb2Udt, rate, info, ckbMinFee, lock } = conversion;
247
+ const { signer, userCells, filters, system } = options;
248
+ const { feeRate, ckbDepositCap, exchangeRatio, depositPool } = system;
249
+
150
250
  const tx = SmartTransaction.default();
151
- const maturities: Epoch[] = [];
251
+ const maturities: ccc.Epoch[] = [[0n, 1n, 80n]]; // 3 minutes minimum
152
252
  let ckbFee = ccc.Zero;
253
+ let amount = conversion.amount;
254
+ let udtWithdrawed = ccc.Zero;
153
255
 
256
+ // Core protocol
154
257
  if (n > 0) {
155
258
  if (isCkb2Udt) {
156
259
  amount -= ckbDepositCap * BigInt(n);
157
- if (amount < 0n) {
260
+ if (amount < ccc.Zero) {
158
261
  // Too many Deposits respectfully to the amount
159
262
  return undefined;
160
263
  }
161
- this.logicManager.deposit(tx, n, ckbDepositCap, address.script);
264
+ this.logicManager.deposit(tx, n, ckbDepositCap, lock);
162
265
  } else {
163
- const myDeposits = deposits.slice(0, n);
266
+ const myDeposits = depositPool.slice(0, n);
164
267
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
165
- amount -= myDeposits.slice(-1)[0]!.udtCumulative;
166
- if (amount < 0n) {
268
+ udtWithdrawed = myDeposits.slice(-1)[0]!.udtCumulative;
269
+ amount -= udtWithdrawed;
270
+ if (amount < ccc.Zero) {
167
271
  // Too many Withdrawal Requests respectfully to the amount
168
272
  return undefined;
169
273
  }
170
- this.ownedOwnerManager.requestWithdrawal(
171
- tx,
172
- myDeposits,
173
- address.script,
174
- );
274
+ this.ownedOwnerManager.requestWithdrawal(tx, myDeposits, lock);
175
275
  maturities.push(...myDeposits.map((d) => d.maturity));
176
276
  }
177
277
  }
178
278
 
179
- if (amount > 0n) {
180
- this.orderManager.mint(
181
- tx,
182
- address.script,
183
- info,
184
- isCkb2Udt ? amount : ccc.Zero,
185
- isCkb2Udt ? ccc.Zero : amount,
186
- );
279
+ // Order protocol
280
+ if (amount > ccc.Zero) {
281
+ this.orderManager.mint(tx, lock, info, {
282
+ ckbValue: isCkb2Udt ? amount : ccc.Zero,
283
+ udtValue: isCkb2Udt ? ccc.Zero : amount,
284
+ });
187
285
 
188
- const ratio = isCkb2Udt ? info.ckbToUdt : info.udtToCkb;
189
- const convertedAmount = convert(isCkb2Udt, amount, ratio);
286
+ const convertedAmount = convert(isCkb2Udt, amount, rate);
190
287
  ckbFee = isCkb2Udt
191
288
  ? amount - convert(false, convertedAmount, exchangeRatio)
192
289
  : convert(true, amount, exchangeRatio) - convertedAmount;
193
290
 
194
291
  maturities.push(
195
- // TODO: use real world data from deposits, open orders and bot balances
196
- orderMaturityEstimate(isCkb2Udt, amount, tip),
292
+ orderMaturityEstimate(isCkb2Udt, amount, udtWithdrawed, system),
197
293
  );
198
- }
199
294
 
200
- // Check that order provides enough fee to the bot for being matched
201
- // feeRate is a good approximation of the bot tx size matching the order
202
- if (10n * (feeRate - ckbFee) > ckbFee) {
203
- return undefined;
295
+ // Check that order provides enough fee to the bot for being matched
296
+ if (ckbFee < ckbMinFee) {
297
+ return undefined;
298
+ }
204
299
  }
205
300
 
206
301
  const { receipts, withdrawalGroups, orders, udts, capacities } =
207
302
  userCells;
208
303
 
209
304
  this.logicManager.completeDeposit(tx, receipts);
210
- this.ownedOwnerManager.withdraw(tx, withdrawalGroups, {
211
- isReadyOnly: true,
212
- });
213
- this.orderManager.melt(tx, orders);
305
+ this.ownedOwnerManager.withdraw(tx, withdrawalGroups, filters);
306
+ this.orderManager.melt(tx, orders, filters);
214
307
  this.ickbUdtManager.addUdts(tx, udts);
215
308
  CapacityManager.addCapacities(tx, capacities);
216
309
 
@@ -234,9 +327,19 @@ export class IckbController {
234
327
  }
235
328
  }
236
329
 
237
- // TODO: Allow filtering which cells are fetched
330
+ /**
331
+ * Retrieves the L1 state based on the provided parameters.
332
+ * @param client - The client to interact with the blockchain.
333
+ * @param filter - The set of filters to apply.
334
+ * @param locks - The script locks to filter the user cells.
335
+ * @param options - Optional parameters for the state retrieval.
336
+ * @param options.minLockUp - The minimum lock-up period (optional).
337
+ * @param options.maxLockUp - The maximum lock-up period (optional).
338
+ * @returns A promise that resolves to an object containing the system state and user cells.
339
+ */
238
340
  async getL1State(
239
341
  client: ccc.Client,
342
+ filter: Set<keyof UserCells | "depositPool" | "ckbBots">,
240
343
  locks: ccc.Script[],
241
344
  options?: {
242
345
  minLockUp?: ccc.Epoch;
@@ -259,24 +362,43 @@ export class IckbController {
259
362
  orders,
260
363
  deposits,
261
364
  withdrawalRequests,
365
+ botCapacities,
262
366
  ] = await Promise.all([
263
367
  client.getFeeRate(),
264
- collect(this.logicManager.findDeposits(client, opts)),
265
- collect(CapacityManager.findCapacities(client, locks, opts)),
266
- collect(this.ickbUdtManager.findUdts(client, locks, opts)),
267
- collect(this.logicManager.findReceipts(client, locks, opts)),
268
- collect(this.ownedOwnerManager.findWithdrawalGroups(client, locks, opts)),
269
- collect(this.orderManager.findOrders(client)),
270
- collect(this.daoManager.findDeposits(client, locks, opts)),
271
- collect(this.daoManager.findWithdrawalRequests(client, locks, opts)),
272
- // TODO: fetch bot cells for estimating timings
368
+ filter.has("depositPool")
369
+ ? collect(this.logicManager.findDeposits(client, opts))
370
+ : [],
371
+ filter.has("capacities")
372
+ ? collect(CapacityManager.findCapacities(client, locks, opts))
373
+ : [],
374
+ filter.has("udts")
375
+ ? collect(this.ickbUdtManager.findUdts(client, locks, opts))
376
+ : [],
377
+ filter.has("receipts")
378
+ ? collect(this.logicManager.findReceipts(client, locks, opts))
379
+ : [],
380
+ filter.has("withdrawalGroups")
381
+ ? collect(
382
+ this.ownedOwnerManager.findWithdrawalGroups(client, locks, opts),
383
+ )
384
+ : [],
385
+ filter.has("orders") ? collect(this.orderManager.findOrders(client)) : [],
386
+ filter.has("deposits")
387
+ ? collect(this.daoManager.findDeposits(client, locks, opts))
388
+ : [],
389
+ filter.has("withdrawalRequests")
390
+ ? collect(this.daoManager.findWithdrawalRequests(client, locks, opts))
391
+ : [],
392
+ filter.has("ckbBots")
393
+ ? collect(CapacityManager.findCapacities(client, locks, opts))
394
+ : [],
273
395
  ]);
274
396
 
275
397
  // Sort depositPool by maturity
276
398
  depositPool.sort((a, b) => epochCompare(a.maturity, b.maturity));
277
399
  // Calculate cumulative value of deposits in the pool
278
400
  let udtCumulative = 0n;
279
- const cumulativeDepositPool: SystemState["deposits"] = [];
401
+ const cumulativeDepositPool: SystemState["depositPool"] = [];
280
402
  for (const deposit of depositPool) {
281
403
  udtCumulative += deposit.udtValue;
282
404
  cumulativeDepositPool.push({ ...deposit, udtCumulative });
@@ -295,6 +417,12 @@ export class IckbController {
295
417
  const exchangeRatio = ickbExchangeRatio(tip);
296
418
  const ckbDepositCap = convert(false, ICKB_DEPOSIT_CAP, exchangeRatio);
297
419
 
420
+ // Calculate the ckb ready to match incoming transactions
421
+ let ckbBots = botCapacities.reduce((acc, c) => acc + c.ckbValue, ccc.Zero);
422
+ // Each bot keeps a reserve of CKB for operations
423
+ ckbBots -= ccc.numFrom(this.bots.length) * ccc.fixedPointFrom("2000");
424
+ ckbBots = ckbBots > ccc.Zero ? ckbBots : ccc.Zero;
425
+
298
426
  return {
299
427
  system: {
300
428
  feeRate,
@@ -302,8 +430,9 @@ export class IckbController {
302
430
  exchangeRatio,
303
431
  ckbDepositCap,
304
432
  udtDepositCap: ICKB_DEPOSIT_CAP,
305
- deposits: cumulativeDepositPool,
306
- orders: nonUserOrders,
433
+ depositPool: cumulativeDepositPool,
434
+ orderPool: nonUserOrders,
435
+ ckbBots,
307
436
  },
308
437
  user: {
309
438
  capacities,
@@ -316,84 +445,129 @@ export class IckbController {
316
445
  },
317
446
  };
318
447
  }
319
-
320
- // TODO: Allow filtering which cells are accounted for
321
- static getBalance({
322
- capacities,
323
- udts,
324
- receipts,
325
- withdrawalGroups,
326
- orders,
327
- }: UserCells): Balance {
328
- const cells: { cell: ccc.Cell; udtValue?: ccc.FixedPoint }[] = [
329
- capacities,
330
- udts,
331
- receipts,
332
- ].flat();
333
- for (const { owner, owned } of withdrawalGroups) {
334
- cells.push(owned, owner);
335
- }
336
- for (const { master, order } of orders) {
337
- cells.push(master, order);
338
- }
339
- let ckb = ccc.Zero;
340
- let udt = ccc.Zero;
341
- for (const cell of cells) {
342
- ckb += cell.cell.cellOutput.capacity;
343
- udt += cell.udtValue ?? ccc.Zero;
344
- }
345
- return {
346
- ckb,
347
- udt,
348
- };
349
- }
350
- }
351
-
352
- export interface Balance {
353
- ckb: ccc.FixedPoint;
354
- udt: ccc.FixedPoint;
355
448
  }
356
449
 
450
+ /**
451
+ * Represents the state of the system.
452
+ */
357
453
  export interface SystemState {
454
+ /** The fee rate for transactions. */
358
455
  feeRate: ccc.Num;
456
+
457
+ /** The tip for the current block header. */
359
458
  tip: ccc.ClientBlockHeader;
360
- exchangeRatio: {
361
- ckbScale: ccc.Num;
362
- udtScale: ccc.Num;
363
- };
459
+
460
+ /** The exchange ratio between CKB and UDT. */
461
+ exchangeRatio: ExchangeRatio;
462
+
463
+ /** The deposit cap expressed in CKB. */
364
464
  ckbDepositCap: ccc.FixedPoint;
465
+
466
+ /** The deposit cap expressed in iCKB. */
365
467
  udtDepositCap: ccc.FixedPoint;
366
- deposits: (IckbDepositCell & { udtCumulative: bigint })[];
367
- orders: OrderCell[];
468
+
469
+ /**
470
+ * The deposit pool containing iCKB deposit cells with cumulative iCKB amounts.
471
+ */
472
+ depositPool: (IckbDepositCell & { udtCumulative: bigint })[];
473
+
474
+ /**
475
+ * The order pool containing non-user order cells.
476
+ */
477
+ orderPool: OrderCell[];
478
+
479
+ /**
480
+ * The ckb held by bots ready to match incoming iCKB to CKB conversions.
481
+ */
482
+ ckbBots: ccc.FixedPoint;
368
483
  }
369
484
 
485
+ /**
486
+ * Represents the user cells in the system.
487
+ */
370
488
  export interface UserCells {
489
+ /** The capacities owned by the user. */
371
490
  capacities: CapacityCell[];
491
+
492
+ /** The UDTs owned by the user. */
372
493
  udts: UdtCell[];
494
+
495
+ /** The receipts associated with the user's transactions. */
373
496
  receipts: ReceiptCell[];
374
- withdrawalGroups: WithdrawalGroups[];
497
+
498
+ /** The withdrawal groups for the user. */
499
+ withdrawalGroups: WithdrawalGroup[];
500
+
501
+ /** The orders placed by the user. */
375
502
  orders: OrderGroup[];
503
+
504
+ /** The classical/non-iCKB deposits made by the user. */
376
505
  deposits: DaoCell[];
506
+
507
+ /** The classical/non-iCKB withdrawal requests made by the user. */
377
508
  withdrawalRequests: DaoCell[];
378
509
  }
379
510
 
511
+ /**
512
+ * Represents a conversion transaction.
513
+ */
380
514
  export interface Conversion {
515
+ /** The smart transaction associated with the conversion. */
381
516
  tx: SmartTransaction;
517
+
518
+ /** The fee in CKB for the order created by the conversion. */
382
519
  ckbFee: ccc.FixedPoint;
383
- maturity: Epoch;
520
+
521
+ /** The estimated maturity epoch for conversion. */
522
+ maturity: ccc.Epoch;
384
523
  }
385
524
 
386
- // Estimate bot ability to fulfill orders:
387
- // - CKB to iCKB orders at 100k CKB every minute
388
- // - iCKB to CKB orders at 200 CKB every minute
525
+ /**
526
+ * Estimates the maturity of an order based on the type of conversion and amount.
527
+ *
528
+ * @param isCkb2Udt - A boolean indicating if the order is from CKB to UDT.
529
+ * @param amount - The amount of CKB or UDT involved in the order.
530
+ * @param udtWithdrawed - The amount of UDT that has been withdrawn by the current conversion via core.
531
+ * @param system - The current system state, which includes the block header tip, order pool, deposit pool, CKB bots, and exchange ratio.
532
+ *
533
+ * @returns The estimated maturity epoch for the order.
534
+ */
389
535
  function orderMaturityEstimate(
390
536
  isCkb2Udt: boolean,
391
537
  amount: bigint,
392
- tip: ccc.ClientBlockHeader,
538
+ udtWithdrawed: ccc.FixedPoint,
539
+ system: SystemState,
393
540
  ): ccc.Epoch {
394
- return epochAdd(tip.epoch, [
541
+ const { tip, orderPool, depositPool, ckbBots, exchangeRatio } = system;
542
+
543
+ // Maturity estimate a priori, the estimation is based on:
544
+ // - CKB to iCKB orders at 100k CKB every minute
545
+ // - iCKB to CKB orders at 200 CKB every minute
546
+ let maturity = epochAdd(tip.epoch, [
395
547
  0n,
396
548
  1n + amount / ccc.fixedPointFrom(isCkb2Udt ? 100000 : 200),
397
549
  4n * 60n,
398
550
  ]);
551
+
552
+ if (isCkb2Udt) {
553
+ return maturity;
554
+ }
555
+
556
+ if (depositPool.length > 0) {
557
+ const udtWaiting = orderPool
558
+ .filter((o) => o.isUdt2CkbMatchable())
559
+ .reduce((acc, o) => acc + o.udtValue, ccc.Zero);
560
+ const udtAvailable = convert(isCkb2Udt, ckbBots, exchangeRatio);
561
+
562
+ const udtToConvert = amount + udtWithdrawed + udtWaiting - udtAvailable;
563
+ const i = binarySearch(
564
+ depositPool.length,
565
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
566
+ (n) => depositPool[n]!.udtCumulative >= udtToConvert,
567
+ );
568
+
569
+ maturity = depositPool[i]?.maturity ?? maturity;
570
+ }
571
+
572
+ return maturity;
399
573
  }