@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/utils/chain.ts
CHANGED
|
@@ -1,43 +1,2 @@
|
|
|
1
|
-
import type { ccc } from "@ckb-ccc/core";
|
|
2
|
-
|
|
3
1
|
/** Public CKB networks supported by Stack consumers. */
|
|
4
2
|
export type SupportedChain = "mainnet" | "testnet";
|
|
5
|
-
|
|
6
|
-
/** Canonical public identity of one supported CKB network. */
|
|
7
|
-
export interface ChainIdentity {
|
|
8
|
-
/** Stack network selector represented by this identity. */
|
|
9
|
-
readonly chain: SupportedChain;
|
|
10
|
-
/** CKB specification network name. */
|
|
11
|
-
readonly networkName: string;
|
|
12
|
-
/** Canonical genesis block hash. */
|
|
13
|
-
readonly genesisHash: ccc.Hex;
|
|
14
|
-
/** Canonical genesis message used by the CKB specification. */
|
|
15
|
-
readonly genesisMessage: string;
|
|
16
|
-
/** Public specification location from which the genesis identity was derived. */
|
|
17
|
-
readonly genesisSource: string;
|
|
18
|
-
/** Canonical address prefix for this network. */
|
|
19
|
-
readonly addressPrefix: "ckb" | "ckt";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** The immutable canonical identity of each supported chain. */
|
|
23
|
-
export const chainIdentities = Object.freeze({
|
|
24
|
-
mainnet: Object.freeze({
|
|
25
|
-
chain: "mainnet",
|
|
26
|
-
networkName: "ckb",
|
|
27
|
-
genesisHash: "0x92b197aa1fba0f63633922c61c92375c9c074a93e85963554f5499fe1450d0e5",
|
|
28
|
-
genesisMessage:
|
|
29
|
-
"lina 0x18e020f6b1237a3d06b75121f25a7efa0550e4b3f44f974822f471902424c104",
|
|
30
|
-
genesisSource:
|
|
31
|
-
"https://raw.githubusercontent.com/nervosnetwork/ckb/develop/resource/specs/mainnet.toml",
|
|
32
|
-
addressPrefix: "ckb",
|
|
33
|
-
}),
|
|
34
|
-
testnet: Object.freeze({
|
|
35
|
-
chain: "testnet",
|
|
36
|
-
networkName: "ckb_testnet",
|
|
37
|
-
genesisHash: "0x10639e0895502b5688a6be8cf69460d76541bfa4821629d86d62ba0aae3f9606",
|
|
38
|
-
genesisMessage: "aggron-v4",
|
|
39
|
-
genesisSource:
|
|
40
|
-
"https://raw.githubusercontent.com/nervosnetwork/ckb/develop/resource/specs/testnet.toml",
|
|
41
|
-
addressPrefix: "ckt",
|
|
42
|
-
}),
|
|
43
|
-
} as const satisfies Record<SupportedChain, ChainIdentity>);
|
package/src/utils/codec.ts
CHANGED
|
@@ -29,6 +29,10 @@ export const CheckedInt32LE = ccc.Codec.from<ccc.NumLike, number>({
|
|
|
29
29
|
},
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
// CCC's unsigned codecs throw on overflow but encode a negative as its two's complement
|
|
33
|
+
// without a word, and `Ratio.toBytes()` is public and skips `validate`, so these checked
|
|
34
|
+
// codecs keep "never encode a negative" in one place; CCC has no signed Int32 codec at all.
|
|
35
|
+
|
|
32
36
|
/** Checked codec for 8-bit unsigned integers. */
|
|
33
37
|
export const CheckedUint8 = checkedUintLE(1);
|
|
34
38
|
|
package/src/utils/utils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ccc } from "@ckb-ccc/core";
|
|
2
|
+
import { cccA } from "@ckb-ccc/core/advanced";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The default page size used when querying cells from the chain.
|
|
@@ -9,52 +10,91 @@ import { ccc } from "@ckb-ccc/core";
|
|
|
9
10
|
export const defaultCellPageSize = 400;
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
* CCC's
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
* A CCC client cache sized for a whole pool: 4096 headers and 4096 transaction bodies,
|
|
14
|
+
* where CCC's defaults keep 128 and 256 (SDK README, Account state). An LRU fills only as
|
|
15
|
+
* far as the book and pool need.
|
|
16
|
+
*/
|
|
17
|
+
export function clientCache(): ccc.ClientCacheMemory {
|
|
18
|
+
return new ccc.ClientCacheMemory(512, 4096, 4096);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A committed cell and the block the node names for its creating transaction. */
|
|
22
|
+
export interface ScannedCell {
|
|
23
|
+
cell: ccc.Cell;
|
|
24
|
+
blockNumber: ccc.Num;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Reads every committed cell matching the key with the block the node names for it, page
|
|
29
|
+
* by page through the raw requestor (CCC's typed scan drops the block), bypassing CCC's
|
|
30
|
+
* cell cache. A full page whose cursor does not advance throws. Why: SDK README, Account
|
|
31
|
+
* state.
|
|
19
32
|
*/
|
|
20
33
|
export async function findCells(
|
|
21
34
|
client: ccc.Client,
|
|
22
35
|
keyLike: ccc.ClientIndexerSearchKeyLike,
|
|
23
|
-
): Promise<
|
|
24
|
-
const
|
|
25
|
-
const
|
|
36
|
+
): Promise<ScannedCell[]> {
|
|
37
|
+
const requestor = jsonRpcRequestor(client);
|
|
38
|
+
const key = cccA.JsonRpcTransformers.indexerSearchKeyFrom(keyLike);
|
|
39
|
+
const cells: ScannedCell[] = [];
|
|
26
40
|
let after: string | undefined;
|
|
27
41
|
for (;;) {
|
|
28
|
-
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, no-restricted-syntax -- the node's indexer answer, parsed field by field below as CCC's own transformer does.
|
|
43
|
+
const page = (await requestor.request("get_cells", [
|
|
29
44
|
key,
|
|
30
45
|
"asc",
|
|
31
|
-
defaultCellPageSize,
|
|
46
|
+
ccc.numToHex(defaultCellPageSize),
|
|
32
47
|
after,
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
])) as RawCellsPage;
|
|
49
|
+
for (const object of page.objects) {
|
|
50
|
+
cells.push({
|
|
51
|
+
cell: ccc.Cell.from({
|
|
52
|
+
outPoint: cccA.JsonRpcTransformers.outPointTo(object.out_point),
|
|
53
|
+
cellOutput: cccA.JsonRpcTransformers.cellOutputTo(object.output),
|
|
54
|
+
outputData: object.output_data ?? "0x",
|
|
55
|
+
}),
|
|
56
|
+
blockNumber: ccc.numFrom(object.block_number),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (page.objects.length < defaultCellPageSize) {
|
|
36
60
|
return cells;
|
|
37
61
|
}
|
|
38
|
-
if (page.
|
|
62
|
+
if (page.last_cursor === after) {
|
|
39
63
|
throw new Error("Cell scan cursor did not advance");
|
|
40
64
|
}
|
|
41
|
-
after = page.
|
|
65
|
+
after = page.last_cursor;
|
|
42
66
|
}
|
|
43
67
|
}
|
|
44
68
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
/** The headers of a batch of block numbers, each read once; every block must exist. */
|
|
70
|
+
export async function headersByNumber(
|
|
71
|
+
client: ccc.Client,
|
|
72
|
+
blockNumbers: Iterable<ccc.Num>,
|
|
73
|
+
): Promise<Map<ccc.Num, ccc.ClientBlockHeader>> {
|
|
74
|
+
const distinct = [...new Set(blockNumbers)];
|
|
75
|
+
const headers = await Promise.all(
|
|
76
|
+
distinct.map(async (blockNumber) => client.getHeaderByNumber(blockNumber)),
|
|
77
|
+
);
|
|
78
|
+
return new Map(
|
|
79
|
+
distinct.map((blockNumber, index) => {
|
|
80
|
+
const header = headers[index];
|
|
81
|
+
if (header === undefined) {
|
|
82
|
+
throw new Error(`Header not found for block number ${String(blockNumber)}`);
|
|
83
|
+
}
|
|
84
|
+
return [blockNumber, header];
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
53
88
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
89
|
+
/** The fields of the indexer's `get_cells` answer the scan reads. */
|
|
90
|
+
interface RawCellsPage {
|
|
91
|
+
last_cursor: string;
|
|
92
|
+
objects: Array<{
|
|
93
|
+
out_point: cccA.JsonRpcOutPoint;
|
|
94
|
+
output: cccA.JsonRpcCellOutput;
|
|
95
|
+
output_data?: ccc.Hex;
|
|
96
|
+
block_number: ccc.Hex;
|
|
97
|
+
}>;
|
|
58
98
|
}
|
|
59
99
|
|
|
60
100
|
/**
|
|
@@ -110,86 +150,17 @@ export function isPlainCapacityCell(cell: ccc.Cell): boolean {
|
|
|
110
150
|
}
|
|
111
151
|
|
|
112
152
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* [0, n), if `f(i)` is true, then `f(i+1)` is also true. This means that there
|
|
116
|
-
* is a prefix of the input range where `f` is false, followed by a suffix where
|
|
117
|
-
* `f` is true. If no such index exists, the function returns `n`.
|
|
118
|
-
*
|
|
119
|
-
* The function `f` is only called for indices in the range [0, n).
|
|
120
|
-
*
|
|
121
|
-
* @param n - The non-negative integer upper bound of the search range (exclusive).
|
|
122
|
-
* @param f - An async function that takes an index `i` and returns a boolean value.
|
|
123
|
-
* @returns The smallest index `i` such that `f(i)` is true, or `n` if no such index exists.
|
|
124
|
-
*
|
|
125
|
-
* @remarks Adapted from Go's standard library search implementation:
|
|
126
|
-
* {@link https://go.dev/src/sort/search.go}
|
|
127
|
-
*/
|
|
128
|
-
export async function asyncBinarySearch(
|
|
129
|
-
n: number,
|
|
130
|
-
f: (i: number) => Promise<boolean>,
|
|
131
|
-
): Promise<number> {
|
|
132
|
-
// Define f(-1) == false and f(n) == true.
|
|
133
|
-
// Invariant: f(i-1) == false, f(j) == true.
|
|
134
|
-
let [i, j] = [0, n];
|
|
135
|
-
while (i < j) {
|
|
136
|
-
const h = Math.trunc((i + j) / 2);
|
|
137
|
-
// i ≤ h < j
|
|
138
|
-
if (!(await f(h))) {
|
|
139
|
-
i = h + 1; // preserves f(i-1) == false
|
|
140
|
-
} else {
|
|
141
|
-
j = h; // preserves f(j) == true
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
// i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
|
|
145
|
-
return i;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* The client's JSON-RPC requestor, when it has one. The connector hands the interface a
|
|
153
|
+
* The client's JSON-RPC requestor, which the SDK's raw reads need: scans with block
|
|
154
|
+
* numbers, status polls and transaction proofs. The connector hands the interface a
|
|
150
155
|
* composition proxy over the public client (`ClientWithFeeRate`, a `ccc.Client` but not a
|
|
151
156
|
* `ccc.ClientJsonRpc`), so `instanceof` misses it; the proxy forwards the property.
|
|
152
157
|
*/
|
|
153
|
-
export function jsonRpcRequestor(client: ccc.Client): ccc.RequestorJsonRpc
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
export function jsonRpcRequestor(client: ccc.Client): ccc.RequestorJsonRpc {
|
|
159
|
+
const requestor = "requestor" in client ? client.requestor : undefined;
|
|
160
|
+
if (!(requestor instanceof ccc.RequestorJsonRpc)) {
|
|
161
|
+
throw new TypeError("The SDK needs a JSON-RPC client");
|
|
156
162
|
}
|
|
157
|
-
|
|
158
|
-
return requestor instanceof ccc.RequestorJsonRpc ? requestor : undefined;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** A node's raw `get_transaction` status record (verbosity 1), the fields the stack reads. */
|
|
162
|
-
export function rawTransactionStatus(response: unknown): {
|
|
163
|
-
status: string | undefined;
|
|
164
|
-
reason: string | undefined;
|
|
165
|
-
blockNumber: ccc.Num | undefined;
|
|
166
|
-
} {
|
|
167
|
-
const record =
|
|
168
|
-
typeof response === "object" &&
|
|
169
|
-
response !== null &&
|
|
170
|
-
"tx_status" in response &&
|
|
171
|
-
typeof response.tx_status === "object" &&
|
|
172
|
-
response.tx_status !== null
|
|
173
|
-
? response.tx_status
|
|
174
|
-
: {};
|
|
175
|
-
return {
|
|
176
|
-
status:
|
|
177
|
-
"status" in record && typeof record.status === "string" ? record.status : undefined,
|
|
178
|
-
reason:
|
|
179
|
-
"reason" in record && typeof record.reason === "string" ? record.reason : undefined,
|
|
180
|
-
blockNumber:
|
|
181
|
-
"block_number" in record && typeof record.block_number === "string"
|
|
182
|
-
? ccc.numFrom(record.block_number)
|
|
183
|
-
: undefined,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function minBigInt(left: bigint, right: bigint): bigint {
|
|
188
|
-
return left < right ? left : right;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function maxBigInt(left: bigint, right: bigint): bigint {
|
|
192
|
-
return left > right ? left : right;
|
|
163
|
+
return requestor;
|
|
193
164
|
}
|
|
194
165
|
|
|
195
166
|
/** `numerator / denominator` rounded up; the denominator is positive. */
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { ccc } from "@ckb-ccc/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wraps `inner` into a client that checks the reads the SDK makes: every header it hands
|
|
5
|
+
* out against the header's own hash, a header read by number against the number asked
|
|
6
|
+
* for, a transaction read by hash against the hash of its body, and a cell against the
|
|
7
|
+
* body of its creating transaction. Other reads pass through unchecked; the cell
|
|
8
|
+
* beside `getCellWithHeader`'s header is one, since the SDK reads only its header.
|
|
9
|
+
*
|
|
10
|
+
* @remarks It wraps the client, not the SDK's readers, because CCC's own DAO valuation
|
|
11
|
+
* (`Cell.getNervosDaoInfo`, in the send) reads headers through the client it is given.
|
|
12
|
+
* Why each check exists: SDK README, Account state.
|
|
13
|
+
*/
|
|
14
|
+
export function verifiedClient(inner: ccc.Client): ccc.Client {
|
|
15
|
+
return new VerifiedClient(inner);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// @ts-expect-error TS2655: the abstract members are forwarded by the proxy at runtime, as in CCC's own connector.
|
|
19
|
+
class VerifiedClient extends ccc.Proxy.Base(ccc.Client) {
|
|
20
|
+
public static readonly [ccc.Proxy.localKeys] = ["bodies"];
|
|
21
|
+
|
|
22
|
+
/** Creating transactions `getCell` read, so concurrent reads of one share a request. */
|
|
23
|
+
private readonly bodies = new Map<
|
|
24
|
+
ccc.Hex,
|
|
25
|
+
Promise<ccc.ClientTransactionResponse | undefined>
|
|
26
|
+
>();
|
|
27
|
+
|
|
28
|
+
public override async getTipHeader(): Promise<ccc.ClientBlockHeader> {
|
|
29
|
+
return verified(await this[ccc.Proxy.inner].getTipHeader());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public override async getHeaderByNumber(
|
|
33
|
+
...args: Parameters<ccc.Client["getHeaderByNumber"]>
|
|
34
|
+
): Promise<ccc.ClientBlockHeader | undefined> {
|
|
35
|
+
const header = ccc.apply(
|
|
36
|
+
verified,
|
|
37
|
+
await this[ccc.Proxy.inner].getHeaderByNumber(...args),
|
|
38
|
+
);
|
|
39
|
+
const number = ccc.numFrom(args[0]);
|
|
40
|
+
if (header !== undefined && header.number !== number) {
|
|
41
|
+
throw new Error(`Header ${header.hash} answers for block ${number.toString()}`);
|
|
42
|
+
}
|
|
43
|
+
return header;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public override async getTransaction(
|
|
47
|
+
...args: Parameters<ccc.Client["getTransaction"]>
|
|
48
|
+
): ReturnType<ccc.Client["getTransaction"]> {
|
|
49
|
+
return verifiedBody(await this[ccc.Proxy.inner].getTransaction(...args), args[0]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The output of the verified creating transaction, cache first: CCC keys cached
|
|
54
|
+
* transactions by their body's hash, and its own `getCell` would build the cell from a
|
|
55
|
+
* read this wrapper never sees, then cache it.
|
|
56
|
+
*/
|
|
57
|
+
public override async getCell(
|
|
58
|
+
...args: Parameters<ccc.Client["getCell"]>
|
|
59
|
+
): ReturnType<ccc.Client["getCell"]> {
|
|
60
|
+
const outPoint = ccc.OutPoint.from(args[0]);
|
|
61
|
+
const { txHash } = outPoint;
|
|
62
|
+
let body = this.bodies.get(txHash);
|
|
63
|
+
if (body === undefined) {
|
|
64
|
+
body = this.readBody(txHash);
|
|
65
|
+
this.bodies.set(txHash, body);
|
|
66
|
+
}
|
|
67
|
+
const output = (await body)?.transaction.getOutput(outPoint.index);
|
|
68
|
+
return output === undefined
|
|
69
|
+
? undefined
|
|
70
|
+
: ccc.Cell.from({
|
|
71
|
+
outPoint,
|
|
72
|
+
cellOutput: output.cellOutput,
|
|
73
|
+
outputData: output.outputData,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private async readBody(
|
|
78
|
+
txHash: ccc.Hex,
|
|
79
|
+
): Promise<ccc.ClientTransactionResponse | undefined> {
|
|
80
|
+
// CCC's own cache keys bodies by their hash, but a consumer's cache need not.
|
|
81
|
+
return (
|
|
82
|
+
verifiedBody(await this.cache.getTransactionResponse(txHash), txHash) ??
|
|
83
|
+
(await this.getTransaction(txHash))
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public override async getCellWithHeader(
|
|
88
|
+
...args: Parameters<ccc.Client["getCellWithHeader"]>
|
|
89
|
+
): ReturnType<ccc.Client["getCellWithHeader"]> {
|
|
90
|
+
const result = await this[ccc.Proxy.inner].getCellWithHeader(...args);
|
|
91
|
+
ccc.apply(verified, result?.header);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The response when its body hashes to the hash asked for, else throws. */
|
|
97
|
+
function verifiedBody(
|
|
98
|
+
response: ccc.ClientTransactionResponse | undefined,
|
|
99
|
+
txHash: ccc.HexLike,
|
|
100
|
+
): ccc.ClientTransactionResponse | undefined {
|
|
101
|
+
const asked = ccc.hexFrom(txHash);
|
|
102
|
+
if (response !== undefined && response.transaction.hash() !== asked) {
|
|
103
|
+
throw new Error(`Transaction ${asked} was answered with a body of another hash`);
|
|
104
|
+
}
|
|
105
|
+
return response;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The header when its fields hash to the hash it carries, else throws. */
|
|
109
|
+
function verified(header: ccc.ClientBlockHeader): ccc.ClientBlockHeader {
|
|
110
|
+
const computed = headerHash(header);
|
|
111
|
+
if (computed !== header.hash) {
|
|
112
|
+
throw new Error(`Header ${header.hash} carries the fields of ${computed}`);
|
|
113
|
+
}
|
|
114
|
+
return header;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The hash of a header as the chain computes it: blake2b over the header struct, each
|
|
119
|
+
* number little-endian, in declaration order, the nonce last.
|
|
120
|
+
*/
|
|
121
|
+
export function headerHash(header: ccc.ClientBlockHeader): ccc.Hex {
|
|
122
|
+
const { dao } = header;
|
|
123
|
+
return ccc.hashCkb(
|
|
124
|
+
ccc.numLeToBytes(header.version, 4),
|
|
125
|
+
ccc.numLeToBytes(header.compactTarget, 4),
|
|
126
|
+
ccc.numLeToBytes(header.timestamp, 8),
|
|
127
|
+
ccc.numLeToBytes(header.number, 8),
|
|
128
|
+
ccc.numLeToBytes(header.epoch.toNum(), 8),
|
|
129
|
+
header.parentHash,
|
|
130
|
+
header.transactionsRoot,
|
|
131
|
+
header.proposalsHash,
|
|
132
|
+
header.extraHash,
|
|
133
|
+
...[dao.c, dao.ar, dao.s, dao.u].map((field) => ccc.numLeToBytes(field, 8)),
|
|
134
|
+
ccc.numLeToBytes(header.nonce, 16),
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ccc } from "@ckb-ccc/core";
|
|
2
|
-
/**
|
|
3
|
-
* The header of the block that committed `txHash`, without the transaction body, which no
|
|
4
|
-
* caller reads. A client with a JSON-RPC requestor, the connector's composed client
|
|
5
|
-
* included, asks the node for the status only (verbosity 1) and then the header by number,
|
|
6
|
-
* which CCC caches once confirmed; a cold read of the pool then moves a few bytes per
|
|
7
|
-
* deposit instead of every deposit transaction. Any other client keeps the typed lookup.
|
|
8
|
-
*/
|
|
9
|
-
export declare function getTransactionHeader(client: ccc.Client, txHash: ccc.Hex): Promise<ccc.ClientBlockHeader | undefined>;
|
|
10
|
-
/**
|
|
11
|
-
* The committing headers of a batch of transactions, each hash read once. A batch is one
|
|
12
|
-
* coherent read: the cells came from one scan, so every header must exist.
|
|
13
|
-
*/
|
|
14
|
-
export declare function transactionHeaders(client: ccc.Client, txHashes: Iterable<ccc.Hex>): Promise<Map<ccc.Hex, ccc.ClientBlockHeader>>;
|
|
15
|
-
/** The headers of a batch of block numbers, each read once; every block must exist. */
|
|
16
|
-
export declare function headersByNumber(client: ccc.Client, blockNumbers: Iterable<ccc.Num>): Promise<Map<ccc.Num, ccc.ClientBlockHeader>>;
|
|
17
|
-
//# sourceMappingURL=transaction_header.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transaction_header.d.ts","sourceRoot":"","sources":["../../src/utils/transaction_header.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,GAAG,CAAC,MAAM,EAClB,MAAM,EAAE,GAAG,CAAC,GAAG,GACd,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAS5C;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,GAAG,CAAC,MAAM,EAClB,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAC1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAc9C;AAED,uFAAuF;AACvF,wBAAsB,eAAe,CACnC,MAAM,EAAE,GAAG,CAAC,MAAM,EAClB,YAAY,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAc9C"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { jsonRpcRequestor, rawTransactionStatus } from "./utils.js";
|
|
2
|
-
/**
|
|
3
|
-
* The header of the block that committed `txHash`, without the transaction body, which no
|
|
4
|
-
* caller reads. A client with a JSON-RPC requestor, the connector's composed client
|
|
5
|
-
* included, asks the node for the status only (verbosity 1) and then the header by number,
|
|
6
|
-
* which CCC caches once confirmed; a cold read of the pool then moves a few bytes per
|
|
7
|
-
* deposit instead of every deposit transaction. Any other client keeps the typed lookup.
|
|
8
|
-
*/
|
|
9
|
-
export async function getTransactionHeader(client, txHash) {
|
|
10
|
-
const requestor = jsonRpcRequestor(client);
|
|
11
|
-
if (requestor === undefined) {
|
|
12
|
-
return (await client.getTransactionWithHeader(txHash))?.header;
|
|
13
|
-
}
|
|
14
|
-
const { blockNumber } = rawTransactionStatus(await requestor.request("get_transaction", [txHash, "0x1"]));
|
|
15
|
-
return blockNumber === undefined ? undefined : client.getHeaderByNumber(blockNumber);
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* The committing headers of a batch of transactions, each hash read once. A batch is one
|
|
19
|
-
* coherent read: the cells came from one scan, so every header must exist.
|
|
20
|
-
*/
|
|
21
|
-
export async function transactionHeaders(client, txHashes) {
|
|
22
|
-
const distinct = [...new Set(txHashes)];
|
|
23
|
-
const headers = await Promise.all(distinct.map(async (txHash) => getTransactionHeader(client, txHash)));
|
|
24
|
-
return new Map(distinct.map((txHash, index) => {
|
|
25
|
-
const header = headers[index];
|
|
26
|
-
if (header === undefined) {
|
|
27
|
-
throw new Error(`Header not found for txHash ${txHash}`);
|
|
28
|
-
}
|
|
29
|
-
return [txHash, header];
|
|
30
|
-
}));
|
|
31
|
-
}
|
|
32
|
-
/** The headers of a batch of block numbers, each read once; every block must exist. */
|
|
33
|
-
export async function headersByNumber(client, blockNumbers) {
|
|
34
|
-
const distinct = [...new Set(blockNumbers)];
|
|
35
|
-
const headers = await Promise.all(distinct.map(async (blockNumber) => client.getHeaderByNumber(blockNumber)));
|
|
36
|
-
return new Map(distinct.map((blockNumber, index) => {
|
|
37
|
-
const header = headers[index];
|
|
38
|
-
if (header === undefined) {
|
|
39
|
-
throw new Error(`Header not found for block number ${String(blockNumber)}`);
|
|
40
|
-
}
|
|
41
|
-
return [blockNumber, header];
|
|
42
|
-
}));
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=transaction_header.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transaction_header.js","sourceRoot":"","sources":["../../src/utils/transaction_header.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAkB,EAClB,MAAe;IAEf,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,MAAM,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC;IACjE,CAAC;IACD,MAAM,EAAE,WAAW,EAAE,GAAG,oBAAoB,CAC1C,MAAM,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAC5D,CAAC;IACF,OAAO,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;AACvF,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAkB,EAClB,QAA2B;IAE3B,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CACrE,CAAC;IACF,OAAO,IAAI,GAAG,CACZ,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAkB,EAClB,YAA+B;IAE/B,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAC3E,CAAC;IACF,OAAO,IAAI,GAAG,CACZ,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,qCAAqC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC/B,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import type { ccc } from "@ckb-ccc/core";
|
|
2
|
-
import { jsonRpcRequestor, rawTransactionStatus } from "./utils.ts";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The header of the block that committed `txHash`, without the transaction body, which no
|
|
6
|
-
* caller reads. A client with a JSON-RPC requestor, the connector's composed client
|
|
7
|
-
* included, asks the node for the status only (verbosity 1) and then the header by number,
|
|
8
|
-
* which CCC caches once confirmed; a cold read of the pool then moves a few bytes per
|
|
9
|
-
* deposit instead of every deposit transaction. Any other client keeps the typed lookup.
|
|
10
|
-
*/
|
|
11
|
-
export async function getTransactionHeader(
|
|
12
|
-
client: ccc.Client,
|
|
13
|
-
txHash: ccc.Hex,
|
|
14
|
-
): Promise<ccc.ClientBlockHeader | undefined> {
|
|
15
|
-
const requestor = jsonRpcRequestor(client);
|
|
16
|
-
if (requestor === undefined) {
|
|
17
|
-
return (await client.getTransactionWithHeader(txHash))?.header;
|
|
18
|
-
}
|
|
19
|
-
const { blockNumber } = rawTransactionStatus(
|
|
20
|
-
await requestor.request("get_transaction", [txHash, "0x1"]),
|
|
21
|
-
);
|
|
22
|
-
return blockNumber === undefined ? undefined : client.getHeaderByNumber(blockNumber);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The committing headers of a batch of transactions, each hash read once. A batch is one
|
|
27
|
-
* coherent read: the cells came from one scan, so every header must exist.
|
|
28
|
-
*/
|
|
29
|
-
export async function transactionHeaders(
|
|
30
|
-
client: ccc.Client,
|
|
31
|
-
txHashes: Iterable<ccc.Hex>,
|
|
32
|
-
): Promise<Map<ccc.Hex, ccc.ClientBlockHeader>> {
|
|
33
|
-
const distinct = [...new Set(txHashes)];
|
|
34
|
-
const headers = await Promise.all(
|
|
35
|
-
distinct.map(async (txHash) => getTransactionHeader(client, txHash)),
|
|
36
|
-
);
|
|
37
|
-
return new Map(
|
|
38
|
-
distinct.map((txHash, index) => {
|
|
39
|
-
const header = headers[index];
|
|
40
|
-
if (header === undefined) {
|
|
41
|
-
throw new Error(`Header not found for txHash ${txHash}`);
|
|
42
|
-
}
|
|
43
|
-
return [txHash, header];
|
|
44
|
-
}),
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** The headers of a batch of block numbers, each read once; every block must exist. */
|
|
49
|
-
export async function headersByNumber(
|
|
50
|
-
client: ccc.Client,
|
|
51
|
-
blockNumbers: Iterable<ccc.Num>,
|
|
52
|
-
): Promise<Map<ccc.Num, ccc.ClientBlockHeader>> {
|
|
53
|
-
const distinct = [...new Set(blockNumbers)];
|
|
54
|
-
const headers = await Promise.all(
|
|
55
|
-
distinct.map(async (blockNumber) => client.getHeaderByNumber(blockNumber)),
|
|
56
|
-
);
|
|
57
|
-
return new Map(
|
|
58
|
-
distinct.map((blockNumber, index) => {
|
|
59
|
-
const header = headers[index];
|
|
60
|
-
if (header === undefined) {
|
|
61
|
-
throw new Error(`Header not found for block number ${String(blockNumber)}`);
|
|
62
|
-
}
|
|
63
|
-
return [blockNumber, header];
|
|
64
|
-
}),
|
|
65
|
-
);
|
|
66
|
-
}
|