@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/conversion/plans.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { ccc } from "@ckb-ccc/core";
|
|
1
2
|
import { DAO_OUTPUT_LIMIT } from "../dao.ts";
|
|
2
3
|
import { readyDeposits, type IckbDepositCell } from "../logic.ts";
|
|
3
|
-
import { ICKB_DEPOSIT_CAP,
|
|
4
|
-
import { maxBigInt } from "../utils/utils.ts";
|
|
4
|
+
import { ICKB_DEPOSIT_CAP, ckbFromUdt } from "../udt.ts";
|
|
5
5
|
import {
|
|
6
6
|
DEFAULT_ORDER_FEE,
|
|
7
7
|
DEFAULT_ORDER_FEE_BASE,
|
|
@@ -37,7 +37,7 @@ export function ckbToIckbConversionPlans(
|
|
|
37
37
|
options: ConversionTransactionOptions,
|
|
38
38
|
): CkbToIckbConversionPlan[] {
|
|
39
39
|
const { amount, context } = options;
|
|
40
|
-
const depositCapacity =
|
|
40
|
+
const depositCapacity = ckbFromUdt(ICKB_DEPOSIT_CAP, context.system.exchangeRatio);
|
|
41
41
|
const depositQuotient = depositCapacity === 0n ? 0n : amount / depositCapacity;
|
|
42
42
|
const maxDeposits =
|
|
43
43
|
depositQuotient > BigInt(MAX_PLANNED_DEPOSITS)
|
|
@@ -98,7 +98,7 @@ function ckbToIckbConversionPlan(
|
|
|
98
98
|
if (estimate?.maturity === undefined) {
|
|
99
99
|
return undefined;
|
|
100
100
|
}
|
|
101
|
-
estimatedMaturity =
|
|
101
|
+
estimatedMaturity = ccc.numMax(estimatedMaturity, estimate.maturity);
|
|
102
102
|
order = { amounts, estimate };
|
|
103
103
|
}
|
|
104
104
|
|
|
@@ -118,7 +118,7 @@ function ickbToCkbConversionPlan(
|
|
|
118
118
|
let estimatedMaturity = context.estimatedMaturity;
|
|
119
119
|
// Selected deposits are ready, so their sampled claim is their date.
|
|
120
120
|
for (const deposit of selectedDeposits) {
|
|
121
|
-
estimatedMaturity =
|
|
121
|
+
estimatedMaturity = ccc.numMax(
|
|
122
122
|
estimatedMaturity,
|
|
123
123
|
deposit.claimEpoch.toUnix(context.system.tip),
|
|
124
124
|
);
|
|
@@ -132,7 +132,7 @@ function ickbToCkbConversionPlan(
|
|
|
132
132
|
if (estimate === undefined) {
|
|
133
133
|
return undefined;
|
|
134
134
|
}
|
|
135
|
-
estimatedMaturity =
|
|
135
|
+
estimatedMaturity = ccc.numMax(estimatedMaturity, estimate.maturity);
|
|
136
136
|
order = { amounts, estimate };
|
|
137
137
|
}
|
|
138
138
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
2
|
import { DAO_HEADER_INDEX_LIMIT } from "../dao.ts";
|
|
3
3
|
import type { OrderGroup } from "../order/cells.ts";
|
|
4
|
+
import { isRefused, isStale } from "../order/fill.ts";
|
|
4
5
|
import type { WithdrawalGroup } from "../owned_owner.ts";
|
|
5
|
-
import { maxBigInt } from "../utils/utils.ts";
|
|
6
6
|
import { maturity } from "./maturity.ts";
|
|
7
7
|
import type {
|
|
8
8
|
AccountAvailabilityProjection,
|
|
@@ -13,21 +13,29 @@ import type {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Builds the conversion planner context from account state and the caller's own orders,
|
|
16
|
-
* split by what it will melt:
|
|
17
|
-
*
|
|
16
|
+
* split by what it will melt: fulfilled orders, buys the bot's whole-fill rule refuses,
|
|
17
|
+
* and orders past the age cutoff are collected and budgeted as account value; the rest
|
|
18
|
+
* stay on the book. DAO growth can make a waiting sell profitable later, so a sell waits
|
|
19
|
+
* for the age cutoff even when the bot would not fill it today.
|
|
18
20
|
*/
|
|
19
21
|
export function projectConversionTransactionContext(
|
|
20
22
|
system: SystemState,
|
|
21
23
|
account: AccountState,
|
|
22
|
-
orders:
|
|
24
|
+
orders: readonly OrderGroup[],
|
|
23
25
|
): ConversionTransactionContextProjection {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
+
const collectable = (group: OrderGroup): boolean =>
|
|
27
|
+
group.order.isFulfilled() || isRefused(group, system) || isStale(group, system.tip);
|
|
28
|
+
const projection = projectAccountAvailability(account, {
|
|
29
|
+
available: orders.filter(collectable),
|
|
30
|
+
pending: orders.filter((group) => !collectable(group)),
|
|
31
|
+
});
|
|
32
|
+
const estimatedMaturity = ccc.numMax(
|
|
33
|
+
system.tip.timestamp,
|
|
26
34
|
...projection.pendingWithdrawals.map((group) =>
|
|
27
35
|
group.owned.maturity.toUnix(system.tip),
|
|
28
36
|
),
|
|
29
37
|
...projection.pendingOrders.map((group) => maturity(group.order, system)),
|
|
30
|
-
|
|
38
|
+
);
|
|
31
39
|
|
|
32
40
|
return {
|
|
33
41
|
projection,
|
package/src/conversion/types.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ccc } from "@ckb-ccc/core";
|
|
2
|
-
import type { LockUpPolicy } from "../dao.ts";
|
|
2
|
+
import type { BroadcastDeadline, LockUpPolicy } from "../dao.ts";
|
|
3
3
|
import type { IckbDepositCell, LogicManager, ReceiptCell } from "../logic.ts";
|
|
4
4
|
import type { OrderCell, OrderGroup } from "../order/cells.ts";
|
|
5
5
|
import type { Info } from "../order/info.ts";
|
|
6
6
|
import type { OrderManager } from "../order/order.ts";
|
|
7
7
|
import type { Ratio } from "../order/ratio.ts";
|
|
8
8
|
import type { OwnedOwnerManager, WithdrawalGroup } from "../owned_owner.ts";
|
|
9
|
-
import type { IckbUdt } from "../udt.ts";
|
|
9
|
+
import type { IckbUdt, ProtocolCell } from "../udt.ts";
|
|
10
10
|
import type { ValueComponents } from "../utils/utils.ts";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -88,13 +88,8 @@ export interface ConversionNotice {
|
|
|
88
88
|
incentiveCkb: bigint;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
*/
|
|
94
|
-
export interface ConversionMetadata {
|
|
95
|
-
/** Composition category selected for the built conversion. */
|
|
96
|
-
kind: "direct" | "order" | "direct-plus-order" | "collect-only";
|
|
97
|
-
}
|
|
91
|
+
/** Composition category selected for a built conversion. */
|
|
92
|
+
export type ConversionKind = "direct" | "order" | "direct-plus-order" | "collect-only";
|
|
98
93
|
|
|
99
94
|
/**
|
|
100
95
|
* Result of attempting to build a conversion transaction.
|
|
@@ -107,7 +102,7 @@ export type ConversionTransactionResult =
|
|
|
107
102
|
/** Estimated maturity timestamp for the conversion result. */
|
|
108
103
|
estimatedMaturity: bigint;
|
|
109
104
|
/** Composition of the selected conversion path. */
|
|
110
|
-
|
|
105
|
+
conversionKind: ConversionKind;
|
|
111
106
|
/** Optional notice about the selected path. */
|
|
112
107
|
conversionNotice?: ConversionNotice;
|
|
113
108
|
/**
|
|
@@ -119,19 +114,25 @@ export type ConversionTransactionResult =
|
|
|
119
114
|
* The epoch the transaction must be broadcast before for its withdrawal requests to
|
|
120
115
|
* claim at their sampled dates (`system.lockUp`); absent when it requests none.
|
|
121
116
|
*/
|
|
122
|
-
broadcastBefore?:
|
|
117
|
+
broadcastBefore?: BroadcastDeadline;
|
|
123
118
|
}
|
|
124
119
|
| {
|
|
125
120
|
ok: false;
|
|
126
|
-
|
|
127
|
-
reason: ConversionTransactionFailureReason;
|
|
121
|
+
reason: "amount-too-small";
|
|
128
122
|
/** Best available maturity estimate from the input context. */
|
|
129
123
|
estimatedMaturity: bigint;
|
|
130
124
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
125
|
+
* The smallest amount this direction accepts at the current fee rate, in the
|
|
126
|
+
* request's unit (CKB shannons or iCKB), so a caller can name it.
|
|
133
127
|
*/
|
|
134
|
-
minimum
|
|
128
|
+
minimum: bigint;
|
|
129
|
+
}
|
|
130
|
+
| {
|
|
131
|
+
ok: false;
|
|
132
|
+
/** Machine-readable failure reason. */
|
|
133
|
+
reason: Exclude<ConversionTransactionFailureReason, "amount-too-small">;
|
|
134
|
+
/** Best available maturity estimate from the input context. */
|
|
135
|
+
estimatedMaturity: bigint;
|
|
135
136
|
};
|
|
136
137
|
|
|
137
138
|
/**
|
|
@@ -149,6 +150,20 @@ export interface CompleteIckbTransactionOptions {
|
|
|
149
150
|
|
|
150
151
|
/** The signer's known liquid cells, plain CKB and iCKB, from the account state already read. */
|
|
151
152
|
cells: ccc.Cell[];
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The receipts and iCKB deposits the transaction may spend, valued by the state read;
|
|
156
|
+
* any superset works, since only the spent ones are summed.
|
|
157
|
+
*/
|
|
158
|
+
protocolCells: readonly ProtocolCell[];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The header the plan was priced at, the state read's `system.tip`, named as a header
|
|
162
|
+
* dep: the chain refuses a transaction whose deps are off the main chain, so a header a
|
|
163
|
+
* node invented, whose DAO rate set the exchange ratio, cannot price an accepted
|
|
164
|
+
* transaction.
|
|
165
|
+
*/
|
|
166
|
+
tip: ccc.ClientBlockHeader;
|
|
152
167
|
}
|
|
153
168
|
|
|
154
169
|
/**
|
|
@@ -244,7 +259,7 @@ export interface ConversionTransactionContextProjection {
|
|
|
244
259
|
export interface SystemState {
|
|
245
260
|
/** The fee rate for transactions. */
|
|
246
261
|
feeRate: ccc.Num;
|
|
247
|
-
/**
|
|
262
|
+
/** The sampled tip's parent, this L1 scan's anchor. It may be stale after scans complete. */
|
|
248
263
|
tip: ccc.ClientBlockHeader;
|
|
249
264
|
/** The exchange ratio between CKB and UDT. */
|
|
250
265
|
exchangeRatio: Ratio;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { ccc } from "@ckb-ccc/core";
|
|
2
|
+
import { DAO_OUTPUT_LIMIT } from "../dao.ts";
|
|
3
3
|
import type { IckbDepositCell } from "../logic.ts";
|
|
4
4
|
import { ICKB_DEPOSIT_CAP } from "../udt.ts";
|
|
5
5
|
|
|
@@ -7,9 +7,6 @@ import { ICKB_DEPOSIT_CAP } from "../udt.ts";
|
|
|
7
7
|
* Ring segment of pool deposits grouped by maturity around the DAO cycle.
|
|
8
8
|
*/
|
|
9
9
|
export interface RingSegment {
|
|
10
|
-
/** Segment index in the ring. */
|
|
11
|
-
index: number;
|
|
12
|
-
|
|
13
10
|
/** Deposits assigned to this segment. */
|
|
14
11
|
deposits: IckbDepositCell[];
|
|
15
12
|
|
|
@@ -29,8 +26,7 @@ export function ringSegments(poolDeposits: readonly IckbDepositCell[]): RingSegm
|
|
|
29
26
|
while (BigInt(segmentCount) * 2n * ICKB_DEPOSIT_CAP < totalUdt) {
|
|
30
27
|
segmentCount *= 2;
|
|
31
28
|
}
|
|
32
|
-
const segments = Array.from({ length: segmentCount }, (
|
|
33
|
-
index,
|
|
29
|
+
const segments = Array.from({ length: segmentCount }, (): RingSegment => ({
|
|
34
30
|
deposits: [],
|
|
35
31
|
udtValue: 0n,
|
|
36
32
|
}));
|
|
@@ -53,7 +49,7 @@ export function ringSegmentIndex(epoch: ccc.Epoch, segmentCount: number): number
|
|
|
53
49
|
throw new Error("Epoch denominator must be positive");
|
|
54
50
|
}
|
|
55
51
|
const scaled = epoch.integer * denominator + epoch.numerator;
|
|
56
|
-
const ring =
|
|
52
|
+
const ring = ccc.Epoch.OneNervosDaoCycle.integer * denominator;
|
|
57
53
|
const wrapped = ((scaled % ring) + ring) % ring;
|
|
58
54
|
return Number((wrapped * BigInt(segmentCount)) / ring);
|
|
59
55
|
}
|
package/src/dao.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
|
-
import { CheckedUint64LE } from "./utils/codec.ts";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Maximum outputs accepted by deployed dao.c, not a general CKB consensus cap.
|
|
@@ -15,9 +14,6 @@ export const DAO_OUTPUT_LIMIT = 64;
|
|
|
15
14
|
*/
|
|
16
15
|
export const DAO_HEADER_INDEX_LIMIT = 256;
|
|
17
16
|
|
|
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
17
|
/**
|
|
22
18
|
* Error thrown when a transaction exceeds the deployed DAO script's output limit.
|
|
23
19
|
*/
|
|
@@ -76,15 +72,13 @@ export function assertDaoOutputLimit(
|
|
|
76
72
|
}
|
|
77
73
|
}
|
|
78
74
|
|
|
79
|
-
/** The canonical DAO deposit data payload. */
|
|
80
|
-
export
|
|
81
|
-
return ccc.hexFrom(CheckedUint64LE.encode(0n));
|
|
82
|
-
}
|
|
75
|
+
/** The canonical DAO deposit data payload: a zero Uint64, eight zero bytes. */
|
|
76
|
+
export const DAO_DEPOSIT_DATA: ccc.Hex = "0x0000000000000000";
|
|
83
77
|
|
|
84
78
|
/** Whether the cell is a DAO deposit of the given DAO script. */
|
|
85
79
|
export function isDaoDeposit(cell: ccc.CellAny, daoScript: ccc.Script): boolean {
|
|
86
80
|
return (
|
|
87
|
-
cell.outputData ===
|
|
81
|
+
cell.outputData === DAO_DEPOSIT_DATA && cell.cellOutput.type?.eq(daoScript) === true
|
|
88
82
|
);
|
|
89
83
|
}
|
|
90
84
|
|
|
@@ -97,7 +91,7 @@ export function isDaoWithdrawalRequest(
|
|
|
97
91
|
daoScript: ccc.Script,
|
|
98
92
|
): boolean {
|
|
99
93
|
return (
|
|
100
|
-
cell.outputData !==
|
|
94
|
+
cell.outputData !== DAO_DEPOSIT_DATA && cell.cellOutput.type?.eq(daoScript) === true
|
|
101
95
|
);
|
|
102
96
|
}
|
|
103
97
|
|
|
@@ -136,6 +130,25 @@ export const WALLET_LOCK_UP: LockUpPolicy = {
|
|
|
136
130
|
broadcastReserve: ccc.Epoch.from([0n, 3n, 8n]),
|
|
137
131
|
};
|
|
138
132
|
|
|
133
|
+
/**
|
|
134
|
+
* How far a tip may trail the local clock under a policy: the selection margin less the
|
|
135
|
+
* broadcast reserve, the slack a stale tip would eat before a request commits after its
|
|
136
|
+
* claim. Five minutes for the bot, thirty for a wallet.
|
|
137
|
+
*/
|
|
138
|
+
export function tipMaxAgeMs(policy: LockUpPolicy): number {
|
|
139
|
+
// The span from the reserve to the margin, on CCC's nominal four-hour epoch.
|
|
140
|
+
return Number(
|
|
141
|
+
policy.minLockUp.toUnix({ epoch: policy.broadcastReserve, timestamp: 0n }),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Throws when `header` trails the local clock by more than `maxAgeMs`. */
|
|
146
|
+
export function assertFresh(header: ccc.ClientBlockHeader, maxAgeMs: number): void {
|
|
147
|
+
if (BigInt(Date.now()) - header.timestamp > BigInt(maxAgeMs)) {
|
|
148
|
+
throw new Error(`Block ${header.hash} is more than ${String(maxAgeMs)} ms old`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
139
152
|
/** A deposit's maturity, rolled a cycle when its claim is too close, and whether it is ready. */
|
|
140
153
|
export function depositMaturity(
|
|
141
154
|
claim: ccc.Epoch,
|
|
@@ -144,7 +157,7 @@ export function depositMaturity(
|
|
|
144
157
|
): { maturity: ccc.Epoch; isReady: boolean } {
|
|
145
158
|
const maturity =
|
|
146
159
|
claim.compare(minLockUp.add(tip.epoch)) <= 0
|
|
147
|
-
? claim.add(
|
|
160
|
+
? claim.add(ccc.Epoch.OneNervosDaoCycle)
|
|
148
161
|
: claim;
|
|
149
162
|
return { maturity, isReady: maxLockUp.add(tip.epoch).compare(maturity) > 0 };
|
|
150
163
|
}
|
|
@@ -155,13 +168,24 @@ export function depositMaturity(
|
|
|
155
168
|
*/
|
|
156
169
|
export function broadcastDeadline(
|
|
157
170
|
claims: readonly ccc.Epoch[],
|
|
158
|
-
|
|
159
|
-
):
|
|
171
|
+
policy: LockUpPolicy,
|
|
172
|
+
): BroadcastDeadline | undefined {
|
|
160
173
|
const earliest = claims.reduce<ccc.Epoch | undefined>(
|
|
161
174
|
(best, claim) => (best === undefined || claim.compare(best) < 0 ? claim : best),
|
|
162
175
|
undefined,
|
|
163
176
|
);
|
|
164
|
-
return earliest
|
|
177
|
+
return earliest === undefined
|
|
178
|
+
? undefined
|
|
179
|
+
: { epoch: earliest.sub(policy.broadcastReserve), tipMaxAgeMs: tipMaxAgeMs(policy) };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The epoch a withdrawal request must be broadcast before, and how far the tip read to
|
|
184
|
+
* check it may trail the clock, the policy's slack: a stale tip would pass the check late.
|
|
185
|
+
*/
|
|
186
|
+
export interface BroadcastDeadline {
|
|
187
|
+
epoch: ccc.Epoch;
|
|
188
|
+
tipMaxAgeMs: number;
|
|
165
189
|
}
|
|
166
190
|
|
|
167
191
|
/** Adds the header dep once. */
|
package/src/index.ts
CHANGED
|
@@ -21,17 +21,17 @@ import type { SupportedChain } from "./utils/chain.ts";
|
|
|
21
21
|
export interface IckbSdk {
|
|
22
22
|
/** Builds and completes a conversion, or returns a typed planning failure. */
|
|
23
23
|
buildConversionTransaction: (
|
|
24
|
-
txLike: ccc.TransactionLike,
|
|
25
24
|
options: ConversionTransactionOptions,
|
|
26
25
|
) => Promise<ConversionTransactionResult>;
|
|
27
|
-
/** Reads system, user-order, and account state
|
|
26
|
+
/** Reads system, user-order, and account state at the sampled tip's parent, under one timing policy. */
|
|
28
27
|
getL1AccountState: (
|
|
29
28
|
client: ccc.Client,
|
|
30
29
|
locks: ccc.Script[],
|
|
31
30
|
lockUp: LockUpPolicy,
|
|
31
|
+
maxFeeRate?: ccc.NumLike,
|
|
32
32
|
) => Promise<{
|
|
33
33
|
system: SystemState;
|
|
34
|
-
|
|
34
|
+
userOrders: OrderGroup[];
|
|
35
35
|
account: AccountState;
|
|
36
36
|
}>;
|
|
37
37
|
}
|
|
@@ -46,7 +46,7 @@ export type {
|
|
|
46
46
|
AccountAvailabilityProjection,
|
|
47
47
|
AccountState,
|
|
48
48
|
ConversionDirection,
|
|
49
|
-
|
|
49
|
+
ConversionKind,
|
|
50
50
|
ConversionNotice,
|
|
51
51
|
ConversionTransactionContext,
|
|
52
52
|
ConversionTransactionContextProjection,
|
|
@@ -56,14 +56,13 @@ export type {
|
|
|
56
56
|
SystemState,
|
|
57
57
|
} from "./conversion/types.ts";
|
|
58
58
|
export { WALLET_LOCK_UP } from "./dao.ts";
|
|
59
|
-
export type { LockUpPolicy } from "./dao.ts";
|
|
59
|
+
export type { BroadcastDeadline, LockUpPolicy } from "./dao.ts";
|
|
60
60
|
export type { IckbDepositCell, ReceiptCell } from "./logic.ts";
|
|
61
61
|
export type { MasterCell, OrderCell, OrderGroup } from "./order/cells.ts";
|
|
62
62
|
export {
|
|
63
63
|
OrderConversionRepresentabilityError,
|
|
64
64
|
quoteConversion,
|
|
65
65
|
} from "./order/conversion.ts";
|
|
66
|
-
export { isRefused, isStale } from "./order/fill.ts";
|
|
67
66
|
export type { Info, InfoLike } from "./order/info.ts";
|
|
68
67
|
export type { Master, MasterLike } from "./order/master.ts";
|
|
69
68
|
export type { OrderData, OrderDataLike } from "./order/order_data.ts";
|
|
@@ -74,7 +73,7 @@ export type {
|
|
|
74
73
|
OwnerCell,
|
|
75
74
|
WithdrawalGroup,
|
|
76
75
|
} from "./owned_owner.ts";
|
|
77
|
-
export {
|
|
76
|
+
export { IckbError } from "./sdk.ts";
|
|
78
77
|
export type { IckbErrorCode } from "./sdk.ts";
|
|
79
78
|
export {
|
|
80
79
|
signAndSendTransaction,
|
|
@@ -86,4 +85,5 @@ export { TransactionWaitError, waitTransaction } from "./send/wait_transaction.t
|
|
|
86
85
|
export type { WaitTransactionOptions } from "./send/wait_transaction.ts";
|
|
87
86
|
export { ickbExchangeRatio } from "./udt.ts";
|
|
88
87
|
export type { SupportedChain } from "./utils/chain.ts";
|
|
89
|
-
export
|
|
88
|
+
export { clientCache } from "./utils/utils.ts";
|
|
89
|
+
export type { ExchangeRatio, ValueComponents } from "./utils/utils.ts";
|
package/src/logic.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
2
|
import {
|
|
3
3
|
assertDaoOutputLimit,
|
|
4
|
+
DAO_DEPOSIT_DATA,
|
|
4
5
|
DaoOutputLimitError,
|
|
5
|
-
depositData,
|
|
6
6
|
depositMaturity,
|
|
7
7
|
isDaoDeposit,
|
|
8
8
|
pushHeaderDep,
|
|
@@ -16,11 +16,11 @@ import {
|
|
|
16
16
|
receiptDataBytes,
|
|
17
17
|
type ReceiptData,
|
|
18
18
|
} from "./udt.ts";
|
|
19
|
-
import { transactionHeaders } from "./utils/transaction_header.ts";
|
|
20
19
|
import {
|
|
21
20
|
findCells,
|
|
21
|
+
headersByNumber,
|
|
22
|
+
type ScannedCell,
|
|
22
23
|
type ScriptDeps,
|
|
23
|
-
type TransactionHeader,
|
|
24
24
|
type ValueComponents,
|
|
25
25
|
} from "./utils/utils.ts";
|
|
26
26
|
|
|
@@ -36,10 +36,8 @@ const phase2TxFeeReserve = ccc.One;
|
|
|
36
36
|
export interface IckbDepositCell extends ValueComponents {
|
|
37
37
|
/** The DAO deposit cell. */
|
|
38
38
|
cell: ccc.Cell;
|
|
39
|
-
/** The
|
|
40
|
-
|
|
41
|
-
/** The DAO interest accrued between the two headers. */
|
|
42
|
-
interests: ccc.Num;
|
|
39
|
+
/** The header of the block that committed the deposit, which values it. */
|
|
40
|
+
header: ccc.ClientBlockHeader;
|
|
43
41
|
/**
|
|
44
42
|
* The DAO claim epoch as sampled at the tip, not rolled: each reader judges it against
|
|
45
43
|
* its own {@link LockUpPolicy} with `depositMaturity`.
|
|
@@ -53,8 +51,8 @@ export interface IckbDepositCell extends ValueComponents {
|
|
|
53
51
|
export interface ReceiptCell extends ValueComponents {
|
|
54
52
|
/** The cell associated with the receipt. */
|
|
55
53
|
cell: ccc.Cell;
|
|
56
|
-
/** The header of the
|
|
57
|
-
header:
|
|
54
|
+
/** The header of the block that committed the deposits, which values them. */
|
|
55
|
+
header: ccc.ClientBlockHeader;
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
/**
|
|
@@ -94,8 +92,8 @@ export class LogicManager implements ScriptDeps {
|
|
|
94
92
|
* Adds `depositQuantity` DAO deposits of `depositCapacity` locked by the logic script,
|
|
95
93
|
* then the receipt that mints their iCKB in phase 2, locked by `lock`.
|
|
96
94
|
*
|
|
97
|
-
* @remarks
|
|
98
|
-
*
|
|
95
|
+
* @remarks Completion (`IckbSdk.completeTransaction`) adds the dep group that also
|
|
96
|
+
* resolves the xUDT script.
|
|
99
97
|
*/
|
|
100
98
|
public deposit(
|
|
101
99
|
txLike: ccc.TransactionLike,
|
|
@@ -103,13 +101,11 @@ export class LogicManager implements ScriptDeps {
|
|
|
103
101
|
depositCapacity: ccc.FixedPoint,
|
|
104
102
|
lock: ccc.Script,
|
|
105
103
|
): ccc.Transaction {
|
|
106
|
-
|
|
107
|
-
if (depositQuantity <= 0) {
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
if (!Number.isSafeInteger(depositQuantity)) {
|
|
111
|
-
throw new TypeError("iCKB deposit quantity must be a safe integer");
|
|
104
|
+
// A zero quantity would mint a receipt for no deposits, which iCKB Logic refuses on chain.
|
|
105
|
+
if (!Number.isSafeInteger(depositQuantity) || depositQuantity <= 0) {
|
|
106
|
+
throw new TypeError("iCKB deposit quantity must be a positive safe integer");
|
|
112
107
|
}
|
|
108
|
+
const tx = ccc.Transaction.from(txLike);
|
|
113
109
|
if (depositQuantity > maxDepositQuantity) {
|
|
114
110
|
// The completion walk steps a plan down on this error.
|
|
115
111
|
throw new DaoOutputLimitError(tx.outputs.length + depositQuantity + 1);
|
|
@@ -118,7 +114,7 @@ export class LogicManager implements ScriptDeps {
|
|
|
118
114
|
const depositCell = ccc.Cell.from({
|
|
119
115
|
previousOutput: { txHash: `0x${"00".repeat(32)}`, index: 0 },
|
|
120
116
|
cellOutput: { capacity: depositCapacity, lock: this.script, type: this.dao.script },
|
|
121
|
-
outputData:
|
|
117
|
+
outputData: DAO_DEPOSIT_DATA,
|
|
122
118
|
});
|
|
123
119
|
const depositAmount = depositCell.capacityFree;
|
|
124
120
|
if (depositAmount < ccc.fixedPointFrom(1000)) {
|
|
@@ -167,15 +163,10 @@ export class LogicManager implements ScriptDeps {
|
|
|
167
163
|
`Receipt ${outPoint} is not an iCKB receipt for this logic script`,
|
|
168
164
|
);
|
|
169
165
|
}
|
|
170
|
-
if (receipt.header.txHash !== receipt.cell.outPoint.txHash) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`Receipt ${outPoint} header txHash ${String(receipt.header.txHash)} does not match cell txHash ${receipt.cell.outPoint.txHash}`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
166
|
}
|
|
176
167
|
tx.addCellDeps(this.cellDeps);
|
|
177
168
|
for (const receipt of receipts) {
|
|
178
|
-
pushHeaderDep(tx, receipt.header.
|
|
169
|
+
pushHeaderDep(tx, receipt.header.hash);
|
|
179
170
|
tx.addInput(receipt.cell);
|
|
180
171
|
}
|
|
181
172
|
return tx;
|
|
@@ -186,23 +177,22 @@ export class LogicManager implements ScriptDeps {
|
|
|
186
177
|
*/
|
|
187
178
|
public async receiptsFrom(
|
|
188
179
|
client: ccc.Client,
|
|
189
|
-
cells: readonly
|
|
180
|
+
cells: readonly ScannedCell[],
|
|
190
181
|
): Promise<ReceiptCell[]> {
|
|
191
|
-
const receipts = cells.filter((cell) => this.isReceipt(cell));
|
|
192
|
-
const headers = await
|
|
182
|
+
const receipts = cells.filter(({ cell }) => this.isReceipt(cell));
|
|
183
|
+
const headers = await headersByNumber(
|
|
193
184
|
client,
|
|
194
|
-
receipts.map((
|
|
185
|
+
receipts.map(({ blockNumber }) => blockNumber),
|
|
186
|
+
);
|
|
187
|
+
return receipts.map(({ cell, blockNumber }) =>
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- every receipt's block was read above.
|
|
189
|
+
receiptCell(cell, headers.get(blockNumber)!),
|
|
195
190
|
);
|
|
196
|
-
return receipts.map((cell) => {
|
|
197
|
-
const { txHash } = cell.outPoint;
|
|
198
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- every receipt's hash was read above.
|
|
199
|
-
return receiptCell(cell, { header: headers.get(txHash)!, txHash });
|
|
200
|
-
});
|
|
201
191
|
}
|
|
202
192
|
|
|
203
193
|
/**
|
|
204
194
|
* Every live iCKB deposit, valued at its deposit header, with its claim epoch at the tip;
|
|
205
|
-
* each deposit
|
|
195
|
+
* each deposit block's header is read once.
|
|
206
196
|
*/
|
|
207
197
|
public async findDeposits(
|
|
208
198
|
client: ccc.Client,
|
|
@@ -214,39 +204,37 @@ export class LogicManager implements ScriptDeps {
|
|
|
214
204
|
scriptType: "lock",
|
|
215
205
|
filter: {
|
|
216
206
|
script: this.dao.script,
|
|
217
|
-
outputData:
|
|
207
|
+
outputData: DAO_DEPOSIT_DATA,
|
|
218
208
|
outputDataSearchMode: "exact",
|
|
219
209
|
},
|
|
220
210
|
scriptSearchMode: "exact",
|
|
221
211
|
withData: true,
|
|
222
212
|
})
|
|
223
|
-
).filter((cell) => this.isDeposit(cell));
|
|
224
|
-
const headers = await
|
|
213
|
+
).filter(({ cell }) => this.isDeposit(cell));
|
|
214
|
+
const headers = await headersByNumber(
|
|
225
215
|
client,
|
|
226
|
-
cells.map((
|
|
216
|
+
cells.map(({ blockNumber }) => blockNumber),
|
|
217
|
+
);
|
|
218
|
+
return cells.map(({ cell, blockNumber }) =>
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- every deposit's block was read above.
|
|
220
|
+
ickbDepositCell(cell, headers.get(blockNumber)!, tip),
|
|
227
221
|
);
|
|
228
|
-
return cells.map((cell) => {
|
|
229
|
-
const { txHash } = cell.outPoint;
|
|
230
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- every deposit's hash was read above.
|
|
231
|
-
return ickbDepositCell(cell, { header: headers.get(txHash)!, txHash }, tip);
|
|
232
|
-
});
|
|
233
222
|
}
|
|
234
223
|
}
|
|
235
224
|
|
|
236
225
|
/** Values a DAO deposit cell at its deposit header and samples its claim epoch at the tip. */
|
|
237
|
-
|
|
226
|
+
function ickbDepositCell(
|
|
238
227
|
cell: ccc.Cell,
|
|
239
|
-
|
|
228
|
+
header: ccc.ClientBlockHeader,
|
|
240
229
|
tip: ccc.ClientBlockHeader,
|
|
241
230
|
): IckbDepositCell {
|
|
242
|
-
const interests = ccc.calcDaoProfit(cell.capacityFree,
|
|
231
|
+
const interests = ccc.calcDaoProfit(cell.capacityFree, header, tip);
|
|
243
232
|
return {
|
|
244
233
|
cell,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
claimEpoch: ccc.calcDaoClaimEpoch(depositHeader.header, tip),
|
|
234
|
+
header,
|
|
235
|
+
claimEpoch: ccc.calcDaoClaimEpoch(header, tip),
|
|
248
236
|
ckbValue: cell.cellOutput.capacity + interests,
|
|
249
|
-
udtValue: ickbValue(cell.capacityFree,
|
|
237
|
+
udtValue: ickbValue(cell.capacityFree, header),
|
|
250
238
|
};
|
|
251
239
|
}
|
|
252
240
|
|
|
@@ -262,7 +250,7 @@ export function readyDeposits(
|
|
|
262
250
|
}
|
|
263
251
|
|
|
264
252
|
/** Decodes a receipt cell and values it at its deposit header. */
|
|
265
|
-
export function receiptCell(cell: ccc.Cell, header:
|
|
253
|
+
export function receiptCell(cell: ccc.Cell, header: ccc.ClientBlockHeader): ReceiptCell {
|
|
266
254
|
let receipt: ReceiptData;
|
|
267
255
|
try {
|
|
268
256
|
receipt = decodeReceiptData(cell.outputData);
|
|
@@ -276,7 +264,7 @@ export function receiptCell(cell: ccc.Cell, header: TransactionHeader): ReceiptC
|
|
|
276
264
|
cell,
|
|
277
265
|
header,
|
|
278
266
|
ckbValue: cell.cellOutput.capacity,
|
|
279
|
-
udtValue: ickbValue(receipt.depositAmount, header
|
|
267
|
+
udtValue: ickbValue(receipt.depositAmount, header) * receipt.depositQuantity,
|
|
280
268
|
};
|
|
281
269
|
}
|
|
282
270
|
|