ckb-session-kit 2.0.0 → 2.2.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/CHANGELOG.md +93 -0
- package/README.md +863 -718
- package/dist/confirmations.d.ts +46 -0
- package/dist/confirmations.js +58 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/onchain.d.ts +131 -2
- package/dist/onchain.js +307 -2
- package/dist/paywall.d.ts +11 -1
- package/dist/paywall.js +15 -0
- package/dist/session.d.ts +7 -2
- package/dist/subscription.d.ts +34 -3
- package/dist/subscription.js +54 -6
- package/package.json +3 -1
package/dist/paywall.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
2
|
import type { Session } from "./session.js";
|
|
3
|
+
import { type ConfirmationFault } from "./confirmations.js";
|
|
3
4
|
/** The memo for a payment: a short UTF-8 label, e.g. `read:article-1`. */
|
|
4
5
|
export declare function paymentMemo(label: string): ccc.Hex;
|
|
5
6
|
/** Whether `lock` runs the standard secp256k1-blake160 sighash_all code (`code`: that script on this network). */
|
|
@@ -46,15 +47,24 @@ export type AccessCheck = {
|
|
|
46
47
|
} | {
|
|
47
48
|
ok: false;
|
|
48
49
|
reason: string;
|
|
49
|
-
|
|
50
|
+
code?: undefined;
|
|
51
|
+
} | ConfirmationFault;
|
|
50
52
|
/**
|
|
51
53
|
* The server's side. `to` is the recipient that must have been paid, as a lock
|
|
52
54
|
* script; `minAmount` in shannons. With `token` (an xUDT type script), the
|
|
53
55
|
* payment is counted in that token's units instead, and `minAmount` is in units.
|
|
56
|
+
*
|
|
57
|
+
* With `minConfirmations` (default 0: committed is enough), a payment that is
|
|
58
|
+
* valid in every other way but fewer than that many blocks deep (counting its
|
|
59
|
+
* own block as 1) gives `code: "unconfirmed"` with its `confirmations`, and one
|
|
60
|
+
* whose block left the main chain `code: "reorged"` (src/confirmations.ts).
|
|
61
|
+
* Throws a `RangeError` for a `minConfirmations` that is not an integer from 0
|
|
62
|
+
* to `MAX_MIN_CONFIRMATIONS`.
|
|
54
63
|
*/
|
|
55
64
|
export declare function verifyAccess(client: ccc.Client, proof: AccessProof, expect: {
|
|
56
65
|
to: ccc.ScriptLike;
|
|
57
66
|
minAmount: bigint;
|
|
58
67
|
memo: ccc.HexLike;
|
|
59
68
|
token?: ccc.ScriptLike;
|
|
69
|
+
minConfirmations?: number | bigint;
|
|
60
70
|
}): Promise<AccessCheck>;
|
package/dist/paywall.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
2
|
import { tokenAmount } from "./onchain.js";
|
|
3
3
|
import { sessionSigner } from "./transfer.js";
|
|
4
|
+
import { confirmationFault, minConfirmationsFrom } from "./confirmations.js";
|
|
4
5
|
/**
|
|
5
6
|
* Paid access, checked against the chain with no database.
|
|
6
7
|
*
|
|
@@ -88,8 +89,16 @@ export async function signAccess(session, txHash, memo) {
|
|
|
88
89
|
* The server's side. `to` is the recipient that must have been paid, as a lock
|
|
89
90
|
* script; `minAmount` in shannons. With `token` (an xUDT type script), the
|
|
90
91
|
* payment is counted in that token's units instead, and `minAmount` is in units.
|
|
92
|
+
*
|
|
93
|
+
* With `minConfirmations` (default 0: committed is enough), a payment that is
|
|
94
|
+
* valid in every other way but fewer than that many blocks deep (counting its
|
|
95
|
+
* own block as 1) gives `code: "unconfirmed"` with its `confirmations`, and one
|
|
96
|
+
* whose block left the main chain `code: "reorged"` (src/confirmations.ts).
|
|
97
|
+
* Throws a `RangeError` for a `minConfirmations` that is not an integer from 0
|
|
98
|
+
* to `MAX_MIN_CONFIRMATIONS`.
|
|
91
99
|
*/
|
|
92
100
|
export async function verifyAccess(client, proof, expect) {
|
|
101
|
+
const minConfirmations = minConfirmationsFrom(expect.minConfirmations);
|
|
93
102
|
if (ccc.hexFrom(proof.memo) !== ccc.hexFrom(expect.memo))
|
|
94
103
|
return { ok: false, reason: "memo does not match" };
|
|
95
104
|
// Always the CKB secp256k1 scheme: a sign type chosen by the caller is not trusted.
|
|
@@ -171,5 +180,11 @@ export async function verifyAccess(client, proof, expect) {
|
|
|
171
180
|
const shown = token ? `${paid} units` : `${ccc.fixedPointToString(paid)} CKB`;
|
|
172
181
|
return { ok: false, reason: `paid ${shown}, below the price` };
|
|
173
182
|
}
|
|
183
|
+
// Depth last: "unconfirmed" then means the payment is good, only too recent.
|
|
184
|
+
if (minConfirmations > 0n) {
|
|
185
|
+
const fault = await confirmationFault(client, await client.getTip(), res, minConfirmations, "payment");
|
|
186
|
+
if (fault)
|
|
187
|
+
return fault;
|
|
188
|
+
}
|
|
174
189
|
return { ok: true, paid };
|
|
175
190
|
}
|
package/dist/session.d.ts
CHANGED
|
@@ -50,9 +50,14 @@ export interface CreateOptions {
|
|
|
50
50
|
* Why a policy error is not about the request itself: "payto-not-anyone-can-pay",
|
|
51
51
|
* the payer's own setup (a charge can never top such a `payTo` up); or
|
|
52
52
|
* "too-many-cells", more cells under a subscription's lock than a status or a
|
|
53
|
-
* charge reads (`MAX_SUBSCRIPTION_CELLS`).
|
|
53
|
+
* charge reads (`MAX_SUBSCRIPTION_CELLS`). From `fundInSession`:
|
|
54
|
+
* "session-has-recipient", the session has an on-chain recipient, so it cannot
|
|
55
|
+
* fund outputs to other locks; "owner-lock-input", the app transaction spends a
|
|
56
|
+
* cell under the owner's lock, which would switch the session lock to owner
|
|
57
|
+
* mode; "app-tx-shape", the app transaction already holds cells only the kit
|
|
58
|
+
* may add (session or key cells), or its inputs exceed its outputs.
|
|
54
59
|
*/
|
|
55
|
-
export type SessionPolicyReason = "payto-not-anyone-can-pay" | "too-many-cells" | "owner-lock-unsafe" | "owner-lock-unreviewed";
|
|
60
|
+
export type SessionPolicyReason = "payto-not-anyone-can-pay" | "too-many-cells" | "owner-lock-unsafe" | "owner-lock-unreviewed" | "session-has-recipient" | "owner-lock-input" | "app-tx-shape";
|
|
56
61
|
export declare class SessionPolicyError extends Error {
|
|
57
62
|
readonly reason?: SessionPolicyReason;
|
|
58
63
|
constructor(message: string, reason?: SessionPolicyReason);
|
package/dist/subscription.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
2
|
import { type SessionLockDeployment } from "./lock.js";
|
|
3
|
-
import { type SigningLockOptions, type RetryOptions } from "./onchain.js";
|
|
3
|
+
import { type SigningLockOptions, type SessionWarning, type RetryOptions } from "./onchain.js";
|
|
4
|
+
import { type ConfirmationFault } from "./confirmations.js";
|
|
4
5
|
/**
|
|
5
6
|
* Subscriptions (docs/design/subscriptions.md): the same session lock, with the
|
|
6
7
|
* roles moved. The subscriber is the owner; the creator's charger key is the
|
|
@@ -126,6 +127,12 @@ export declare function openSubscription(subscriber: ccc.Signer, deployment: Ses
|
|
|
126
127
|
beforeSend?: (sub: Subscription) => Promise<void>;
|
|
127
128
|
/** Let an unreviewed charger lock through, with a warning (`allowUnreviewedOwnerLock` covers the subscriber's own lock only). */
|
|
128
129
|
allowUnreviewedChargerLock?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Called before anything is signed with a `SessionWarning` about the
|
|
132
|
+
* subscriber's lock (`"owner-lock-build-unreviewed"` or
|
|
133
|
+
* `"owner-lock-build-unresolved"`); throw to stop. Without it, `console.warn`.
|
|
134
|
+
*/
|
|
135
|
+
onWarning?: (warning: SessionWarning) => void | Promise<void>;
|
|
129
136
|
} & SigningLockOptions): Promise<{
|
|
130
137
|
txHash: ccc.Hex;
|
|
131
138
|
subscription: Subscription;
|
|
@@ -233,7 +240,8 @@ export type SubscriptionCheck = {
|
|
|
233
240
|
} | {
|
|
234
241
|
ok: false;
|
|
235
242
|
reason: string;
|
|
236
|
-
|
|
243
|
+
code?: undefined;
|
|
244
|
+
} | ConfirmationFault;
|
|
237
245
|
/**
|
|
238
246
|
* The creator checks a subscription from the chain alone. Nothing in the request
|
|
239
247
|
* is trusted but the out point. The cell at `outPoint` must be live, under this
|
|
@@ -257,6 +265,13 @@ export type SubscriptionCheck = {
|
|
|
257
265
|
* type hash, `price` in token units as `max_per_tx_token`); `minBalance` is in token
|
|
258
266
|
* units, and the payment counted is token units into `payTo`'s cells of the token.
|
|
259
267
|
* Without `token`, a 160-byte cell is refused, and the reverse.
|
|
268
|
+
*
|
|
269
|
+
* With `minConfirmations` (default 0: committed is enough), the creating
|
|
270
|
+
* transaction must be that many blocks deep, its own block counting as 1: a cell
|
|
271
|
+
* valid in every other way gives `code: "unconfirmed"` until then, and
|
|
272
|
+
* `code: "reorged"` if that block left the main chain (src/confirmations.ts).
|
|
273
|
+
* Throws a `RangeError` for a `minConfirmations` that is not an integer from 0
|
|
274
|
+
* to `MAX_MIN_CONFIRMATIONS`.
|
|
260
275
|
*/
|
|
261
276
|
export declare function verifySubscription(client: ccc.Client, deployment: SessionLockDeployment, outPoint: ccc.OutPointLike, expected: {
|
|
262
277
|
charger: string;
|
|
@@ -266,6 +281,7 @@ export declare function verifySubscription(client: ccc.Client, deployment: Sessi
|
|
|
266
281
|
minBalance?: bigint;
|
|
267
282
|
account?: string;
|
|
268
283
|
token?: ccc.ScriptLike;
|
|
284
|
+
minConfirmations?: number | bigint;
|
|
269
285
|
}): Promise<SubscriptionCheck>;
|
|
270
286
|
export interface SubscriptionStatus {
|
|
271
287
|
/**
|
|
@@ -283,6 +299,11 @@ export interface SubscriptionStatus {
|
|
|
283
299
|
chargesLeft: bigint;
|
|
284
300
|
/** Blocks until the creator may charge again (0 = now). */
|
|
285
301
|
nextChargeIn: bigint | undefined;
|
|
302
|
+
/**
|
|
303
|
+
* With `minConfirmations`: the block a charge still short of that depth will
|
|
304
|
+
* pay through once it has it, when that is past `paidThroughBlock`. Not access yet.
|
|
305
|
+
*/
|
|
306
|
+
unconfirmedThroughBlock?: bigint;
|
|
286
307
|
}
|
|
287
308
|
/**
|
|
288
309
|
* Where a subscription stands, for a creator's server that keeps no database:
|
|
@@ -298,17 +319,27 @@ export interface SubscriptionStatus {
|
|
|
298
319
|
* `MAX_SUBSCRIPTION_CELLS` cells (or recent transactions) sit under the lock:
|
|
299
320
|
* not a verdict on the subscription, so not an `ok: false`. A server should
|
|
300
321
|
* answer it as a conflict the subscriber can clear by cancelling and opening again.
|
|
322
|
+
*
|
|
323
|
+
* With `minConfirmations` (default 0: committed is enough), only payments that
|
|
324
|
+
* many blocks deep count, their own block counting as 1, and still on the main
|
|
325
|
+
* chain. An opening short of it gives `code: "unconfirmed"` (or `"reorged"`); a
|
|
326
|
+
* charge short of it does not extend `paidThroughBlock` yet, and shows in
|
|
327
|
+
* `unconfirmedThroughBlock`. The tip is read once. Throws a `RangeError` for a
|
|
328
|
+
* `minConfirmations` that is not an integer from 0 to `MAX_MIN_CONFIRMATIONS`.
|
|
301
329
|
*/
|
|
302
330
|
export declare function subscriptionStatus(client: ccc.Client, deployment: SessionLockDeployment, sub: Subscription, proof: {
|
|
303
331
|
openTx: ccc.HexLike;
|
|
304
332
|
account: string;
|
|
333
|
+
}, options?: {
|
|
334
|
+
minConfirmations?: number | bigint;
|
|
305
335
|
}): Promise<{
|
|
306
336
|
ok: true;
|
|
307
337
|
status: SubscriptionStatus;
|
|
308
338
|
} | {
|
|
309
339
|
ok: false;
|
|
310
340
|
reason: string;
|
|
311
|
-
|
|
341
|
+
code?: undefined;
|
|
342
|
+
} | ConfirmationFault>;
|
|
312
343
|
/**
|
|
313
344
|
* About how many blocks `seconds` is, from the average block time over the last
|
|
314
345
|
* `sample` blocks. Block time varies; round up when a period means "no more often than".
|
package/dist/subscription.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
2
|
import { relativeBlocksSince, sessionLockCellDep, sessionLockScript, } from "./lock.js";
|
|
3
|
-
import { addTopUp, assertPlainXudt, assertSigningLock, checkSigningLock, isAnyoneCanPay, keyCellFloor, ownerSweep, ownOrigin, plainXudtFault, randomPick, completeFeeOrNoFeeCell, rebuildOnDeadInputs, SessionBalanceError, signAndSend, tokenAmount, tokenData, withoutDeadCached, withinU128, } from "./onchain.js";
|
|
3
|
+
import { addTopUp, assertPlainXudt, assertSigningLock, checkSigningLock, isAnyoneCanPay, keyCellFloor, ownerSweep, ownOrigin, plainXudtFault, randomPick, completeFeeOrNoFeeCell, rebuildOnDeadInputs, SessionBalanceError, signAndSend, tokenAmount, tokenData, warnOwnerLockBuild, withoutDeadCached, withinU128, } from "./onchain.js";
|
|
4
4
|
import { assertCurrentDeployment } from "./deployment.js";
|
|
5
5
|
import { SessionPolicyError } from "./session.js";
|
|
6
|
+
import { confirmationFault, minConfirmationsFrom } from "./confirmations.js";
|
|
6
7
|
const tokenOf = (sub) => sub.token && ccc.Script.from(sub.token.type);
|
|
7
8
|
export function subscriptionLock(deployment, sub) {
|
|
8
9
|
const token = tokenOf(sub);
|
|
@@ -297,6 +298,7 @@ export async function openSubscription(subscriber, deployment, opts) {
|
|
|
297
298
|
}
|
|
298
299
|
const subscriberLock = (await subscriber.getRecommendedAddressObj()).script;
|
|
299
300
|
await assertSigningLock(subscriberLock, client, opts);
|
|
301
|
+
await warnOwnerLockBuild(subscriberLock, client, opts.onWarning);
|
|
300
302
|
const [chargerLock, payToLock] = await Promise.all([opts.charger, opts.payTo].map(async (a) => (await ccc.Address.fromString(a, client)).script));
|
|
301
303
|
// A charger lock that signs nothing would let anyone trigger charges (review M2). Its own
|
|
302
304
|
// opt-in: opting in for the subscriber's wallet does not waive review of the creator's lock (review W6 I1).
|
|
@@ -394,7 +396,9 @@ export async function findSubscriptionCells(client, deployment, sub) {
|
|
|
394
396
|
* cell's creating transaction and age (undefined while not committed), so a
|
|
395
397
|
* charge or a status fetches each of them once (review, 28 Oct).
|
|
396
398
|
*/
|
|
397
|
-
async function subscriptionState(client, deployment, sub
|
|
399
|
+
async function subscriptionState(client, deployment, sub,
|
|
400
|
+
/** The tip, when the caller already read it (one tip per call). */
|
|
401
|
+
knownTip) {
|
|
398
402
|
const lock = subscriptionLock(deployment, sub);
|
|
399
403
|
const token = tokenOf(sub);
|
|
400
404
|
const cells = await subscriptionCells(client, lock, token, MAX_SUBSCRIPTION_CELLS);
|
|
@@ -407,7 +411,7 @@ async function subscriptionState(client, deployment, sub) {
|
|
|
407
411
|
byHash.set(h, client.getTransactionNoCache(h)); // the node's answer, not the cache's
|
|
408
412
|
return byHash.get(h);
|
|
409
413
|
};
|
|
410
|
-
const [tip, created] = await Promise.all([client.getTip(), Promise.all(cells.map((c) => creating(c.outPoint.txHash)))]);
|
|
414
|
+
const [tip, created] = await Promise.all([knownTip ?? client.getTip(), Promise.all(cells.map((c) => creating(c.outPoint.txHash)))]);
|
|
411
415
|
const ages = created.map((res) => {
|
|
412
416
|
const born = res?.blockNumber;
|
|
413
417
|
return born === undefined || born === null ? undefined : tip - ccc.numFrom(born);
|
|
@@ -712,8 +716,16 @@ function chargesLeftOf(sub, lock, balance, tokenBalance) {
|
|
|
712
716
|
* type hash, `price` in token units as `max_per_tx_token`); `minBalance` is in token
|
|
713
717
|
* units, and the payment counted is token units into `payTo`'s cells of the token.
|
|
714
718
|
* Without `token`, a 160-byte cell is refused, and the reverse.
|
|
719
|
+
*
|
|
720
|
+
* With `minConfirmations` (default 0: committed is enough), the creating
|
|
721
|
+
* transaction must be that many blocks deep, its own block counting as 1: a cell
|
|
722
|
+
* valid in every other way gives `code: "unconfirmed"` until then, and
|
|
723
|
+
* `code: "reorged"` if that block left the main chain (src/confirmations.ts).
|
|
724
|
+
* Throws a `RangeError` for a `minConfirmations` that is not an integer from 0
|
|
725
|
+
* to `MAX_MIN_CONFIRMATIONS`.
|
|
715
726
|
*/
|
|
716
727
|
export async function verifySubscription(client, deployment, outPoint, expected) {
|
|
728
|
+
const minConfirmations = minConfirmationsFrom(expected.minConfirmations);
|
|
717
729
|
const cell = await client.getCellLive(outPoint, true, false);
|
|
718
730
|
if (!cell)
|
|
719
731
|
return { ok: false, reason: "no live cell at that out point" };
|
|
@@ -795,6 +807,12 @@ export async function verifySubscription(client, deployment, outPoint, expected)
|
|
|
795
807
|
if (expected.account !== undefined && !commitsToAccount(created, sub, expected.account)) {
|
|
796
808
|
return { ok: false, reason: "the opening does not commit to this account" };
|
|
797
809
|
}
|
|
810
|
+
// Depth last: "unconfirmed" then means the cell is good, only too recent.
|
|
811
|
+
if (minConfirmations > 0n) {
|
|
812
|
+
const fault = await confirmationFault(client, await client.getTip(), { blockNumber: created.block, blockHash: created.blockHash }, minConfirmations, "the transaction that created the cell");
|
|
813
|
+
if (fault)
|
|
814
|
+
return fault;
|
|
815
|
+
}
|
|
798
816
|
return {
|
|
799
817
|
ok: true,
|
|
800
818
|
subscription: sub,
|
|
@@ -864,7 +882,7 @@ fetched) {
|
|
|
864
882
|
const paid = out - amount(toPayTo(inputs.map((c) => ({ cellOutput: c.cellOutput, data: c.outputData }))));
|
|
865
883
|
if (paid < owed)
|
|
866
884
|
return notPaid;
|
|
867
|
-
return { tx, block: ccc.numFrom(created.blockNumber), inputs };
|
|
885
|
+
return { tx, block: ccc.numFrom(created.blockNumber), blockHash: created.blockHash, inputs };
|
|
868
886
|
}
|
|
869
887
|
/** Whether `p` carries `subscriptionMemo(account)` in the witness of an input under the subscriber's lock. */
|
|
870
888
|
function commitsToAccount(p, sub, account) {
|
|
@@ -885,8 +903,16 @@ function commitsToAccount(p, sub, account) {
|
|
|
885
903
|
* `MAX_SUBSCRIPTION_CELLS` cells (or recent transactions) sit under the lock:
|
|
886
904
|
* not a verdict on the subscription, so not an `ok: false`. A server should
|
|
887
905
|
* answer it as a conflict the subscriber can clear by cancelling and opening again.
|
|
906
|
+
*
|
|
907
|
+
* With `minConfirmations` (default 0: committed is enough), only payments that
|
|
908
|
+
* many blocks deep count, their own block counting as 1, and still on the main
|
|
909
|
+
* chain. An opening short of it gives `code: "unconfirmed"` (or `"reorged"`); a
|
|
910
|
+
* charge short of it does not extend `paidThroughBlock` yet, and shows in
|
|
911
|
+
* `unconfirmedThroughBlock`. The tip is read once. Throws a `RangeError` for a
|
|
912
|
+
* `minConfirmations` that is not an integer from 0 to `MAX_MIN_CONFIRMATIONS`.
|
|
888
913
|
*/
|
|
889
|
-
export async function subscriptionStatus(client, deployment, sub, proof) {
|
|
914
|
+
export async function subscriptionStatus(client, deployment, sub, proof, options = {}) {
|
|
915
|
+
const minConfirmations = minConfirmationsFrom(options.minConfirmations);
|
|
890
916
|
try {
|
|
891
917
|
assertTerms(sub);
|
|
892
918
|
const token = tokenOf(sub);
|
|
@@ -912,7 +938,15 @@ export async function subscriptionStatus(client, deployment, sub, proof) {
|
|
|
912
938
|
return { ok: false, reason: "the opening created no cell under this subscription's lock" };
|
|
913
939
|
if (!commitsToAccount(opening, sub, proof.account))
|
|
914
940
|
return { ok: false, reason: "the opening does not commit to this account" };
|
|
915
|
-
|
|
941
|
+
// One tip for the whole call: the opening's depth, each charge's, and `active`.
|
|
942
|
+
const knownTip = minConfirmations > 0n ? await client.getTip() : undefined;
|
|
943
|
+
const depthFault = (p, what) => knownTip === undefined
|
|
944
|
+
? Promise.resolve(undefined)
|
|
945
|
+
: confirmationFault(client, knownTip, { blockNumber: p.block, blockHash: p.blockHash }, minConfirmations, what);
|
|
946
|
+
const openingFault = await depthFault(opening, "the opening");
|
|
947
|
+
if (openingFault)
|
|
948
|
+
return openingFault;
|
|
949
|
+
const { cells, balance, tokenBalance, nextChargeIn, tip, created, spent, ownerAfter } = await subscriptionState(client, deployment, sub, knownTip);
|
|
916
950
|
let paidThroughBlock;
|
|
917
951
|
const renews = (p, txHash) =>
|
|
918
952
|
// Only the proven opening or a charge renews; another payment to payTo that
|
|
@@ -924,14 +958,27 @@ export async function subscriptionStatus(client, deployment, sub, proof) {
|
|
|
924
958
|
if (spent && !ownerAfter && !paying.some((c) => c.txHash === spent.txHash))
|
|
925
959
|
paying.push({ txHash: spent.txHash, fetched: spent.fetched });
|
|
926
960
|
const payments = await Promise.all(paying.map((c) => payment(c.txHash, c.fetched)));
|
|
961
|
+
let unconfirmedThroughBlock;
|
|
927
962
|
for (const [i, { txHash }] of paying.entries()) {
|
|
928
963
|
const p = payments[i];
|
|
929
964
|
if (!renews(p, txHash))
|
|
930
965
|
continue;
|
|
931
966
|
const through = p.block + sub.periodBlocks;
|
|
967
|
+
// The opening's depth was checked above; a charge's is checked here.
|
|
968
|
+
const fault = txHash === openHash ? undefined : await depthFault(p, "a charge");
|
|
969
|
+
if (fault) {
|
|
970
|
+
// Too recent: it counts later. Reorged: it never does.
|
|
971
|
+
if (fault.code === "unconfirmed" && (unconfirmedThroughBlock === undefined || through > unconfirmedThroughBlock)) {
|
|
972
|
+
unconfirmedThroughBlock = through;
|
|
973
|
+
}
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
932
976
|
if (paidThroughBlock === undefined || through > paidThroughBlock)
|
|
933
977
|
paidThroughBlock = through;
|
|
934
978
|
}
|
|
979
|
+
if (unconfirmedThroughBlock !== undefined && paidThroughBlock !== undefined && unconfirmedThroughBlock <= paidThroughBlock) {
|
|
980
|
+
unconfirmedThroughBlock = undefined;
|
|
981
|
+
}
|
|
935
982
|
return {
|
|
936
983
|
ok: true,
|
|
937
984
|
status: {
|
|
@@ -942,6 +989,7 @@ export async function subscriptionStatus(client, deployment, sub, proof) {
|
|
|
942
989
|
...(sub.token && { tokenBalance }),
|
|
943
990
|
chargesLeft: chargesLeftOf(sub, lock, balance, tokenBalance),
|
|
944
991
|
nextChargeIn,
|
|
992
|
+
...(unconfirmedThroughBlock !== undefined && { unconfirmedThroughBlock }),
|
|
945
993
|
},
|
|
946
994
|
};
|
|
947
995
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ckb-session-kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Browser-held self-custody sessions for CKB applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ckb",
|
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
"smoke:contention": "tsx scripts/testnet-contention.ts",
|
|
58
58
|
"smoke:clear-planted": "tsx scripts/testnet-clear-planted.ts",
|
|
59
59
|
"smoke:owner-locks": "tsx scripts/testnet-owner-locks.ts",
|
|
60
|
+
"smoke:fund-pledge": "tsx scripts/testnet-fund-pledge.ts",
|
|
61
|
+
"smoke:pool-lag": "tsx scripts/testnet-pool-lag.ts",
|
|
60
62
|
"matrix:wallets": "tsx scripts/wallet-matrix.ts",
|
|
61
63
|
"prepack": "npm run build",
|
|
62
64
|
"pack:check": "node scripts/pack-check.mjs"
|