@ickb/sdk 1000.0.81 → 9000.0.1

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 (157) hide show
  1. package/README.md +81 -32
  2. package/dist/constants.d.ts +13 -22
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +80 -76
  5. package/dist/constants.js.map +1 -1
  6. package/dist/conversion/estimate.d.ts +39 -0
  7. package/dist/conversion/estimate.d.ts.map +1 -0
  8. package/dist/conversion/estimate.js +117 -0
  9. package/dist/conversion/estimate.js.map +1 -0
  10. package/dist/conversion/maturity.d.ts +15 -0
  11. package/dist/conversion/maturity.d.ts.map +1 -0
  12. package/dist/conversion/maturity.js +112 -0
  13. package/dist/conversion/maturity.js.map +1 -0
  14. package/dist/conversion/plans.d.ts +20 -0
  15. package/dist/conversion/plans.d.ts.map +1 -0
  16. package/dist/conversion/plans.js +106 -0
  17. package/dist/conversion/plans.js.map +1 -0
  18. package/dist/conversion/projection.d.ts +22 -0
  19. package/dist/conversion/projection.d.ts.map +1 -0
  20. package/dist/conversion/projection.js +91 -0
  21. package/dist/conversion/projection.js.map +1 -0
  22. package/dist/conversion/types.d.ts +252 -0
  23. package/dist/conversion/types.d.ts.map +1 -0
  24. package/dist/conversion/types.js +2 -0
  25. package/dist/conversion/types.js.map +1 -0
  26. package/dist/conversion/withdrawal_ring.d.ts +36 -0
  27. package/dist/conversion/withdrawal_ring.d.ts.map +1 -0
  28. package/dist/conversion/withdrawal_ring.js +87 -0
  29. package/dist/conversion/withdrawal_ring.js.map +1 -0
  30. package/dist/dao.d.ts +86 -0
  31. package/dist/dao.d.ts.map +1 -0
  32. package/dist/dao.js +113 -0
  33. package/dist/dao.js.map +1 -0
  34. package/dist/index.d.ts +51 -3
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +20 -3
  37. package/dist/index.js.map +1 -1
  38. package/dist/logic.d.ts +85 -0
  39. package/dist/logic.d.ts.map +1 -0
  40. package/dist/logic.js +184 -0
  41. package/dist/logic.js.map +1 -0
  42. package/dist/order/cells.d.ts +207 -0
  43. package/dist/order/cells.d.ts.map +1 -0
  44. package/dist/order/cells.js +427 -0
  45. package/dist/order/cells.js.map +1 -0
  46. package/dist/order/conversion.d.ts +43 -0
  47. package/dist/order/conversion.d.ts.map +1 -0
  48. package/dist/order/conversion.js +123 -0
  49. package/dist/order/conversion.js.map +1 -0
  50. package/dist/order/fee.d.ts +9 -0
  51. package/dist/order/fee.d.ts.map +1 -0
  52. package/dist/order/fee.js +24 -0
  53. package/dist/order/fee.js.map +1 -0
  54. package/dist/order/fill.d.ts +36 -0
  55. package/dist/order/fill.d.ts.map +1 -0
  56. package/dist/order/fill.js +53 -0
  57. package/dist/order/fill.js.map +1 -0
  58. package/dist/order/info.d.ts +62 -0
  59. package/dist/order/info.d.ts.map +1 -0
  60. package/dist/order/info.js +99 -0
  61. package/dist/order/info.js.map +1 -0
  62. package/dist/order/master.d.ts +49 -0
  63. package/dist/order/master.d.ts.map +1 -0
  64. package/dist/order/master.js +27 -0
  65. package/dist/order/master.js.map +1 -0
  66. package/dist/order/matcher.d.ts +43 -0
  67. package/dist/order/matcher.d.ts.map +1 -0
  68. package/dist/order/matcher.js +123 -0
  69. package/dist/order/matcher.js.map +1 -0
  70. package/dist/order/order.d.ts +59 -0
  71. package/dist/order/order.d.ts.map +1 -0
  72. package/dist/order/order.js +239 -0
  73. package/dist/order/order.js.map +1 -0
  74. package/dist/order/order_data.d.ts +51 -0
  75. package/dist/order/order_data.d.ts.map +1 -0
  76. package/dist/order/order_data.js +64 -0
  77. package/dist/order/order_data.js.map +1 -0
  78. package/dist/order/ratio.d.ts +42 -0
  79. package/dist/order/ratio.d.ts.map +1 -0
  80. package/dist/order/ratio.js +82 -0
  81. package/dist/order/ratio.js.map +1 -0
  82. package/dist/order/relative.d.ts +43 -0
  83. package/dist/order/relative.d.ts.map +1 -0
  84. package/dist/order/relative.js +54 -0
  85. package/dist/order/relative.js.map +1 -0
  86. package/dist/owned_owner.d.ts +113 -0
  87. package/dist/owned_owner.d.ts.map +1 -0
  88. package/dist/owned_owner.js +280 -0
  89. package/dist/owned_owner.js.map +1 -0
  90. package/dist/sdk.d.ts +71 -42
  91. package/dist/sdk.d.ts.map +1 -1
  92. package/dist/sdk.js +388 -209
  93. package/dist/sdk.js.map +1 -1
  94. package/dist/send/sign_and_send_transaction.d.ts +46 -0
  95. package/dist/send/sign_and_send_transaction.d.ts.map +1 -0
  96. package/dist/send/sign_and_send_transaction.js +101 -0
  97. package/dist/send/sign_and_send_transaction.js.map +1 -0
  98. package/dist/send/wait_transaction.d.ts +41 -0
  99. package/dist/send/wait_transaction.d.ts.map +1 -0
  100. package/dist/send/wait_transaction.js +204 -0
  101. package/dist/send/wait_transaction.js.map +1 -0
  102. package/dist/udt.d.ts +123 -0
  103. package/dist/udt.d.ts.map +1 -0
  104. package/dist/udt.js +244 -0
  105. package/dist/udt.js.map +1 -0
  106. package/dist/utils/chain.d.ts +38 -0
  107. package/dist/utils/chain.d.ts.map +1 -0
  108. package/dist/utils/chain.js +20 -0
  109. package/dist/utils/chain.js.map +1 -0
  110. package/dist/utils/codec.d.ts +19 -0
  111. package/dist/utils/codec.d.ts.map +1 -0
  112. package/dist/utils/codec.js +66 -0
  113. package/dist/utils/codec.js.map +1 -0
  114. package/dist/utils/transaction_header.d.ts +17 -0
  115. package/dist/utils/transaction_header.d.ts.map +1 -0
  116. package/dist/utils/transaction_header.js +44 -0
  117. package/dist/utils/transaction_header.js.map +1 -0
  118. package/dist/utils/utils.d.ts +126 -0
  119. package/dist/utils/utils.d.ts.map +1 -0
  120. package/dist/utils/utils.js +151 -0
  121. package/dist/utils/utils.js.map +1 -0
  122. package/package.json +19 -26
  123. package/src/constants.ts +84 -181
  124. package/src/conversion/estimate.ts +157 -0
  125. package/src/conversion/maturity.ts +159 -0
  126. package/src/conversion/plans.ts +144 -0
  127. package/src/conversion/projection.ts +128 -0
  128. package/src/conversion/types.ts +287 -0
  129. package/src/conversion/withdrawal_ring.ts +126 -0
  130. package/src/dao.ts +172 -0
  131. package/src/index.ts +89 -3
  132. package/src/logic.ts +295 -0
  133. package/src/order/cells.ts +520 -0
  134. package/src/order/conversion.ts +173 -0
  135. package/src/order/fee.ts +30 -0
  136. package/src/order/fill.ts +83 -0
  137. package/src/order/info.ts +178 -0
  138. package/src/order/master.ts +42 -0
  139. package/src/order/matcher.ts +191 -0
  140. package/src/order/order.ts +302 -0
  141. package/src/order/order_data.ts +128 -0
  142. package/src/order/ratio.ts +144 -0
  143. package/src/order/relative.ts +106 -0
  144. package/src/owned_owner.ts +394 -0
  145. package/src/sdk.ts +555 -440
  146. package/src/send/sign_and_send_transaction.ts +120 -0
  147. package/src/send/wait_transaction.ts +271 -0
  148. package/src/udt.ts +317 -0
  149. package/src/utils/chain.ts +43 -0
  150. package/src/utils/codec.ts +74 -0
  151. package/src/utils/transaction_header.ts +66 -0
  152. package/src/utils/utils.ts +237 -0
  153. package/dist/codec.d.ts +0 -3
  154. package/dist/codec.d.ts.map +0 -1
  155. package/dist/codec.js +0 -58
  156. package/dist/codec.js.map +0 -1
  157. package/src/codec.ts +0 -138
@@ -0,0 +1,394 @@
1
+ import { ccc, mol } from "@ckb-ccc/core";
2
+ import {
3
+ assertDaoOutputLimit,
4
+ DAO_HEADER_INDEX_LIMIT,
5
+ DaoHeaderIndexError,
6
+ isDaoWithdrawalRequest,
7
+ pushHeaderDep,
8
+ } from "./dao.ts";
9
+ import type { IckbDepositCell } from "./logic.ts";
10
+ import { ickbValue } from "./udt.ts";
11
+ import { CheckedInt32LE, CheckedUint64LE } from "./utils/codec.ts";
12
+ import { headersByNumber, transactionHeaders } from "./utils/transaction_header.ts";
13
+ import type { ScriptDeps, TransactionHeader, ValueComponents } from "./utils/utils.ts";
14
+
15
+ /**
16
+ * A live Nervos DAO withdrawal request cell, valued at the two headers it spans.
17
+ */
18
+ export interface DaoWithdrawalRequestCell {
19
+ /** The DAO withdrawal request cell. */
20
+ cell: ccc.Cell;
21
+ /** The deposit header and the request's own header. */
22
+ headers: [TransactionHeader, TransactionHeader];
23
+ /** The DAO interest accrued between the two headers. */
24
+ interests: ccc.Num;
25
+ /** The DAO claim epoch of the request. */
26
+ maturity: ccc.Epoch;
27
+ /** Whether the claim epoch is at or before the tip: the request can be withdrawn. */
28
+ isReady: boolean;
29
+ /** The capacity the withdrawal returns: the cell's plus the interest. */
30
+ ckbValue: ccc.FixedPoint;
31
+ }
32
+
33
+ /** The owner marker payload: where its owned cell sits in their shared transaction. */
34
+ export interface OwnerData {
35
+ /** Signed output-index distance from the owner marker to the owned cell. */
36
+ ownedDistance: ccc.Num;
37
+ }
38
+
39
+ const OwnerDataCodec = mol.struct({
40
+ ownedDistance: CheckedInt32LE,
41
+ });
42
+ // An owner marker's fixed prefix: a signed 32-bit distance.
43
+ const ownerDataBytes = 4;
44
+
45
+ /** Encodes an owner marker payload. */
46
+ export function encodeOwnerData(data: OwnerData): ccc.Bytes {
47
+ return OwnerDataCodec.encode(data);
48
+ }
49
+
50
+ /**
51
+ * Decodes the fixed owner-data prefix and ignores trailing cell payload bytes.
52
+ *
53
+ * @remarks The owner data prefix is 4 bytes after the `0x` marker: a signed little-endian
54
+ * relative output-index distance to the owned cell. Later bytes belong to other protocol
55
+ * data and are intentionally tolerated here.
56
+ */
57
+ export function decodeOwnerData(outputData: ccc.Hex): OwnerData {
58
+ const { ownedDistance } = OwnerDataCodec.decode(
59
+ outputData.slice(0, 2 + 2 * ownerDataBytes),
60
+ );
61
+ return { ownedDistance: ccc.numFrom(ownedDistance) };
62
+ }
63
+
64
+ /**
65
+ * Wraps an owner marker cell that references an owned withdrawal request.
66
+ */
67
+ export class OwnerCell implements ValueComponents {
68
+ /** The live owner marker cell whose output data points to the owned request. */
69
+ public cell: ccc.Cell;
70
+
71
+ /** Owner marker cells carry no UDT value. */
72
+ public readonly udtValue = 0n;
73
+
74
+ /** Creates an owner marker wrapper for a live cell. */
75
+ constructor(cell: ccc.Cell) {
76
+ this.cell = cell;
77
+ }
78
+
79
+ /** Returns the CKB capacity held by the owner marker cell. */
80
+ public get ckbValue(): ccc.FixedPoint {
81
+ return this.cell.cellOutput.capacity;
82
+ }
83
+
84
+ /**
85
+ * Returns the owned withdrawal request out point referenced by this owner marker.
86
+ *
87
+ * @remarks Owner data stores a signed output-index distance. The owned cell is resolved
88
+ * on the same transaction hash as the owner marker cell.
89
+ */
90
+ public getOwned(): ccc.OutPoint {
91
+ const { txHash, index } = this.cell.outPoint;
92
+ let ownedDistance: ccc.Num;
93
+ try {
94
+ ({ ownedDistance } = decodeOwnerData(this.cell.outputData));
95
+ } catch (error) {
96
+ throw new Error(
97
+ `Invalid owner marker payload at ${this.cell.outPoint.toHex()}: ${this.cell.outputData}`,
98
+ { cause: error },
99
+ );
100
+ }
101
+ const ownedIndex = index + ownedDistance;
102
+ if (ownedIndex < 0n) {
103
+ throw new Error(
104
+ `Owner marker ${this.cell.outPoint.toHex()} points before output 0 with distance ${String(ownedDistance)}`,
105
+ );
106
+ }
107
+ return new ccc.OutPoint(txHash, ownedIndex);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Pairs an owned DAO withdrawal request with the owner marker cell that points to it.
113
+ */
114
+ export class WithdrawalGroup implements ValueComponents {
115
+ /** The decoded DAO withdrawal request controlled by this owner marker. */
116
+ public owned: DaoWithdrawalRequestCell;
117
+
118
+ /** The owner marker cell that references the owned withdrawal request. */
119
+ public owner: OwnerCell;
120
+
121
+ /** Creates a withdrawal group from a decoded request and its owner marker. */
122
+ constructor(owned: DaoWithdrawalRequestCell, owner: OwnerCell) {
123
+ this.owned = owned;
124
+ this.owner = owner;
125
+ }
126
+
127
+ /** Returns the total CKB capacity in the owned withdrawal and owner marker cells. */
128
+ public get ckbValue(): ccc.FixedPoint {
129
+ return this.owned.ckbValue + this.owner.cell.cellOutput.capacity;
130
+ }
131
+
132
+ /** Returns the iCKB amount represented by the owned withdrawal request. */
133
+ public get udtValue(): ccc.FixedPoint {
134
+ return ickbValue(this.owned.cell.capacityFree, this.owned.headers[0].header);
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Builds and finds Owned Owner withdrawal groups for an iCKB deployment.
140
+ */
141
+ export class OwnedOwnerManager implements ScriptDeps {
142
+ /** The Owned Owner script used as owner marker type and withdrawal request lock. */
143
+ public readonly script: ccc.Script;
144
+
145
+ /** Cell dependencies required to execute the Owned Owner script. */
146
+ public readonly cellDeps: ccc.CellDep[];
147
+
148
+ /** The Nervos DAO script the withdrawal requests carry, with its cell deps. */
149
+ public readonly dao: ScriptDeps;
150
+
151
+ /** Creates the manager for one Owned Owner deployment over one DAO deployment. */
152
+ constructor(script: ccc.Script, cellDeps: ccc.CellDep[], dao: ScriptDeps) {
153
+ this.script = script;
154
+ this.cellDeps = cellDeps;
155
+ this.dao = dao;
156
+ }
157
+
158
+ /** Whether the cell is an owner marker for this manager's script. */
159
+ public isOwner(cell: ccc.Cell): boolean {
160
+ if (cell.cellOutput.type?.eq(this.script) !== true || cell.outputData.length < 10) {
161
+ return false;
162
+ }
163
+ try {
164
+ new OwnerCell(cell).getOwned();
165
+ return true;
166
+ } catch {
167
+ return false;
168
+ }
169
+ }
170
+
171
+ /** Whether the cell is a DAO withdrawal request locked by this manager's script. */
172
+ public isOwned(cell: ccc.Cell): boolean {
173
+ return (
174
+ isDaoWithdrawalRequest(cell, this.dao.script) &&
175
+ cell.cellOutput.lock.eq(this.script)
176
+ );
177
+ }
178
+
179
+ /**
180
+ * Spends the deposits into DAO withdrawal requests locked by the Owned Owner script,
181
+ * each followed by an owner marker locked by `lock` that points back at it.
182
+ *
183
+ * @remarks Caller must ensure UDT cellDeps are added to the transaction, for example
184
+ * via `ickbUdt.addCellDeps(tx)`.
185
+ */
186
+ public requestWithdrawal(
187
+ txLike: ccc.TransactionLike,
188
+ deposits: IckbDepositCell[],
189
+ lock: ccc.Script,
190
+ ): ccc.Transaction {
191
+ const tx = ccc.Transaction.from(txLike);
192
+ if (deposits.length === 0) {
193
+ return tx;
194
+ }
195
+ // The DAO script pairs each request output with the input at the same index.
196
+ if (
197
+ tx.inputs.length !== tx.outputs.length ||
198
+ tx.outputs.length !== tx.outputsData.length
199
+ ) {
200
+ throw new Error("Transaction has different inputs and outputs lengths");
201
+ }
202
+ tx.addCellDeps(this.cellDeps);
203
+ tx.addCellDeps(this.dao.cellDeps);
204
+ const requestStart = tx.outputs.length;
205
+ for (const { cell, headers } of deposits) {
206
+ const depositHeader = headers[0];
207
+ if (depositHeader.txHash !== cell.outPoint.txHash) {
208
+ throw new Error(
209
+ `DAO deposit ${cell.outPoint.toHex()} header txHash ${String(depositHeader.txHash)} does not match cell txHash ${cell.outPoint.txHash}`,
210
+ );
211
+ }
212
+ if (cell.cellOutput.lock.args.length !== this.script.args.length) {
213
+ throw new Error("Withdrawal request lock args has different size from deposit");
214
+ }
215
+ pushHeaderDep(tx, depositHeader.header.hash);
216
+ tx.addInput(cell);
217
+ tx.addOutput(
218
+ { capacity: cell.cellOutput.capacity, lock: this.script, type: this.dao.script },
219
+ CheckedUint64LE.encode(depositHeader.header.number),
220
+ );
221
+ }
222
+ for (let index = 0; index < deposits.length; index += 1) {
223
+ tx.addOutput(
224
+ { lock, type: this.script },
225
+ // Negative: owner markers are appended after their withdrawal outputs.
226
+ encodeOwnerData({
227
+ ownedDistance: BigInt(requestStart + index - tx.outputs.length),
228
+ }),
229
+ );
230
+ }
231
+ assertDaoOutputLimit(tx, this.dao.script);
232
+ return tx;
233
+ }
234
+
235
+ /**
236
+ * Spends the owned withdrawal requests and their owner markers, with the DAO header deps,
237
+ * since, and witness each request needs.
238
+ *
239
+ * @remarks The distinct deposit headers are moved to the front of the header deps and
240
+ * the withdrawal headers appended, so a deposit header's index is its rank among the
241
+ * collected withdrawals. Caller must ensure UDT cellDeps are added to the transaction.
242
+ */
243
+ public withdraw(
244
+ txLike: ccc.TransactionLike,
245
+ withdrawalGroups: WithdrawalGroup[],
246
+ ): ccc.Transaction {
247
+ const tx = ccc.Transaction.from(txLike);
248
+ if (withdrawalGroups.length === 0) {
249
+ return tx;
250
+ }
251
+ for (const group of withdrawalGroups) {
252
+ const ownedOutPoint = group.owned.cell.outPoint;
253
+ const linkedOutPoint = group.owner.getOwned();
254
+ if (!linkedOutPoint.eq(ownedOutPoint)) {
255
+ throw new Error(
256
+ `Withdrawal owner ${group.owner.cell.outPoint.toHex()} points to ${linkedOutPoint.toHex()} but group owned cell is ${ownedOutPoint.toHex()}`,
257
+ );
258
+ }
259
+ const { cell, headers } = group.owned;
260
+ if (headers[1].txHash !== cell.outPoint.txHash) {
261
+ throw new Error(
262
+ `DAO withdrawal request ${cell.outPoint.toHex()} header txHash ${String(headers[1].txHash)} does not match cell txHash ${cell.outPoint.txHash}`,
263
+ );
264
+ }
265
+ }
266
+ tx.addCellDeps(this.cellDeps);
267
+ tx.addCellDeps(this.dao.cellDeps);
268
+ // Only this step names a header by index, and the deployed script reads that index as
269
+ // one byte, so the deposit headers go first, ahead of whatever the requests and receipts
270
+ // pushed: the limit then counts these alone. Safe because nothing else in the stack reads
271
+ // a header by position and every built transaction passes here once.
272
+ const depositHashes = [
273
+ ...new Set(withdrawalGroups.map(({ owned }) => owned.headers[0].header.hash)),
274
+ ];
275
+ tx.headerDeps = [
276
+ ...depositHashes,
277
+ ...tx.headerDeps.filter((hash) => !depositHashes.includes(hash)),
278
+ ];
279
+ for (const { owned } of withdrawalGroups) {
280
+ pushHeaderDep(tx, owned.headers[1].header.hash);
281
+ }
282
+ for (const { owned } of withdrawalGroups) {
283
+ const headerIndex = tx.headerDeps.indexOf(owned.headers[0].header.hash);
284
+ // The deployed dao.c reads one byte of the u64 index field (RFC 0023 erratum,
285
+ // nervosnetwork/rfcs pull 456), so index 256 would be read as 0.
286
+ if (headerIndex >= DAO_HEADER_INDEX_LIMIT) {
287
+ throw new DaoHeaderIndexError(headerIndex);
288
+ }
289
+ const inputIndex =
290
+ tx.addInput({
291
+ outPoint: owned.cell.outPoint,
292
+ cellOutput: owned.cell.cellOutput,
293
+ outputData: owned.cell.outputData,
294
+ since: { relative: "absolute", metric: "epoch", value: owned.maturity.toNum() },
295
+ }) - 1;
296
+ const witness = tx.getWitnessArgs(inputIndex) ?? ccc.WitnessArgs.from({});
297
+ if ((witness.inputType ?? "") !== "") {
298
+ throw new Error("Witnesses of withdrawal request already in use");
299
+ }
300
+ witness.inputType = ccc.hexFrom(CheckedUint64LE.encode(headerIndex));
301
+ tx.setWitnessArgs(inputIndex, witness);
302
+ }
303
+ for (const { owner } of withdrawalGroups) {
304
+ tx.addInput(owner.cell);
305
+ }
306
+ assertDaoOutputLimit(tx, this.dao.script);
307
+ return tx;
308
+ }
309
+
310
+ /**
311
+ * The owner markers among `cells` paired with their owned withdrawal requests, which live
312
+ * in the marker's own transaction at the encoded distance. Markers whose target is not an
313
+ * owned withdrawal request are skipped. Each header is read once per batch.
314
+ */
315
+ public async withdrawalGroupsFrom(
316
+ client: ccc.Client,
317
+ cells: readonly ccc.Cell[],
318
+ tip: ccc.ClientBlockHeader,
319
+ ): Promise<WithdrawalGroup[]> {
320
+ const owners = cells
321
+ .filter((cell) => this.isOwner(cell))
322
+ .map((cell) => new OwnerCell(cell));
323
+ const ownedCells = await Promise.all(
324
+ owners.map(async (owner) => client.getCell(owner.getOwned())),
325
+ );
326
+ const pairs = owners.flatMap((owner, index) => {
327
+ const owned = ownedCells[index];
328
+ return owned !== undefined && this.isOwned(owned)
329
+ ? [{ owner, owned, depositBlockNumber: depositBlockNumberOf(owned) }]
330
+ : [];
331
+ });
332
+ const [depositHeaders, requestHeaders] = await Promise.all([
333
+ headersByNumber(
334
+ client,
335
+ pairs.map((pair) => pair.depositBlockNumber),
336
+ ),
337
+ transactionHeaders(
338
+ client,
339
+ pairs.map((pair) => pair.owned.outPoint.txHash),
340
+ ),
341
+ ]);
342
+ return pairs.map(({ owner, owned, depositBlockNumber }) => {
343
+ const { txHash } = owned.outPoint;
344
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- both headers were read above.
345
+ const depositHeader = depositHeaders.get(depositBlockNumber)!;
346
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- both headers were read above.
347
+ const requestHeader = requestHeaders.get(txHash)!;
348
+ return new WithdrawalGroup(
349
+ withdrawalRequestCell(
350
+ owned,
351
+ depositHeader,
352
+ { header: requestHeader, txHash },
353
+ tip,
354
+ ),
355
+ owner,
356
+ );
357
+ });
358
+ }
359
+ }
360
+
361
+ /** Values a DAO withdrawal request at its two headers and judges it claimable at the tip. */
362
+ export function withdrawalRequestCell(
363
+ cell: ccc.Cell,
364
+ depositHeader: ccc.ClientBlockHeader,
365
+ requestHeader: TransactionHeader,
366
+ tip: ccc.ClientBlockHeader,
367
+ ): DaoWithdrawalRequestCell {
368
+ const interests = ccc.calcDaoProfit(
369
+ cell.capacityFree,
370
+ depositHeader,
371
+ requestHeader.header,
372
+ );
373
+ const maturity = ccc.calcDaoClaimEpoch(depositHeader, requestHeader.header);
374
+ return {
375
+ cell,
376
+ headers: [{ header: depositHeader }, requestHeader],
377
+ interests,
378
+ maturity,
379
+ isReady: maturity.compare(tip.epoch) <= 0,
380
+ ckbValue: cell.cellOutput.capacity + interests,
381
+ };
382
+ }
383
+
384
+ /** The deposit block number a withdrawal request records in its data. */
385
+ function depositBlockNumberOf(cell: ccc.Cell): ccc.Num {
386
+ try {
387
+ return CheckedUint64LE.decode(cell.outputData);
388
+ } catch (error) {
389
+ throw new Error(
390
+ `Invalid DAO withdrawal request payload at ${cell.outPoint.toHex()}: ${cell.outputData}`,
391
+ { cause: error },
392
+ );
393
+ }
394
+ }