ckb-session-kit 0.0.0-stage → 1.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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +558 -3
- package/dist/index.d.ts +15 -0
- package/dist/index.js +8 -0
- package/dist/lock.d.ts +70 -0
- package/dist/lock.js +106 -0
- package/dist/onchain.d.ts +212 -0
- package/dist/onchain.js +561 -0
- package/dist/paywall.d.ts +31 -0
- package/dist/paywall.js +116 -0
- package/dist/place.d.ts +104 -0
- package/dist/place.js +151 -0
- package/dist/session.d.ts +63 -0
- package/dist/session.js +54 -0
- package/dist/store.d.ts +26 -0
- package/dist/store.js +181 -0
- package/dist/subscription.d.ts +221 -0
- package/dist/subscription.js +622 -0
- package/dist/transfer.d.ts +19 -0
- package/dist/transfer.js +33 -0
- package/package.json +70 -6
package/dist/store.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
export class SessionStoreConflict extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super("another session is already stored here (opened in another tab?); reload the page");
|
|
4
|
+
this.name = "SessionStoreConflict";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
const UNREADABLE = "the stored session is unreadable; it may still hold a funded key, so it is never overwritten";
|
|
8
|
+
/** The stored value's public key, without parsing the rest. */
|
|
9
|
+
function storedKey(raw) {
|
|
10
|
+
if (raw === undefined)
|
|
11
|
+
return undefined;
|
|
12
|
+
let key;
|
|
13
|
+
try {
|
|
14
|
+
key = JSON.parse(raw).publicKey;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error(UNREADABLE);
|
|
18
|
+
}
|
|
19
|
+
if (typeof key !== "string")
|
|
20
|
+
throw new Error(UNREADABLE);
|
|
21
|
+
return key;
|
|
22
|
+
}
|
|
23
|
+
export function serializeSession(s) {
|
|
24
|
+
const stored = {
|
|
25
|
+
v: 1,
|
|
26
|
+
privateKey: s.privateKey,
|
|
27
|
+
publicKey: s.publicKey,
|
|
28
|
+
expiresAt: s.policy.expiresAt.toISOString(),
|
|
29
|
+
maxPerTx: s.policy.scope.maxPerTx.toString(),
|
|
30
|
+
...(s.policy.scope.recipients && { recipients: s.policy.scope.recipients }),
|
|
31
|
+
...(s.onchain && {
|
|
32
|
+
onchain: {
|
|
33
|
+
ownerLock: s.onchain.ownerLock,
|
|
34
|
+
minInterval: s.onchain.minInterval.toString(),
|
|
35
|
+
...(s.onchain.recipientLockHash && { recipientLockHash: s.onchain.recipientLockHash }),
|
|
36
|
+
...(s.onchain.token && {
|
|
37
|
+
token: { type: s.onchain.token.type, maxPerTx: s.onchain.token.maxPerTx.toString() },
|
|
38
|
+
}),
|
|
39
|
+
...(s.onchain.openTx && { openTx: s.onchain.openTx }),
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
return JSON.stringify(stored);
|
|
44
|
+
}
|
|
45
|
+
export function deserializeSession(json) {
|
|
46
|
+
let d;
|
|
47
|
+
try {
|
|
48
|
+
d = JSON.parse(json);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error(UNREADABLE);
|
|
52
|
+
}
|
|
53
|
+
if (d.v !== 1)
|
|
54
|
+
throw new Error(`unsupported stored session version: ${String(d.v)}`);
|
|
55
|
+
return {
|
|
56
|
+
privateKey: d.privateKey,
|
|
57
|
+
publicKey: d.publicKey,
|
|
58
|
+
policy: {
|
|
59
|
+
expiresAt: new Date(d.expiresAt),
|
|
60
|
+
scope: {
|
|
61
|
+
maxPerTx: BigInt(d.maxPerTx),
|
|
62
|
+
...(d.recipients && { recipients: d.recipients }),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
...(d.onchain && {
|
|
66
|
+
onchain: {
|
|
67
|
+
ownerLock: {
|
|
68
|
+
codeHash: d.onchain.ownerLock.codeHash,
|
|
69
|
+
hashType: d.onchain.ownerLock.hashType,
|
|
70
|
+
args: d.onchain.ownerLock.args,
|
|
71
|
+
},
|
|
72
|
+
minInterval: BigInt(d.onchain.minInterval),
|
|
73
|
+
...(d.onchain.recipientLockHash && { recipientLockHash: d.onchain.recipientLockHash }),
|
|
74
|
+
...(d.onchain.token && {
|
|
75
|
+
token: {
|
|
76
|
+
type: {
|
|
77
|
+
codeHash: d.onchain.token.type.codeHash,
|
|
78
|
+
hashType: d.onchain.token.type.hashType,
|
|
79
|
+
args: d.onchain.token.type.args,
|
|
80
|
+
},
|
|
81
|
+
maxPerTx: BigInt(d.onchain.token.maxPerTx),
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
...(d.onchain.openTx && { openTx: d.onchain.openTx }),
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export function memoryStore() {
|
|
90
|
+
let value;
|
|
91
|
+
return {
|
|
92
|
+
load: async () => (value === undefined ? undefined : deserializeSession(value)),
|
|
93
|
+
save: async (s) => {
|
|
94
|
+
const held = storedKey(value);
|
|
95
|
+
if (held !== undefined && held !== s.publicKey)
|
|
96
|
+
throw new SessionStoreConflict();
|
|
97
|
+
value = serializeSession(s);
|
|
98
|
+
},
|
|
99
|
+
clear: async (s) => {
|
|
100
|
+
if (storedKey(value) === s.publicKey)
|
|
101
|
+
value = undefined;
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const DB = "ckb-session-kit";
|
|
106
|
+
const STORE = "sessions";
|
|
107
|
+
const KEY = "current";
|
|
108
|
+
function request(r) {
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
r.onsuccess = () => resolve(r.result);
|
|
111
|
+
r.onerror = () => reject(r.error);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async function open(idb) {
|
|
115
|
+
const r = idb.open(DB, 1);
|
|
116
|
+
r.onupgradeneeded = () => r.result.createObjectStore(STORE);
|
|
117
|
+
return request(r);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* One session per `key`. Pages on the same origin that each run their own
|
|
121
|
+
* session (a wallet page and a reader page, say) pass different keys.
|
|
122
|
+
*/
|
|
123
|
+
export function indexedDbStore(idb = globalThis.indexedDB, key = KEY) {
|
|
124
|
+
// Read and write in one readwrite transaction, so two tabs cannot interleave.
|
|
125
|
+
// It resolves only once the transaction has committed, durably: a save is what
|
|
126
|
+
// makes it safe to send funds to the key.
|
|
127
|
+
const update = async (decide, value) => {
|
|
128
|
+
const db = await open(idb);
|
|
129
|
+
try {
|
|
130
|
+
const tx = db.transaction(STORE, "readwrite", { durability: "strict" });
|
|
131
|
+
const done = new Promise((resolve, reject) => {
|
|
132
|
+
tx.oncomplete = () => resolve();
|
|
133
|
+
tx.onabort = () => reject(tx.error ?? new Error("session store transaction aborted"));
|
|
134
|
+
tx.onerror = () => reject(tx.error ?? new Error("session store transaction failed"));
|
|
135
|
+
});
|
|
136
|
+
done.catch(() => { }); // awaited below; a decide() error aborts it first
|
|
137
|
+
const store = tx.objectStore(STORE);
|
|
138
|
+
const raw = await request(store.get(key));
|
|
139
|
+
let action;
|
|
140
|
+
try {
|
|
141
|
+
action = decide(raw);
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
tx.abort();
|
|
145
|
+
throw e;
|
|
146
|
+
}
|
|
147
|
+
if (action === "put")
|
|
148
|
+
await request(store.put(value, key));
|
|
149
|
+
if (action === "delete")
|
|
150
|
+
await request(store.delete(key));
|
|
151
|
+
await done;
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
db.close();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
return {
|
|
158
|
+
load: async () => {
|
|
159
|
+
const db = await open(idb);
|
|
160
|
+
try {
|
|
161
|
+
const raw = await request(db.transaction(STORE, "readonly").objectStore(STORE).get(key));
|
|
162
|
+
return raw === undefined ? undefined : deserializeSession(raw);
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
db.close();
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
save: async (session) => {
|
|
169
|
+
const value = serializeSession(session);
|
|
170
|
+
await update((raw) => {
|
|
171
|
+
const held = storedKey(raw);
|
|
172
|
+
if (held !== undefined && held !== session.publicKey)
|
|
173
|
+
throw new SessionStoreConflict();
|
|
174
|
+
return "put";
|
|
175
|
+
}, value);
|
|
176
|
+
},
|
|
177
|
+
clear: async (session) => {
|
|
178
|
+
await update((raw) => (storedKey(raw) === session.publicKey ? "delete" : "keep"));
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { ccc } from "@ckb-ccc/core";
|
|
2
|
+
import { type SessionLockDeployment } from "./lock.js";
|
|
3
|
+
/**
|
|
4
|
+
* Subscriptions (docs/design/subscriptions.md): the same session lock, with the
|
|
5
|
+
* roles moved. The subscriber is the owner; the creator's charger key is the
|
|
6
|
+
* session key; `max_per_tx` is the price, `min_interval` the period, and the
|
|
7
|
+
* recipient the creator's payout address. So the network lets the charger take
|
|
8
|
+
* at most the price, at most once per period, only to the payout address, and
|
|
9
|
+
* the subscriber can sweep everything back alone at any time.
|
|
10
|
+
*
|
|
11
|
+
* - `openSubscription` — the subscriber funds one cell. One signature.
|
|
12
|
+
* - `chargeSubscription` — the creator's charger key takes the price, paying the
|
|
13
|
+
* fee from its own cell. No subscriber involved.
|
|
14
|
+
* - `cancelSubscription` — the subscriber sweeps the cell back. One signature.
|
|
15
|
+
* - `verifySubscription` — the creator checks a subscription it was told about.
|
|
16
|
+
*
|
|
17
|
+
* A `Subscription` holds no private key: both sides can store it as it is.
|
|
18
|
+
*
|
|
19
|
+
* A token subscription (`token` set) is paid in an xUDT token instead: the
|
|
20
|
+
* 160-byte token layout with `max_per_tx` 0, so no CKB ever leaves the cell, and
|
|
21
|
+
* the price as `max_per_tx_token`. Charges top up the payout address's
|
|
22
|
+
* anyone-can-pay cell of that token (docs/design/subscriptions.md, "Token subscriptions").
|
|
23
|
+
*/
|
|
24
|
+
export interface Subscription {
|
|
25
|
+
subscriberLockHash: ccc.Hex;
|
|
26
|
+
chargerLockHash: ccc.Hex;
|
|
27
|
+
payToLockHash: ccc.Hex;
|
|
28
|
+
/** Per charge, in the subscription's asset: shannons, or token units in a token subscription. */
|
|
29
|
+
price: bigint;
|
|
30
|
+
/** Blocks between two charges of the same cell. */
|
|
31
|
+
periodBlocks: bigint;
|
|
32
|
+
/** A token subscription: the plain xUDT type script it is paid in. */
|
|
33
|
+
token?: {
|
|
34
|
+
type: {
|
|
35
|
+
codeHash: ccc.Hex;
|
|
36
|
+
hashType: ccc.HashType;
|
|
37
|
+
args: ccc.Hex;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
/** The funding transaction, when this side opened it. */
|
|
41
|
+
openTx?: ccc.Hex;
|
|
42
|
+
}
|
|
43
|
+
export declare function subscriptionLock(deployment: SessionLockDeployment, sub: Subscription): ccc.Script;
|
|
44
|
+
/**
|
|
45
|
+
* The memo an opening commits to, binding the subscription to one account at the
|
|
46
|
+
* creator: a hash, so the account id never appears on-chain and can double as a
|
|
47
|
+
* secret the subscriber's browser presents to the creator.
|
|
48
|
+
*/
|
|
49
|
+
export declare function subscriptionMemo(account: string): ccc.Hex;
|
|
50
|
+
/**
|
|
51
|
+
* The subscriber opens a subscription, in one transaction and one signature: the
|
|
52
|
+
* first period's price goes to `payTo` at once (a new cell cannot be charged for a
|
|
53
|
+
* whole period, so otherwise the first period would be free; review H1), and one
|
|
54
|
+
* cell holding `budget` shannons goes under the session lock. No key cell: the
|
|
55
|
+
* creator already owns cells under its charger key. `budget` of the cell's own
|
|
56
|
+
* size (153 CKB) plus n prices pays n more periods; what is left comes back on
|
|
57
|
+
* cancel. With a price of 153 CKB or more, the last 153 can be charged too.
|
|
58
|
+
*
|
|
59
|
+
* `account` (the subscriber's id at the creator) is committed in the witness of
|
|
60
|
+
* the subscriber's own input, which the subscriber's signature covers; the
|
|
61
|
+
* creator checks it with `verifySubscription`, so nobody else can present this
|
|
62
|
+
* subscription as theirs (review H3).
|
|
63
|
+
*
|
|
64
|
+
* `payTo` should be an anyone-can-pay address with a live cell, so a charge of
|
|
65
|
+
* any size tops it up; otherwise each charge creates a new cell and the price must
|
|
66
|
+
* be at least that cell's size (61 CKB for a plain address). Keep a plain cell in
|
|
67
|
+
* the wallet: cancelling needs one to prove ownership.
|
|
68
|
+
*
|
|
69
|
+
* With `token`, the subscription is paid in that plain xUDT and `price` is in
|
|
70
|
+
* token units: the cell holds `token.amount` units plus its own size in CKB
|
|
71
|
+
* (282 CKB; `budget` may add more, which stays until cancel), and the first price
|
|
72
|
+
* tops up `payTo`'s cell of the token. `payTo` must then be anyone-can-pay and
|
|
73
|
+
* already hold a cell of the token: no CKB may leave a token subscription, so a
|
|
74
|
+
* charge cannot pay for a new token cell.
|
|
75
|
+
*/
|
|
76
|
+
export declare function openSubscription(subscriber: ccc.Signer, deployment: SessionLockDeployment, opts: {
|
|
77
|
+
charger: string;
|
|
78
|
+
payTo: string;
|
|
79
|
+
price: bigint;
|
|
80
|
+
periodBlocks: bigint;
|
|
81
|
+
/** Shannons in the subscription cell. Required without `token`; with one, defaults to the cell's own size. */
|
|
82
|
+
budget?: bigint;
|
|
83
|
+
/** A token subscription: the plain xUDT it is paid in, and the units put in the cell (beyond the first price). */
|
|
84
|
+
token?: {
|
|
85
|
+
type: ccc.ScriptLike;
|
|
86
|
+
amount: bigint;
|
|
87
|
+
};
|
|
88
|
+
account?: string;
|
|
89
|
+
beforeSend?: (sub: Subscription) => Promise<void>;
|
|
90
|
+
}): Promise<{
|
|
91
|
+
txHash: ccc.Hex;
|
|
92
|
+
subscription: Subscription;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* The live cells of a subscription, their balance (shannons) and token balance
|
|
96
|
+
* (units; 0 without a token), and how many blocks until one can be charged.
|
|
97
|
+
*/
|
|
98
|
+
export declare function findSubscriptionCells(client: ccc.Client, deployment: SessionLockDeployment, sub: Subscription): Promise<{
|
|
99
|
+
lock: ccc.Script;
|
|
100
|
+
cells: ccc.Cell[];
|
|
101
|
+
balance: bigint;
|
|
102
|
+
tokenBalance: bigint;
|
|
103
|
+
nextChargeIn: bigint | undefined;
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* The creator's charger key takes one price from the subscription and pays it to
|
|
107
|
+
* `payTo` (topping up its anyone-can-pay cell with `topUp`). The fee comes from
|
|
108
|
+
* one of the charger's own plain cells, which goes back to it smaller. Throws a
|
|
109
|
+
* `SessionBalanceError` while no cell is a full period old, or once what is left
|
|
110
|
+
* cannot cover a price and still leave a cell behind.
|
|
111
|
+
*
|
|
112
|
+
* In a token subscription the price is in token units and always tops up
|
|
113
|
+
* `payTo`'s cell of the token (`topUp` is implied). The change keeps every
|
|
114
|
+
* shannon that came in (no CKB may leave) and the tokens less one price, in
|
|
115
|
+
* exactly 16 data bytes.
|
|
116
|
+
*/
|
|
117
|
+
export declare function chargeSubscription(charger: ccc.Signer, deployment: SessionLockDeployment, sub: Subscription, opts: {
|
|
118
|
+
payTo: string;
|
|
119
|
+
topUp?: boolean;
|
|
120
|
+
beforeSend?: (txHash: ccc.Hex) => Promise<void>;
|
|
121
|
+
}): Promise<ccc.Hex>;
|
|
122
|
+
/**
|
|
123
|
+
* The subscriber takes everything left back, in owner mode: no creator involved
|
|
124
|
+
* and no wait. `nothingFound` means no cell is left (already cancelled, or run dry).
|
|
125
|
+
* In a token subscription the tokens come back as one token cell (`refundedTokens`).
|
|
126
|
+
* Subscriptions are also found and swept by `recoverSessions`, since their args
|
|
127
|
+
* begin with the subscriber's lock hash like any session's.
|
|
128
|
+
*/
|
|
129
|
+
export declare function cancelSubscription(subscriber: ccc.Signer, deployment: SessionLockDeployment, sub: Subscription): Promise<{
|
|
130
|
+
txHash?: ccc.Hex;
|
|
131
|
+
refunded: bigint;
|
|
132
|
+
refundedTokens?: bigint;
|
|
133
|
+
nothingFound?: true;
|
|
134
|
+
}>;
|
|
135
|
+
export type SubscriptionCheck = {
|
|
136
|
+
ok: true;
|
|
137
|
+
subscription: Subscription;
|
|
138
|
+
/** Shannons in the cell. */
|
|
139
|
+
balance: bigint;
|
|
140
|
+
/** Token units in the cell (token subscriptions only). */
|
|
141
|
+
tokenBalance?: bigint;
|
|
142
|
+
/** Charges the cell can still pay. */
|
|
143
|
+
chargesLeft: bigint;
|
|
144
|
+
/** The block until which the subscriber has paid: the block that created this cell, plus one period. */
|
|
145
|
+
paidThroughBlock: bigint;
|
|
146
|
+
} | {
|
|
147
|
+
ok: false;
|
|
148
|
+
reason: string;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* The creator checks a subscription from the chain alone. Nothing in the request
|
|
152
|
+
* is trusted but the out point. The cell at `outPoint` must be live, under this
|
|
153
|
+
* deployment of the session lock (never an older one), untyped and without data,
|
|
154
|
+
* with 112-byte args holding exactly these terms, and at least `minBalance`.
|
|
155
|
+
*
|
|
156
|
+
* And the transaction that created the cell must have paid `payTo` at least one
|
|
157
|
+
* price: the opening pays the first period, every charge pays one. A cell the
|
|
158
|
+
* subscriber re-created in owner mode to reset its age paid nothing, and is
|
|
159
|
+
* refused (review H2). So access is granted up to `paidThroughBlock`; check the
|
|
160
|
+
* current cell again (`findSubscriptionCells`) once that block has passed.
|
|
161
|
+
*
|
|
162
|
+
* With `account`, the creating transaction must also carry `subscriptionMemo(account)`
|
|
163
|
+
* in the witness of an input under the subscriber's lock, which only the
|
|
164
|
+
* subscriber's signature covers: someone else's subscription cannot be claimed
|
|
165
|
+
* (review H3). The memo is in the opening only, so check `account` once, when a
|
|
166
|
+
* subscription is registered, then keep the subscription by its lock.
|
|
167
|
+
*
|
|
168
|
+
* With `token` (a token subscription), the cell must instead be typed with exactly
|
|
169
|
+
* that token and hold its amount, with 160-byte args (`max_per_tx` 0, the token's
|
|
170
|
+
* type hash, `price` in token units as `max_per_tx_token`); `minBalance` is in token
|
|
171
|
+
* units, and the payment counted is token units into `payTo`'s cells of the token.
|
|
172
|
+
* Without `token`, a 160-byte cell is refused, and the reverse.
|
|
173
|
+
*/
|
|
174
|
+
export declare function verifySubscription(client: ccc.Client, deployment: SessionLockDeployment, outPoint: ccc.OutPointLike, expected: {
|
|
175
|
+
charger: string;
|
|
176
|
+
payTo: string;
|
|
177
|
+
price: bigint;
|
|
178
|
+
periodBlocks: bigint;
|
|
179
|
+
minBalance?: bigint;
|
|
180
|
+
account?: string;
|
|
181
|
+
token?: ccc.ScriptLike;
|
|
182
|
+
}): Promise<SubscriptionCheck>;
|
|
183
|
+
export interface SubscriptionStatus {
|
|
184
|
+
/** The block until which the subscriber has paid, from the newest paying cell; undefined when no cell is left. */
|
|
185
|
+
paidThroughBlock: bigint | undefined;
|
|
186
|
+
tip: bigint;
|
|
187
|
+
/** Whether the tip is within the paid period. */
|
|
188
|
+
active: boolean;
|
|
189
|
+
/** Shannons in the subscription's cells. */
|
|
190
|
+
balance: bigint;
|
|
191
|
+
/** Token units in the subscription's cells (token subscriptions only). */
|
|
192
|
+
tokenBalance?: bigint;
|
|
193
|
+
chargesLeft: bigint;
|
|
194
|
+
/** Blocks until the creator may charge again (0 = now). */
|
|
195
|
+
nextChargeIn: bigint | undefined;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Where a subscription stands, for a creator's server that keeps no database:
|
|
199
|
+
* the subscriber sends the terms' subscriber lock hash, the opening's hash and
|
|
200
|
+
* its account id on every request. The opening must have paid the first price
|
|
201
|
+
* into a cell under exactly this subscription's lock and committed to `account`
|
|
202
|
+
* under the subscriber's signature; then the live cells give the period paid for.
|
|
203
|
+
* Each must have been created by a payment, so a cell re-created by the
|
|
204
|
+
* subscriber to reset its age counts for nothing. Cancelling ends access at once.
|
|
205
|
+
* A token subscription (`sub.token`) counts payments in its token, as `verifySubscription` does.
|
|
206
|
+
*/
|
|
207
|
+
export declare function subscriptionStatus(client: ccc.Client, deployment: SessionLockDeployment, sub: Subscription, proof: {
|
|
208
|
+
openTx: ccc.HexLike;
|
|
209
|
+
account: string;
|
|
210
|
+
}): Promise<{
|
|
211
|
+
ok: true;
|
|
212
|
+
status: SubscriptionStatus;
|
|
213
|
+
} | {
|
|
214
|
+
ok: false;
|
|
215
|
+
reason: string;
|
|
216
|
+
}>;
|
|
217
|
+
/**
|
|
218
|
+
* About how many blocks `seconds` is, from the average block time over the last
|
|
219
|
+
* `sample` blocks. Block time varies; round up when a period means "no more often than".
|
|
220
|
+
*/
|
|
221
|
+
export declare function blocksForSeconds(client: ccc.Client, seconds: number, sample?: bigint): Promise<bigint>;
|