@ickb/sdk 1000.0.82 → 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,287 @@
1
+ import type { ccc } from "@ckb-ccc/core";
2
+ import type { LockUpPolicy } from "../dao.ts";
3
+ import type { IckbDepositCell, LogicManager, ReceiptCell } from "../logic.ts";
4
+ import type { OrderCell, OrderGroup } from "../order/cells.ts";
5
+ import type { Info } from "../order/info.ts";
6
+ import type { OrderManager } from "../order/order.ts";
7
+ import type { Ratio } from "../order/ratio.ts";
8
+ import type { OwnedOwnerManager, WithdrawalGroup } from "../owned_owner.ts";
9
+ import type { IckbUdt } from "../udt.ts";
10
+ import type { ValueComponents } from "../utils/utils.ts";
11
+
12
+ /**
13
+ * Direction requested by a conversion transaction.
14
+ */
15
+ export type ConversionDirection = "ckb-to-ickb" | "ickb-to-ckb";
16
+
17
+ /**
18
+ * Snapshot used to plan one wallet conversion transaction.
19
+ */
20
+ export interface ConversionTransactionContext {
21
+ /** Public system state sampled for conversion planning. */
22
+ system: SystemState;
23
+ /** User receipt cells available for deposit completion. */
24
+ receipts: ReceiptCell[];
25
+ /** User withdrawal groups ready to complete. */
26
+ readyWithdrawals: WithdrawalGroup[];
27
+ /** Order groups available for collection or budgeting as account value. */
28
+ availableOrders: OrderGroup[];
29
+ /** The wallet's liquid cells, plain CKB and iCKB, that completion funds from and sweeps. */
30
+ cells: ccc.Cell[];
31
+ /** Projected CKB available to the wallet after pending state is considered. */
32
+ ckbAvailable: bigint;
33
+ /** Projected iCKB available to the wallet after pending state is considered. */
34
+ ickbAvailable: bigint;
35
+ /**
36
+ * The latest date at which everything the wallet has converting, plus this request, is
37
+ * collectable; an amount of zero is the collection itself.
38
+ */
39
+ estimatedMaturity: bigint;
40
+ }
41
+
42
+ /**
43
+ * Inputs and policy limits for building one conversion transaction.
44
+ */
45
+ export interface ConversionTransactionOptions {
46
+ /** Conversion direction to build. */
47
+ direction: ConversionDirection;
48
+
49
+ /** Requested input amount in the source asset. */
50
+ amount: bigint;
51
+
52
+ /**
53
+ * Lock that owns every cell the transaction creates for the user, the conversion outputs
54
+ * and the change; the signer's recommended lock by default. Completion sweeps the liquid
55
+ * cells along within its input limit, so a foreign lock redirects those funds too.
56
+ */
57
+ lock?: ccc.Script;
58
+
59
+ /** Signer whose committed cells fund the completed transaction. */
60
+ signer: ccc.Signer;
61
+
62
+ /** Sampled state used to plan this conversion. */
63
+ context: ConversionTransactionContext;
64
+ }
65
+
66
+ /**
67
+ * Reason a conversion transaction could not be built without throwing.
68
+ */
69
+ export type ConversionTransactionFailureReason =
70
+ | "amount-negative"
71
+ | "insufficient-ckb"
72
+ | "insufficient-ickb"
73
+ | "amount-too-small"
74
+ | "nothing-to-do";
75
+
76
+ /**
77
+ * A dust iCKB-to-CKB order: too small for the default fee to clear the estimate threshold,
78
+ * so it pays the smallest fee that does. For callers to surface in UI or logs.
79
+ */
80
+ export interface ConversionNotice {
81
+ /** iCKB input amount that triggered the notice. */
82
+ inputIckb: bigint;
83
+
84
+ /** CKB output estimate for the noticed path. */
85
+ outputCkb: bigint;
86
+
87
+ /** CKB incentive associated with the noticed path. */
88
+ incentiveCkb: bigint;
89
+ }
90
+
91
+ /**
92
+ * High-level conversion composition used by a built transaction.
93
+ */
94
+ export interface ConversionMetadata {
95
+ /** Composition category selected for the built conversion. */
96
+ kind: "direct" | "order" | "direct-plus-order" | "collect-only";
97
+ }
98
+
99
+ /**
100
+ * Result of attempting to build a conversion transaction.
101
+ */
102
+ export type ConversionTransactionResult =
103
+ | {
104
+ ok: true;
105
+ /** Completed transaction, funded from the signer's committed cells. Callers own signing and send. */
106
+ tx: ccc.Transaction;
107
+ /** Estimated maturity timestamp for the conversion result. */
108
+ estimatedMaturity: bigint;
109
+ /** Composition of the selected conversion path. */
110
+ conversion: ConversionMetadata;
111
+ /** Optional notice about the selected path. */
112
+ conversionNotice?: ConversionNotice;
113
+ /**
114
+ * True when every liquid cell of the context is an input. False when the input limit
115
+ * left some behind: a move to another lock then needs another transaction.
116
+ */
117
+ isSweepComplete: boolean;
118
+ /**
119
+ * The epoch the transaction must be broadcast before for its withdrawal requests to
120
+ * claim at their sampled dates (`system.lockUp`); absent when it requests none.
121
+ */
122
+ broadcastBefore?: ccc.Epoch;
123
+ }
124
+ | {
125
+ ok: false;
126
+ /** Machine-readable failure reason. */
127
+ reason: ConversionTransactionFailureReason;
128
+ /** Best available maturity estimate from the input context. */
129
+ estimatedMaturity: bigint;
130
+ /**
131
+ * For `amount-too-small`: the smallest amount this direction accepts at the current fee
132
+ * rate, in the request's unit (CKB shannons or iCKB), so a caller can name it.
133
+ */
134
+ minimum?: bigint;
135
+ };
136
+
137
+ /**
138
+ * Options for completing a partial iCKB transaction before signing and sending.
139
+ */
140
+ export interface CompleteIckbTransactionOptions {
141
+ /** Signer that prepares the transaction; its recommended lock owns the change by default. */
142
+ signer: ccc.Signer;
143
+
144
+ /** Lock that owns the change, iCKB and plain, when not the signer's recommended lock. */
145
+ lock?: ccc.Script;
146
+
147
+ /** Fee rate passed to CCC fee completion. */
148
+ feeRate: ccc.Num;
149
+
150
+ /** The signer's known liquid cells, plain CKB and iCKB, from the account state already read. */
151
+ cells: ccc.Cell[];
152
+ }
153
+
154
+ /**
155
+ * Quote details for one order-based conversion path.
156
+ */
157
+ export interface ConversionOrderEstimate {
158
+ /** Output amount after applying the order ratio and fee. */
159
+ convertedAmount: ccc.FixedPoint;
160
+ /** CKB fee component embedded in the order conversion. */
161
+ ckbFee: ccc.FixedPoint;
162
+ /** Order info that should be encoded into a created order. */
163
+ info: Info;
164
+ /** Estimated maturity timestamp, or `undefined` when it cannot be estimated. */
165
+ maturity: ccc.Num | undefined;
166
+ /** A non-fatal notice about the path this estimate took, for the caller to surface. */
167
+ notice?: ConversionNotice;
168
+ }
169
+
170
+ /**
171
+ * Input accepted by maturity estimation, either a live order cell or raw order data plus values.
172
+ */
173
+ export type MaturityOrderInput =
174
+ | OrderCell
175
+ | {
176
+ info: Info;
177
+ amounts: ValueComponents;
178
+ };
179
+
180
+ /**
181
+ * Raw wallet-owned cells and grouped iCKB state sampled from L1.
182
+ */
183
+ export interface AccountState {
184
+ /** Plain capacity cells owned by the account locks. */
185
+ capacityCells: ccc.Cell[];
186
+ /** Native iCKB xUDT cells owned by the account locks. */
187
+ nativeUdtCells: ccc.Cell[];
188
+ /** Receipt cells owned by the account. */
189
+ receipts: ReceiptCell[];
190
+ /** Withdrawal groups owned by the account. */
191
+ withdrawalGroups: WithdrawalGroup[];
192
+ }
193
+
194
+ /**
195
+ * Account balances split into available, pending, and order/withdrawal buckets.
196
+ */
197
+ export interface AccountAvailabilityProjection {
198
+ /**
199
+ * CKB in the account's liquid cells, plain and iCKB alike: what the wallet shows and what
200
+ * the next transaction spends, before the change cells, an order's master cell and the
201
+ * fee it must fund on top.
202
+ */
203
+ ckbNative: bigint;
204
+ /** iCKB in the account's native iCKB cells. */
205
+ ickbNative: bigint;
206
+ /** `ckbNative` plus the CKB the receipts, ready withdrawals and collectable orders return. */
207
+ ckbAvailable: bigint;
208
+ /** `ickbNative` plus the iCKB the receipts and collectable orders return. */
209
+ ickbAvailable: bigint;
210
+ /** CKB pending in withdrawals or orders. */
211
+ ckbPending: bigint;
212
+ /** iCKB pending in withdrawals or orders. */
213
+ ickbPending: bigint;
214
+ /** Total CKB balance including pending positions. */
215
+ ckbBalance: bigint;
216
+ /** Total iCKB balance including pending positions. */
217
+ ickbBalance: bigint;
218
+ /**
219
+ * Matured withdrawal groups one transaction can complete: at most the deposit-header slots
220
+ * the DAO script addresses (`DAO_HEADER_INDEX_LIMIT`), which the deposit headers hold alone.
221
+ */
222
+ readyWithdrawals: WithdrawalGroup[];
223
+ /** Withdrawal groups still waiting for DAO maturity or for a header slot in a later turn. */
224
+ pendingWithdrawals: WithdrawalGroup[];
225
+ /** Order groups available for collection or budgeting as account value. */
226
+ availableOrders: OrderGroup[];
227
+ /** Order groups that are owned but unavailable or unresolved. */
228
+ pendingOrders: OrderGroup[];
229
+ }
230
+
231
+ /**
232
+ * Combined projection and transaction-planning context for one sampled account state.
233
+ */
234
+ export interface ConversionTransactionContextProjection {
235
+ /** User-facing availability projection. */
236
+ projection: AccountAvailabilityProjection;
237
+ /** Builder-facing transaction context derived from the same sampled state. */
238
+ context: ConversionTransactionContext;
239
+ }
240
+
241
+ /**
242
+ * Public sampled system state used for quotes, maturity, and conversion planning.
243
+ */
244
+ export interface SystemState {
245
+ /** The fee rate for transactions. */
246
+ feeRate: ccc.Num;
247
+ /** Sampled tip used for this L1 scan. It may be stale after scans complete. */
248
+ tip: ccc.ClientBlockHeader;
249
+ /** The exchange ratio between CKB and UDT. */
250
+ exchangeRatio: Ratio;
251
+ /** Every order past par on the book, the wallet's own included: what the bot can fill. */
252
+ orderPool: OrderGroup[];
253
+ /** Every iCKB pool deposit with its claim epoch at this tip: the CKB supply. */
254
+ poolDeposits: IckbDepositCell[];
255
+ /** The caller's withdrawal timing rules, which every builder and the sender follow. */
256
+ lockUp: LockUpPolicy;
257
+ }
258
+
259
+ /** Manager set that builds one iCKB SDK instance. */
260
+ export interface SdkManagers {
261
+ /** iCKB xUDT manager with receipt and DAO deposit aware completion. */
262
+ ickbUdt: IckbUdt;
263
+ /** Owned-owner manager for withdrawal request and claim cells. */
264
+ ownedOwner: OwnedOwnerManager;
265
+ /** iCKB Logic manager for deposits and receipts. */
266
+ ickbLogic: LogicManager;
267
+ /** Limit order manager. */
268
+ order: OrderManager;
269
+ }
270
+
271
+ export interface ConversionOrder {
272
+ amounts: ValueComponents;
273
+ estimate: ConversionOrderEstimate;
274
+ }
275
+
276
+ export interface CkbToIckbConversionPlan {
277
+ depositCapacity: bigint;
278
+ depositCount: number;
279
+ estimatedMaturity: bigint;
280
+ order?: ConversionOrder;
281
+ }
282
+
283
+ export interface IckbToCkbConversionPlan {
284
+ estimatedMaturity: bigint;
285
+ order?: ConversionOrder;
286
+ selectedDeposits: IckbDepositCell[];
287
+ }
@@ -0,0 +1,126 @@
1
+ import type { ccc } from "@ckb-ccc/core";
2
+ import { DAO_CYCLE_EPOCHS, DAO_OUTPUT_LIMIT } from "../dao.ts";
3
+ import type { IckbDepositCell } from "../logic.ts";
4
+ import { ICKB_DEPOSIT_CAP } from "../udt.ts";
5
+
6
+ /**
7
+ * Ring segment of pool deposits grouped by maturity around the DAO cycle.
8
+ */
9
+ export interface RingSegment {
10
+ /** Segment index in the ring. */
11
+ index: number;
12
+
13
+ /** Deposits assigned to this segment. */
14
+ deposits: IckbDepositCell[];
15
+
16
+ /** Total iCKB value of deposits in this segment. */
17
+ udtValue: bigint;
18
+ }
19
+
20
+ /**
21
+ * Splits pool deposits into power-of-two maturity ring segments, one segment per two
22
+ * deposit caps of pool iCKB.
23
+ */
24
+ export function ringSegments(poolDeposits: readonly IckbDepositCell[]): RingSegment[] {
25
+ // Value, not count: zero-value deposits, made without iCKB Logic running, must not add segments.
26
+ // Two caps per segment, not one: the room the bot's own fills need, see node/docs/policy.md.
27
+ const totalUdt = poolDeposits.reduce((sum, deposit) => sum + deposit.udtValue, 0n);
28
+ let segmentCount = 1;
29
+ while (BigInt(segmentCount) * 2n * ICKB_DEPOSIT_CAP < totalUdt) {
30
+ segmentCount *= 2;
31
+ }
32
+ const segments = Array.from({ length: segmentCount }, (_, index): RingSegment => ({
33
+ index,
34
+ deposits: [],
35
+ udtValue: 0n,
36
+ }));
37
+
38
+ // The ring is one DAO cycle round, so a claim rolled a cycle lands in the same segment.
39
+ for (const deposit of poolDeposits) {
40
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ringSegmentIndex returns 0 <= index < segmentCount.
41
+ const segment = segments[ringSegmentIndex(deposit.claimEpoch, segmentCount)]!;
42
+ segment.deposits.push(deposit);
43
+ segment.udtValue += deposit.udtValue;
44
+ }
45
+
46
+ return segments;
47
+ }
48
+
49
+ /** The segment index an epoch falls in, for a deposit's claim or the tip. */
50
+ export function ringSegmentIndex(epoch: ccc.Epoch, segmentCount: number): number {
51
+ const { denominator } = epoch;
52
+ if (denominator <= 0n) {
53
+ throw new Error("Epoch denominator must be positive");
54
+ }
55
+ const scaled = epoch.integer * denominator + epoch.numerator;
56
+ const ring = DAO_CYCLE_EPOCHS * denominator;
57
+ const wrapped = ((scaled % ring) + ring) % ring;
58
+ return Number((wrapped * BigInt(segmentCount)) / ring);
59
+ }
60
+
61
+ /**
62
+ * Returns a filter that excludes the ring anchor deposits from surplus selection: each
63
+ * segment keeps its anchor, the largest deposit not in `ready`, else the largest one.
64
+ */
65
+ export function ringSurplusDepositFilter(
66
+ poolDeposits: readonly IckbDepositCell[],
67
+ ready: readonly IckbDepositCell[],
68
+ ): (deposit: IckbDepositCell) => boolean {
69
+ const isReady = new Set(ready.map((deposit) => deposit.cell.outPoint.toHex()));
70
+ const anchors = new Set(
71
+ ringSegments(poolDeposits).flatMap((segment) => {
72
+ const anchor = segment.deposits.reduce<IckbDepositCell | undefined>(
73
+ (best, deposit) =>
74
+ best === undefined || isBetterRingAnchor(deposit, best, isReady)
75
+ ? deposit
76
+ : best,
77
+ undefined,
78
+ );
79
+ return anchor === undefined ? [] : [anchor.cell.outPoint.toHex()];
80
+ }),
81
+ );
82
+ return (deposit) => !anchors.has(deposit.cell.outPoint.toHex());
83
+ }
84
+
85
+ function isBetterRingAnchor(
86
+ candidate: IckbDepositCell,
87
+ current: IckbDepositCell,
88
+ isReady: ReadonlySet<string>,
89
+ ): boolean {
90
+ const candidateReady = isReady.has(candidate.cell.outPoint.toHex());
91
+ if (candidateReady !== isReady.has(current.cell.outPoint.toHex())) {
92
+ return !candidateReady;
93
+ }
94
+ return candidate.udtValue > current.udtValue;
95
+ }
96
+
97
+ /**
98
+ * Walks the candidates in the given order and takes each one that still fits under
99
+ * `maxAmount`; the caller chooses the order, earliest claim first
100
+ * for a user (the wait they feel), surplus before anchors for the bot. The chain stops at
101
+ * the most requests one transaction can carry, a request and its owner marker per deposit;
102
+ * completion steps down from there when change and fees need more room.
103
+ */
104
+ export function fitWithdrawalDeposits(
105
+ candidates: readonly IckbDepositCell[],
106
+ maxAmount: bigint,
107
+ ): IckbDepositCell[] {
108
+ const deposits: IckbDepositCell[] = [];
109
+ let total = 0n;
110
+ for (const deposit of candidates) {
111
+ if (deposits.length === DAO_OUTPUT_LIMIT / 2) {
112
+ break;
113
+ }
114
+ if (total + deposit.udtValue > maxAmount) {
115
+ continue;
116
+ }
117
+ total += deposit.udtValue;
118
+ deposits.push(deposit);
119
+ }
120
+ return deposits;
121
+ }
122
+
123
+ /** Earliest claim first: the deposit that turns into CKB soonest. */
124
+ export function sortByClaim(deposits: readonly IckbDepositCell[]): IckbDepositCell[] {
125
+ return deposits.toSorted((left, right) => left.claimEpoch.compare(right.claimEpoch));
126
+ }
package/src/dao.ts ADDED
@@ -0,0 +1,172 @@
1
+ import { ccc } from "@ckb-ccc/core";
2
+ import { CheckedUint64LE } from "./utils/codec.ts";
3
+
4
+ /**
5
+ * Maximum outputs accepted by deployed dao.c, not a general CKB consensus cap.
6
+ * A node upgrade cannot lift a limit in that deployed script.
7
+ */
8
+ export const DAO_OUTPUT_LIMIT = 64;
9
+
10
+ /**
11
+ * Deposit-header indices the deployed DAO script can address in a phase-2 witness.
12
+ *
13
+ * @remarks The script reads one byte of the u64 field (RFC 0023 erratum,
14
+ * nervosnetwork/rfcs pull 456).
15
+ */
16
+ export const DAO_HEADER_INDEX_LIMIT = 256;
17
+
18
+ /** One DAO cycle, the lock-up a deposit renews by when it is not withdrawn. */
19
+ export const DAO_CYCLE_EPOCHS = 180n;
20
+
21
+ /**
22
+ * Error thrown when a transaction exceeds the deployed DAO script's output limit.
23
+ */
24
+ export class DaoOutputLimitError extends Error {
25
+ /**
26
+ * Creates an output-limit error for a transaction with too many outputs.
27
+ */
28
+ constructor(outputCount: number, options?: ErrorOptions) {
29
+ super(
30
+ `NervosDAO transaction has ${String(outputCount)} output cells, exceeding the limit of ${String(DAO_OUTPUT_LIMIT)}`,
31
+ options,
32
+ );
33
+ this.name = "DaoOutputLimitError";
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Error thrown when a phase-2 withdrawal would reference a deposit header the
39
+ * deployed DAO script cannot address.
40
+ */
41
+ export class DaoHeaderIndexError extends Error {
42
+ /**
43
+ * Creates a header-index error for one unrepresentable index.
44
+ */
45
+ constructor(headerIndex: number, options?: ErrorOptions) {
46
+ super(
47
+ `NervosDAO deposit header index ${String(headerIndex)} is not below ${String(DAO_HEADER_INDEX_LIMIT)}`,
48
+ options,
49
+ );
50
+ this.name = "DaoHeaderIndexError";
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Throws when a transaction using the DAO script exceeds the Nervos DAO output limit. An
56
+ * oversized transaction with an unresolved input is refused too: whether it uses the DAO
57
+ * cannot be known, and the completion walk steps a plan down on this error either way.
58
+ */
59
+ export function assertDaoOutputLimit(
60
+ txLike: ccc.TransactionLike,
61
+ daoScriptLike: ccc.ScriptLike,
62
+ ): void {
63
+ const tx = ccc.Transaction.from(txLike);
64
+ if (tx.outputs.length <= DAO_OUTPUT_LIMIT) {
65
+ return;
66
+ }
67
+ const daoScript = ccc.Script.from(daoScriptLike);
68
+ if (
69
+ tx.inputs.some(
70
+ (input) =>
71
+ input.cellOutput === undefined || input.cellOutput.type?.eq(daoScript) === true,
72
+ ) ||
73
+ tx.outputs.some((output) => output.type?.eq(daoScript) === true)
74
+ ) {
75
+ throw new DaoOutputLimitError(tx.outputs.length);
76
+ }
77
+ }
78
+
79
+ /** The canonical DAO deposit data payload. */
80
+ export function depositData(): ccc.Hex {
81
+ return ccc.hexFrom(CheckedUint64LE.encode(0n));
82
+ }
83
+
84
+ /** Whether the cell is a DAO deposit of the given DAO script. */
85
+ export function isDaoDeposit(cell: ccc.CellAny, daoScript: ccc.Script): boolean {
86
+ return (
87
+ cell.outputData === depositData() && cell.cellOutput.type?.eq(daoScript) === true
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Whether the cell uses the DAO script and is not deposit-shaped. This structural check
93
+ * does not decode the withdrawal request's deposit block number payload.
94
+ */
95
+ export function isDaoWithdrawalRequest(
96
+ cell: ccc.CellAny,
97
+ daoScript: ccc.Script,
98
+ ): boolean {
99
+ return (
100
+ cell.outputData !== depositData() && cell.cellOutput.type?.eq(daoScript) === true
101
+ );
102
+ }
103
+
104
+ /**
105
+ * A caller's timing rules for withdrawal requests, in epochs (the chain's clock, so no
106
+ * wall-clock drift). Selection: a deposit may be requested when its claim epoch is past
107
+ * `tip + minLockUp` (closer, the request would commit after the claim and lock for another
108
+ * cycle, so the deposit is judged on its next cycle) and before `tip + maxLockUp`.
109
+ * Broadcast: the signed request is sent only while the tip is before the earliest selected
110
+ * claim less `broadcastReserve`, else it is refused before broadcast.
111
+ */
112
+ export interface LockUpPolicy {
113
+ minLockUp: ccc.Epoch;
114
+ maxLockUp: ccc.Epoch;
115
+ broadcastReserve: ccc.Epoch;
116
+ }
117
+
118
+ /**
119
+ * The bot's rules on the nominal four-hour epoch: claims twenty minutes to an hour out,
120
+ * sent while fifteen minutes remain. The bot signs at once, so a short window suffices, and
121
+ * the estimate models the pool's supply on it (`sdk/docs/pool_maturity_estimates.md`).
122
+ */
123
+ export const BOT_LOCK_UP: LockUpPolicy = {
124
+ minLockUp: ccc.Epoch.from([0n, 1n, 12n]),
125
+ maxLockUp: ccc.Epoch.from([0n, 4n, 16n]),
126
+ broadcastReserve: ccc.Epoch.from([0n, 1n, 16n]),
127
+ };
128
+
129
+ /**
130
+ * A wallet's rules on the nominal four-hour epoch: claims two hours to three days out,
131
+ * sent while ninety minutes remain. A user may leave the request in a wallet popup.
132
+ */
133
+ export const WALLET_LOCK_UP: LockUpPolicy = {
134
+ minLockUp: ccc.Epoch.from([0n, 1n, 2n]),
135
+ maxLockUp: ccc.Epoch.from([18n, 0n, 1n]),
136
+ broadcastReserve: ccc.Epoch.from([0n, 3n, 8n]),
137
+ };
138
+
139
+ /** A deposit's maturity, rolled a cycle when its claim is too close, and whether it is ready. */
140
+ export function depositMaturity(
141
+ claim: ccc.Epoch,
142
+ tip: ccc.ClientBlockHeader,
143
+ { minLockUp, maxLockUp }: LockUpPolicy,
144
+ ): { maturity: ccc.Epoch; isReady: boolean } {
145
+ const maturity =
146
+ claim.compare(minLockUp.add(tip.epoch)) <= 0
147
+ ? claim.add([DAO_CYCLE_EPOCHS, 0n, 1n])
148
+ : claim;
149
+ return { maturity, isReady: maxLockUp.add(tip.epoch).compare(maturity) > 0 };
150
+ }
151
+
152
+ /**
153
+ * The epoch a transaction requesting these deposits must be broadcast before: the earliest
154
+ * claim less the reserve; undefined when it requests none.
155
+ */
156
+ export function broadcastDeadline(
157
+ claims: readonly ccc.Epoch[],
158
+ { broadcastReserve }: LockUpPolicy,
159
+ ): ccc.Epoch | undefined {
160
+ const earliest = claims.reduce<ccc.Epoch | undefined>(
161
+ (best, claim) => (best === undefined || claim.compare(best) < 0 ? claim : best),
162
+ undefined,
163
+ );
164
+ return earliest?.sub(broadcastReserve);
165
+ }
166
+
167
+ /** Adds the header dep once. */
168
+ export function pushHeaderDep(tx: ccc.Transaction, hash: ccc.Hex): void {
169
+ if (!tx.headerDeps.includes(hash)) {
170
+ tx.headerDeps.push(hash);
171
+ }
172
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,89 @@
1
- export * from "./sdk.js";
2
- export * from "./codec.js";
3
- export * from "./constants.js";
1
+ /**
2
+ * Public SDK for one iCKB conversion workflow: read the sampled state, quote, build and
3
+ * complete a conversion, sign, send, and wait.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+
8
+ import type { ccc } from "@ckb-ccc/core";
9
+ import type {
10
+ AccountState,
11
+ ConversionTransactionOptions,
12
+ ConversionTransactionResult,
13
+ SystemState,
14
+ } from "./conversion/types.ts";
15
+ import type { LockUpPolicy } from "./dao.ts";
16
+ import type { OrderGroup } from "./order/cells.ts";
17
+ import { IckbSdk as IckbSdkClass } from "./sdk.ts";
18
+ import type { SupportedChain } from "./utils/chain.ts";
19
+
20
+ /** The SDK an integrator drives: the state read and the conversion builder. */
21
+ export interface IckbSdk {
22
+ /** Builds and completes a conversion, or returns a typed planning failure. */
23
+ buildConversionTransaction: (
24
+ txLike: ccc.TransactionLike,
25
+ options: ConversionTransactionOptions,
26
+ ) => Promise<ConversionTransactionResult>;
27
+ /** Reads system, user-order, and account state against one sampled tip, under one timing policy. */
28
+ getL1AccountState: (
29
+ client: ccc.Client,
30
+ locks: ccc.Script[],
31
+ lockUp: LockUpPolicy,
32
+ ) => Promise<{
33
+ system: SystemState;
34
+ user: { orders: OrderGroup[] };
35
+ account: AccountState;
36
+ }>;
37
+ }
38
+
39
+ /** Creates the SDK for one chain's deployment. */
40
+ // eslint-disable-next-line @typescript-eslint/no-redeclare -- The public type and the constructor namespace intentionally share a name.
41
+ export const IckbSdk: { fromChain: (chain: SupportedChain) => IckbSdk } = IckbSdkClass;
42
+
43
+ export { DEFAULT_ORDER_FEE, DEFAULT_ORDER_FEE_BASE } from "./conversion/estimate.ts";
44
+ export { projectConversionTransactionContext } from "./conversion/projection.ts";
45
+ export type {
46
+ AccountAvailabilityProjection,
47
+ AccountState,
48
+ ConversionDirection,
49
+ ConversionMetadata,
50
+ ConversionNotice,
51
+ ConversionTransactionContext,
52
+ ConversionTransactionContextProjection,
53
+ ConversionTransactionFailureReason,
54
+ ConversionTransactionOptions,
55
+ ConversionTransactionResult,
56
+ SystemState,
57
+ } from "./conversion/types.ts";
58
+ export { WALLET_LOCK_UP } from "./dao.ts";
59
+ export type { LockUpPolicy } from "./dao.ts";
60
+ export type { IckbDepositCell, ReceiptCell } from "./logic.ts";
61
+ export type { MasterCell, OrderCell, OrderGroup } from "./order/cells.ts";
62
+ export {
63
+ OrderConversionRepresentabilityError,
64
+ quoteConversion,
65
+ } from "./order/conversion.ts";
66
+ export { isRefused, isStale } from "./order/fill.ts";
67
+ export type { Info, InfoLike } from "./order/info.ts";
68
+ export type { Master, MasterLike } from "./order/master.ts";
69
+ export type { OrderData, OrderDataLike } from "./order/order_data.ts";
70
+ export { Ratio } from "./order/ratio.ts";
71
+ export type { Relative, RelativeLike } from "./order/relative.ts";
72
+ export type {
73
+ DaoWithdrawalRequestCell,
74
+ OwnerCell,
75
+ WithdrawalGroup,
76
+ } from "./owned_owner.ts";
77
+ export { hasTransactionActivity, IckbError } from "./sdk.ts";
78
+ export type { IckbErrorCode } from "./sdk.ts";
79
+ export {
80
+ signAndSendTransaction,
81
+ signerAccountLocks,
82
+ TransactionBroadcastError,
83
+ TransactionExpiredError,
84
+ } from "./send/sign_and_send_transaction.ts";
85
+ export { TransactionWaitError, waitTransaction } from "./send/wait_transaction.ts";
86
+ export type { WaitTransactionOptions } from "./send/wait_transaction.ts";
87
+ export { ickbExchangeRatio } from "./udt.ts";
88
+ export type { SupportedChain } from "./utils/chain.ts";
89
+ export type { ExchangeRatio, TransactionHeader, ValueComponents } from "./utils/utils.ts";