@ckbfs/api 1.5.1 → 2.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 +31 -6
- package/RFC.v3.md +210 -0
- package/dist/index.d.ts +72 -7
- package/dist/index.js +437 -75
- package/dist/utils/checksum.d.ts +16 -0
- package/dist/utils/checksum.js +74 -8
- package/dist/utils/constants.d.ts +2 -1
- package/dist/utils/constants.js +12 -2
- package/dist/utils/file.d.ts +44 -0
- package/dist/utils/file.js +303 -30
- package/dist/utils/molecule.d.ts +13 -1
- package/dist/utils/molecule.js +32 -5
- package/dist/utils/transaction-backup.d.ts +117 -0
- package/dist/utils/transaction-backup.js +624 -0
- package/dist/utils/transaction.d.ts +7 -115
- package/dist/utils/transaction.js +45 -622
- package/dist/utils/transactions/index.d.ts +8 -0
- package/dist/utils/transactions/index.js +31 -0
- package/dist/utils/transactions/shared.d.ts +57 -0
- package/dist/utils/transactions/shared.js +17 -0
- package/dist/utils/transactions/v1v2.d.ts +80 -0
- package/dist/utils/transactions/v1v2.js +592 -0
- package/dist/utils/transactions/v3.d.ts +124 -0
- package/dist/utils/transactions/v3.js +369 -0
- package/dist/utils/witness.d.ts +45 -0
- package/dist/utils/witness.js +145 -3
- package/examples/append-v3.ts +310 -0
- package/examples/chunked-publish.ts +307 -0
- package/examples/publish-v3.ts +152 -0
- package/examples/publish.ts +4 -4
- package/examples/retrieve-v3.ts +222 -0
- package/package.json +6 -2
- package/small-example.txt +1 -0
- package/src/index.ts +568 -87
- package/src/utils/checksum.ts +90 -9
- package/src/utils/constants.ts +19 -2
- package/src/utils/file.ts +386 -35
- package/src/utils/molecule.ts +43 -6
- package/src/utils/transaction-backup.ts +849 -0
- package/src/utils/transaction.ts +39 -848
- package/src/utils/transactions/index.ts +16 -0
- package/src/utils/transactions/shared.ts +64 -0
- package/src/utils/transactions/v1v2.ts +791 -0
- package/src/utils/transactions/v3.ts +564 -0
- package/src/utils/witness.ts +193 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import { ccc, Transaction, Script, Signer } from "@ckb-ccc/core";
|
|
2
|
+
import { calculateChecksum, updateChecksum } from "../checksum";
|
|
3
|
+
import { CKBFSData, CKBFSDataType } from "../molecule";
|
|
4
|
+
import { createChunkedCKBFSV3Witnesses, CKBFSV3WitnessOptions } from "../witness";
|
|
5
|
+
import {
|
|
6
|
+
getCKBFSScriptConfig,
|
|
7
|
+
NetworkType,
|
|
8
|
+
ProtocolVersion,
|
|
9
|
+
DEFAULT_NETWORK,
|
|
10
|
+
} from "../constants";
|
|
11
|
+
import {
|
|
12
|
+
ensureHexPrefix,
|
|
13
|
+
BaseCKBFSCellOptions,
|
|
14
|
+
BasePublishOptions,
|
|
15
|
+
BaseAppendOptions
|
|
16
|
+
} from "./shared";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* V3 CKBFS transaction utilities - witnesses-based storage with backlinks
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* V3-specific Options for publishing a file to CKBFS
|
|
24
|
+
*/
|
|
25
|
+
export interface PublishV3Options extends Omit<BaseCKBFSCellOptions, 'version'> {
|
|
26
|
+
contentChunks: Uint8Array[];
|
|
27
|
+
feeRate?: number;
|
|
28
|
+
from?: Transaction;
|
|
29
|
+
version: typeof ProtocolVersion.V3;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* V3-specific Options for appending content to a CKBFS file
|
|
34
|
+
*/
|
|
35
|
+
export interface AppendV3Options {
|
|
36
|
+
ckbfsCell: {
|
|
37
|
+
outPoint: { txHash: string; index: number };
|
|
38
|
+
data: CKBFSDataType;
|
|
39
|
+
type: Script;
|
|
40
|
+
lock: Script;
|
|
41
|
+
capacity: bigint;
|
|
42
|
+
};
|
|
43
|
+
contentChunks: Uint8Array[];
|
|
44
|
+
feeRate?: number;
|
|
45
|
+
network?: NetworkType;
|
|
46
|
+
version: typeof ProtocolVersion.V3;
|
|
47
|
+
from?: Transaction;
|
|
48
|
+
previousTxHash: string;
|
|
49
|
+
previousWitnessIndex: number;
|
|
50
|
+
previousChecksum: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* V3-specific Options for transferring a CKBFS file
|
|
55
|
+
*/
|
|
56
|
+
export interface TransferV3Options {
|
|
57
|
+
ckbfsCell: {
|
|
58
|
+
outPoint: { txHash: string; index: number };
|
|
59
|
+
data: CKBFSDataType;
|
|
60
|
+
type: Script;
|
|
61
|
+
lock: Script;
|
|
62
|
+
capacity: bigint;
|
|
63
|
+
};
|
|
64
|
+
newLock: Script;
|
|
65
|
+
feeRate?: number;
|
|
66
|
+
network?: NetworkType;
|
|
67
|
+
version: typeof ProtocolVersion.V3;
|
|
68
|
+
from?: Transaction;
|
|
69
|
+
previousTxHash: string;
|
|
70
|
+
previousWitnessIndex: number;
|
|
71
|
+
previousChecksum: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates a CKBFS v3 cell
|
|
76
|
+
* @param options Options for creating the CKBFS cell
|
|
77
|
+
* @returns The created cell output
|
|
78
|
+
*/
|
|
79
|
+
export function createCKBFSV3Cell(options: BaseCKBFSCellOptions) {
|
|
80
|
+
const {
|
|
81
|
+
contentType,
|
|
82
|
+
filename,
|
|
83
|
+
capacity,
|
|
84
|
+
lock,
|
|
85
|
+
network = DEFAULT_NETWORK,
|
|
86
|
+
useTypeID = false,
|
|
87
|
+
} = options;
|
|
88
|
+
|
|
89
|
+
// Get CKBFS script config for v3
|
|
90
|
+
const config = getCKBFSScriptConfig(network, ProtocolVersion.V3, useTypeID);
|
|
91
|
+
|
|
92
|
+
// Create pre CKBFS type script
|
|
93
|
+
const preCkbfsTypeScript = new Script(
|
|
94
|
+
ensureHexPrefix(config.codeHash),
|
|
95
|
+
config.hashType as any,
|
|
96
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Return the cell output
|
|
100
|
+
return {
|
|
101
|
+
lock,
|
|
102
|
+
type: preCkbfsTypeScript,
|
|
103
|
+
capacity: capacity || 200n * 100000000n, // Default 200 CKB
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Prepares a transaction for publishing a file to CKBFS v3 without fee and change handling
|
|
109
|
+
* @param options Options for publishing the file
|
|
110
|
+
* @returns Promise resolving to the prepared transaction and the output index of CKBFS Cell
|
|
111
|
+
*/
|
|
112
|
+
export async function preparePublishV3Transaction(
|
|
113
|
+
options: PublishV3Options,
|
|
114
|
+
): Promise<{tx: Transaction, outputIndex: number, emptyTypeID: boolean}> {
|
|
115
|
+
const {
|
|
116
|
+
from,
|
|
117
|
+
contentChunks,
|
|
118
|
+
contentType,
|
|
119
|
+
filename,
|
|
120
|
+
lock,
|
|
121
|
+
capacity,
|
|
122
|
+
network = DEFAULT_NETWORK,
|
|
123
|
+
useTypeID = false,
|
|
124
|
+
} = options;
|
|
125
|
+
|
|
126
|
+
// Calculate checksum for the combined content
|
|
127
|
+
const combinedContent = Buffer.concat(contentChunks);
|
|
128
|
+
const checksum = await calculateChecksum(combinedContent);
|
|
129
|
+
|
|
130
|
+
// V3 format uses single index (first witness containing content)
|
|
131
|
+
const contentStartIndex = from?.witnesses.length || 1;
|
|
132
|
+
|
|
133
|
+
// Create CKBFS v3 witnesses (no backlinks for publish operation)
|
|
134
|
+
const ckbfsWitnesses = createChunkedCKBFSV3Witnesses(contentChunks, {
|
|
135
|
+
// For publish operation, all previous fields are zero
|
|
136
|
+
previousTxHash: '0x' + '00'.repeat(32),
|
|
137
|
+
previousWitnessIndex: 0,
|
|
138
|
+
previousChecksum: 0,
|
|
139
|
+
startIndex: contentStartIndex,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Create CKBFS v3 cell output data
|
|
143
|
+
const outputData = CKBFSData.pack(
|
|
144
|
+
{
|
|
145
|
+
index: contentStartIndex,
|
|
146
|
+
checksum,
|
|
147
|
+
contentType,
|
|
148
|
+
filename,
|
|
149
|
+
},
|
|
150
|
+
ProtocolVersion.V3,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Get CKBFS script config
|
|
154
|
+
const config = getCKBFSScriptConfig(network, ProtocolVersion.V3, useTypeID);
|
|
155
|
+
|
|
156
|
+
// Create pre CKBFS type script
|
|
157
|
+
const preCkbfsTypeScript = new Script(
|
|
158
|
+
ensureHexPrefix(config.codeHash),
|
|
159
|
+
config.hashType as any,
|
|
160
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// Calculate the required capacity for the output cell
|
|
164
|
+
const ckbfsCellSize =
|
|
165
|
+
BigInt(
|
|
166
|
+
outputData.length +
|
|
167
|
+
preCkbfsTypeScript.occupiedSize +
|
|
168
|
+
lock.occupiedSize +
|
|
169
|
+
8,
|
|
170
|
+
) * 100000000n;
|
|
171
|
+
|
|
172
|
+
// Create pre transaction without cell deps initially
|
|
173
|
+
let preTx: Transaction;
|
|
174
|
+
if(from) {
|
|
175
|
+
// If from is not empty, inject/merge the fields
|
|
176
|
+
preTx = Transaction.from({
|
|
177
|
+
...from,
|
|
178
|
+
outputs: from.outputs.length === 0
|
|
179
|
+
? [
|
|
180
|
+
createCKBFSV3Cell({
|
|
181
|
+
contentType,
|
|
182
|
+
filename,
|
|
183
|
+
lock,
|
|
184
|
+
network,
|
|
185
|
+
useTypeID,
|
|
186
|
+
capacity: ckbfsCellSize || capacity,
|
|
187
|
+
}),
|
|
188
|
+
]
|
|
189
|
+
: [
|
|
190
|
+
...from.outputs,
|
|
191
|
+
createCKBFSV3Cell({
|
|
192
|
+
contentType,
|
|
193
|
+
filename,
|
|
194
|
+
lock,
|
|
195
|
+
network,
|
|
196
|
+
useTypeID,
|
|
197
|
+
capacity: ckbfsCellSize || capacity,
|
|
198
|
+
}),
|
|
199
|
+
],
|
|
200
|
+
witnesses: from.witnesses.length === 0
|
|
201
|
+
? [
|
|
202
|
+
[], // Empty secp witness for signing if not provided
|
|
203
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
204
|
+
]
|
|
205
|
+
: [
|
|
206
|
+
...from.witnesses,
|
|
207
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
208
|
+
],
|
|
209
|
+
outputsData: from.outputsData.length === 0
|
|
210
|
+
? [outputData]
|
|
211
|
+
: [
|
|
212
|
+
...from.outputsData,
|
|
213
|
+
outputData,
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
} else {
|
|
217
|
+
preTx = Transaction.from({
|
|
218
|
+
outputs: [
|
|
219
|
+
createCKBFSV3Cell({
|
|
220
|
+
contentType,
|
|
221
|
+
filename,
|
|
222
|
+
lock,
|
|
223
|
+
network,
|
|
224
|
+
useTypeID,
|
|
225
|
+
capacity: ckbfsCellSize || capacity,
|
|
226
|
+
}),
|
|
227
|
+
],
|
|
228
|
+
witnesses: [
|
|
229
|
+
[], // Empty secp witness for signing
|
|
230
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
231
|
+
],
|
|
232
|
+
outputsData: [outputData],
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Add the CKBFS dep group cell dependency
|
|
237
|
+
preTx.addCellDeps({
|
|
238
|
+
outPoint: {
|
|
239
|
+
txHash: ensureHexPrefix(config.depTxHash),
|
|
240
|
+
index: config.depIndex || 0,
|
|
241
|
+
},
|
|
242
|
+
depType: "depGroup",
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Create type ID args
|
|
246
|
+
const outputIndex = from ? from.outputs.length : 0;
|
|
247
|
+
const args = preTx.inputs.length > 0 ? ccc.hashTypeId(preTx.inputs[0], outputIndex) : "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
248
|
+
|
|
249
|
+
// Create CKBFS type script with type ID
|
|
250
|
+
const ckbfsTypeScript = new Script(
|
|
251
|
+
ensureHexPrefix(config.codeHash),
|
|
252
|
+
config.hashType as any,
|
|
253
|
+
args,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
// Create final transaction with same cell deps as preTx
|
|
257
|
+
const tx = Transaction.from({
|
|
258
|
+
cellDeps: preTx.cellDeps,
|
|
259
|
+
witnesses: preTx.witnesses,
|
|
260
|
+
outputsData: preTx.outputsData,
|
|
261
|
+
inputs: preTx.inputs,
|
|
262
|
+
outputs: outputIndex === 0
|
|
263
|
+
? [
|
|
264
|
+
{
|
|
265
|
+
lock,
|
|
266
|
+
type: ckbfsTypeScript,
|
|
267
|
+
capacity: preTx.outputs[outputIndex].capacity,
|
|
268
|
+
},
|
|
269
|
+
]
|
|
270
|
+
: [
|
|
271
|
+
...preTx.outputs.slice(0, outputIndex), // Include rest of outputs (e.g., change)
|
|
272
|
+
{
|
|
273
|
+
lock,
|
|
274
|
+
type: ckbfsTypeScript,
|
|
275
|
+
capacity: preTx.outputs[outputIndex].capacity,
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return {tx, outputIndex, emptyTypeID: args === "0x0000000000000000000000000000000000000000000000000000000000000000"};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Creates a transaction for publishing a file to CKBFS v3
|
|
285
|
+
* @param signer The signer to use for the transaction
|
|
286
|
+
* @param options Options for publishing the file
|
|
287
|
+
* @returns Promise resolving to the created transaction
|
|
288
|
+
*/
|
|
289
|
+
export async function createPublishV3Transaction(
|
|
290
|
+
signer: Signer,
|
|
291
|
+
options: PublishV3Options,
|
|
292
|
+
): Promise<Transaction> {
|
|
293
|
+
const {
|
|
294
|
+
feeRate,
|
|
295
|
+
lock,
|
|
296
|
+
} = options;
|
|
297
|
+
|
|
298
|
+
// Use preparePublishV3Transaction to create the base transaction
|
|
299
|
+
const { tx: preTx, outputIndex, emptyTypeID } = await preparePublishV3Transaction(options);
|
|
300
|
+
|
|
301
|
+
// Complete inputs by capacity
|
|
302
|
+
await preTx.completeInputsByCapacity(signer);
|
|
303
|
+
|
|
304
|
+
// Complete fee change to lock
|
|
305
|
+
await preTx.completeFeeChangeToLock(signer, lock, feeRate || 2000);
|
|
306
|
+
|
|
307
|
+
// If emptyTypeID is true, we need to create the proper type ID args
|
|
308
|
+
if (emptyTypeID) {
|
|
309
|
+
// Get CKBFS script config
|
|
310
|
+
const config = getCKBFSScriptConfig(options.network || DEFAULT_NETWORK, ProtocolVersion.V3, options.useTypeID || false);
|
|
311
|
+
|
|
312
|
+
// Create type ID args
|
|
313
|
+
const args = ccc.hashTypeId(preTx.inputs[0], outputIndex);
|
|
314
|
+
|
|
315
|
+
// Create CKBFS type script with type ID
|
|
316
|
+
const ckbfsTypeScript = new Script(
|
|
317
|
+
ensureHexPrefix(config.codeHash),
|
|
318
|
+
config.hashType as any,
|
|
319
|
+
args,
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// Create final transaction with updated type script
|
|
323
|
+
const tx = Transaction.from({
|
|
324
|
+
cellDeps: preTx.cellDeps,
|
|
325
|
+
witnesses: preTx.witnesses,
|
|
326
|
+
outputsData: preTx.outputsData,
|
|
327
|
+
inputs: preTx.inputs,
|
|
328
|
+
outputs: preTx.outputs.map((output, index) =>
|
|
329
|
+
index === outputIndex
|
|
330
|
+
? {
|
|
331
|
+
...output,
|
|
332
|
+
type: ckbfsTypeScript,
|
|
333
|
+
}
|
|
334
|
+
: output
|
|
335
|
+
),
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
return tx;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return preTx;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Creates a transaction for appending content to a CKBFS v3 file
|
|
346
|
+
* @param signer The signer to use for the transaction
|
|
347
|
+
* @param options Options for appending content
|
|
348
|
+
* @returns Promise resolving to the created transaction
|
|
349
|
+
*/
|
|
350
|
+
export async function createAppendV3Transaction(
|
|
351
|
+
signer: Signer,
|
|
352
|
+
options: AppendV3Options,
|
|
353
|
+
): Promise<Transaction> {
|
|
354
|
+
const {
|
|
355
|
+
ckbfsCell,
|
|
356
|
+
contentChunks,
|
|
357
|
+
feeRate,
|
|
358
|
+
network = DEFAULT_NETWORK,
|
|
359
|
+
previousTxHash,
|
|
360
|
+
previousWitnessIndex,
|
|
361
|
+
previousChecksum,
|
|
362
|
+
} = options;
|
|
363
|
+
|
|
364
|
+
// Calculate new checksum by updating from previous checksum
|
|
365
|
+
const combinedContent = Buffer.concat(contentChunks);
|
|
366
|
+
const newChecksum = await updateChecksum(previousChecksum, combinedContent);
|
|
367
|
+
|
|
368
|
+
// Create CKBFS v3 witnesses with backlink info
|
|
369
|
+
const ckbfsWitnesses = createChunkedCKBFSV3Witnesses(contentChunks, {
|
|
370
|
+
previousTxHash,
|
|
371
|
+
previousWitnessIndex,
|
|
372
|
+
previousChecksum,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// V3 format uses single index (first witness containing content)
|
|
376
|
+
const contentStartIndex = 1; // First witness is for signer, content starts at index 1
|
|
377
|
+
|
|
378
|
+
// Create updated CKBFS v3 cell output data
|
|
379
|
+
const outputData = CKBFSData.pack(
|
|
380
|
+
{
|
|
381
|
+
index: contentStartIndex,
|
|
382
|
+
checksum: newChecksum,
|
|
383
|
+
contentType: ckbfsCell.data.contentType,
|
|
384
|
+
filename: ckbfsCell.data.filename,
|
|
385
|
+
},
|
|
386
|
+
ProtocolVersion.V3,
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
// Get CKBFS script config
|
|
390
|
+
const config = getCKBFSScriptConfig(network, ProtocolVersion.V3, false);
|
|
391
|
+
|
|
392
|
+
// Calculate the required capacity for the output cell
|
|
393
|
+
const ckbfsCellSize =
|
|
394
|
+
BigInt(outputData.length + ckbfsCell.type.occupiedSize + ckbfsCell.lock.occupiedSize + 8) *
|
|
395
|
+
100000000n;
|
|
396
|
+
|
|
397
|
+
// Use the maximum value between calculated size and original capacity
|
|
398
|
+
const outputCapacity = ckbfsCellSize > ckbfsCell.capacity ? ckbfsCellSize : ckbfsCell.capacity;
|
|
399
|
+
|
|
400
|
+
// Create transaction
|
|
401
|
+
const tx = Transaction.from({
|
|
402
|
+
inputs: [
|
|
403
|
+
{
|
|
404
|
+
previousOutput: ckbfsCell.outPoint,
|
|
405
|
+
since: "0x0",
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
outputs: [
|
|
409
|
+
{
|
|
410
|
+
lock: ckbfsCell.lock,
|
|
411
|
+
type: ckbfsCell.type,
|
|
412
|
+
capacity: outputCapacity,
|
|
413
|
+
},
|
|
414
|
+
],
|
|
415
|
+
witnesses: [
|
|
416
|
+
[], // Empty secp witness for signing
|
|
417
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
418
|
+
],
|
|
419
|
+
outputsData: [outputData],
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Add the CKBFS dep group cell dependency
|
|
423
|
+
tx.addCellDeps({
|
|
424
|
+
outPoint: {
|
|
425
|
+
txHash: ensureHexPrefix(config.depTxHash),
|
|
426
|
+
index: config.depIndex || 0,
|
|
427
|
+
},
|
|
428
|
+
depType: "depGroup",
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Complete inputs by capacity for fees
|
|
432
|
+
await tx.completeInputsByCapacity(signer);
|
|
433
|
+
|
|
434
|
+
// Complete fee change to lock
|
|
435
|
+
await tx.completeFeeChangeToLock(signer, ckbfsCell.lock, feeRate || 2000);
|
|
436
|
+
|
|
437
|
+
return tx;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Creates a transaction for transferring ownership of a CKBFS v3 file
|
|
442
|
+
* @param signer The signer to use for the transaction
|
|
443
|
+
* @param options Options for transferring the file
|
|
444
|
+
* @returns Promise resolving to the created transaction
|
|
445
|
+
*/
|
|
446
|
+
export async function createTransferV3Transaction(
|
|
447
|
+
signer: Signer,
|
|
448
|
+
options: TransferV3Options,
|
|
449
|
+
): Promise<Transaction> {
|
|
450
|
+
const {
|
|
451
|
+
ckbfsCell,
|
|
452
|
+
newLock,
|
|
453
|
+
feeRate,
|
|
454
|
+
network = DEFAULT_NETWORK,
|
|
455
|
+
previousTxHash,
|
|
456
|
+
previousWitnessIndex,
|
|
457
|
+
previousChecksum,
|
|
458
|
+
} = options;
|
|
459
|
+
|
|
460
|
+
// Create CKBFS v3 witness with backlink info but no content (transfer only)
|
|
461
|
+
const ckbfsWitnesses = createChunkedCKBFSV3Witnesses([new Uint8Array(0)], {
|
|
462
|
+
previousTxHash,
|
|
463
|
+
previousWitnessIndex,
|
|
464
|
+
previousChecksum,
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// V3 format uses single index (first witness containing backlink)
|
|
468
|
+
const contentStartIndex = 1; // First witness is for signer, backlink at index 1
|
|
469
|
+
|
|
470
|
+
// Create CKBFS v3 cell output data (unchanged for transfer)
|
|
471
|
+
const outputData = CKBFSData.pack(
|
|
472
|
+
{
|
|
473
|
+
index: contentStartIndex,
|
|
474
|
+
checksum: ckbfsCell.data.checksum, // Checksum unchanged in transfer
|
|
475
|
+
contentType: ckbfsCell.data.contentType,
|
|
476
|
+
filename: ckbfsCell.data.filename,
|
|
477
|
+
},
|
|
478
|
+
ProtocolVersion.V3,
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// Get CKBFS script config
|
|
482
|
+
const config = getCKBFSScriptConfig(network, ProtocolVersion.V3, false);
|
|
483
|
+
|
|
484
|
+
// Create transaction
|
|
485
|
+
const tx = Transaction.from({
|
|
486
|
+
inputs: [
|
|
487
|
+
{
|
|
488
|
+
previousOutput: ckbfsCell.outPoint,
|
|
489
|
+
since: "0x0",
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
outputs: [
|
|
493
|
+
{
|
|
494
|
+
lock: newLock,
|
|
495
|
+
type: ckbfsCell.type,
|
|
496
|
+
capacity: ckbfsCell.capacity,
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
witnesses: [
|
|
500
|
+
[], // Empty secp witness for signing
|
|
501
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
502
|
+
],
|
|
503
|
+
outputsData: [outputData],
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// Add the CKBFS dep group cell dependency
|
|
507
|
+
tx.addCellDeps({
|
|
508
|
+
outPoint: {
|
|
509
|
+
txHash: ensureHexPrefix(config.depTxHash),
|
|
510
|
+
index: config.depIndex || 0,
|
|
511
|
+
},
|
|
512
|
+
depType: "depGroup",
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// Complete inputs by capacity for fees
|
|
516
|
+
await tx.completeInputsByCapacity(signer);
|
|
517
|
+
|
|
518
|
+
// Complete fee change to lock
|
|
519
|
+
await tx.completeFeeChangeToLock(signer, newLock, feeRate || 2000);
|
|
520
|
+
|
|
521
|
+
return tx;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Creates a complete transaction for publishing a file to CKBFS v3
|
|
526
|
+
* @param signer The signer to use for the transaction
|
|
527
|
+
* @param options Options for publishing the file
|
|
528
|
+
* @returns Promise resolving to the signed transaction
|
|
529
|
+
*/
|
|
530
|
+
export async function publishCKBFSV3(
|
|
531
|
+
signer: Signer,
|
|
532
|
+
options: PublishV3Options,
|
|
533
|
+
): Promise<Transaction> {
|
|
534
|
+
const tx = await createPublishV3Transaction(signer, options);
|
|
535
|
+
return signer.signTransaction(tx);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Creates a complete transaction for appending content to a CKBFS v3 file
|
|
540
|
+
* @param signer The signer to use for the transaction
|
|
541
|
+
* @param options Options for appending content
|
|
542
|
+
* @returns Promise resolving to the signed transaction
|
|
543
|
+
*/
|
|
544
|
+
export async function appendCKBFSV3(
|
|
545
|
+
signer: Signer,
|
|
546
|
+
options: AppendV3Options,
|
|
547
|
+
): Promise<Transaction> {
|
|
548
|
+
const tx = await createAppendV3Transaction(signer, options);
|
|
549
|
+
return signer.signTransaction(tx);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Creates a complete transaction for transferring ownership of a CKBFS v3 file
|
|
554
|
+
* @param signer The signer to use for the transaction
|
|
555
|
+
* @param options Options for transferring the file
|
|
556
|
+
* @returns Promise resolving to the signed transaction
|
|
557
|
+
*/
|
|
558
|
+
export async function transferCKBFSV3(
|
|
559
|
+
signer: Signer,
|
|
560
|
+
options: TransferV3Options,
|
|
561
|
+
): Promise<Transaction> {
|
|
562
|
+
const tx = await createTransferV3Transaction(signer, options);
|
|
563
|
+
return signer.signTransaction(tx);
|
|
564
|
+
}
|