@ckbfs/api 2.0.1 → 2.0.2
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/dist/utils/transactions/v3.d.ts +16 -0
- package/dist/utils/transactions/v3.js +183 -12
- package/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/utils/transactions/v3.ts +229 -13
|
@@ -87,6 +87,15 @@ export declare function preparePublishV3Transaction(options: PublishV3Options):
|
|
|
87
87
|
* @returns Promise resolving to the created transaction
|
|
88
88
|
*/
|
|
89
89
|
export declare function createPublishV3Transaction(signer: Signer, options: PublishV3Options): Promise<Transaction>;
|
|
90
|
+
/**
|
|
91
|
+
* Prepares a transaction for appending content to a CKBFS v3 file without fee and change handling
|
|
92
|
+
* @param options Options for appending content
|
|
93
|
+
* @returns Promise resolving to the prepared transaction and the output index of CKBFS Cell
|
|
94
|
+
*/
|
|
95
|
+
export declare function prepareAppendV3Transaction(options: AppendV3Options): Promise<{
|
|
96
|
+
tx: Transaction;
|
|
97
|
+
outputIndex: number;
|
|
98
|
+
}>;
|
|
90
99
|
/**
|
|
91
100
|
* Creates a transaction for appending content to a CKBFS v3 file
|
|
92
101
|
* @param signer The signer to use for the transaction
|
|
@@ -94,6 +103,13 @@ export declare function createPublishV3Transaction(signer: Signer, options: Publ
|
|
|
94
103
|
* @returns Promise resolving to the created transaction
|
|
95
104
|
*/
|
|
96
105
|
export declare function createAppendV3Transaction(signer: Signer, options: AppendV3Options): Promise<Transaction>;
|
|
106
|
+
/**
|
|
107
|
+
* Creates a transaction for appending content to a CKBFS v3 file (dry run without fee completion)
|
|
108
|
+
* @param signer The signer to use for the transaction
|
|
109
|
+
* @param options Options for appending content
|
|
110
|
+
* @returns Promise resolving to the created transaction
|
|
111
|
+
*/
|
|
112
|
+
export declare function createAppendV3TransactionDry(signer: Signer, options: AppendV3Options): Promise<Transaction>;
|
|
97
113
|
/**
|
|
98
114
|
* Creates a transaction for transferring ownership of a CKBFS v3 file
|
|
99
115
|
* @param signer The signer to use for the transaction
|
|
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createCKBFSV3Cell = createCKBFSV3Cell;
|
|
4
4
|
exports.preparePublishV3Transaction = preparePublishV3Transaction;
|
|
5
5
|
exports.createPublishV3Transaction = createPublishV3Transaction;
|
|
6
|
+
exports.prepareAppendV3Transaction = prepareAppendV3Transaction;
|
|
6
7
|
exports.createAppendV3Transaction = createAppendV3Transaction;
|
|
8
|
+
exports.createAppendV3TransactionDry = createAppendV3TransactionDry;
|
|
7
9
|
exports.createTransferV3Transaction = createTransferV3Transaction;
|
|
8
10
|
exports.publishCKBFSV3 = publishCKBFSV3;
|
|
9
11
|
exports.appendCKBFSV3 = appendCKBFSV3;
|
|
@@ -209,6 +211,125 @@ async function createPublishV3Transaction(signer, options) {
|
|
|
209
211
|
}
|
|
210
212
|
return preTx;
|
|
211
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Prepares a transaction for appending content to a CKBFS v3 file without fee and change handling
|
|
216
|
+
* @param options Options for appending content
|
|
217
|
+
* @returns Promise resolving to the prepared transaction and the output index of CKBFS Cell
|
|
218
|
+
*/
|
|
219
|
+
async function prepareAppendV3Transaction(options) {
|
|
220
|
+
const { from, ckbfsCell, contentChunks, network = constants_1.DEFAULT_NETWORK, previousTxHash, previousWitnessIndex, previousChecksum, } = options;
|
|
221
|
+
// Calculate new checksum by updating from previous checksum
|
|
222
|
+
const combinedContent = Buffer.concat(contentChunks);
|
|
223
|
+
const newChecksum = await (0, checksum_1.updateChecksum)(previousChecksum, combinedContent);
|
|
224
|
+
// Calculate the actual witness indices where our content is placed
|
|
225
|
+
const contentStartIndex = from?.witnesses.length || 1;
|
|
226
|
+
// Create CKBFS v3 witnesses with backlink info
|
|
227
|
+
const ckbfsWitnesses = (0, witness_1.createChunkedCKBFSV3Witnesses)(contentChunks, {
|
|
228
|
+
previousTxHash,
|
|
229
|
+
previousWitnessIndex,
|
|
230
|
+
previousChecksum,
|
|
231
|
+
startIndex: contentStartIndex,
|
|
232
|
+
});
|
|
233
|
+
// Create updated CKBFS v3 cell output data
|
|
234
|
+
const outputData = molecule_1.CKBFSData.pack({
|
|
235
|
+
index: contentStartIndex,
|
|
236
|
+
checksum: newChecksum,
|
|
237
|
+
contentType: ckbfsCell.data.contentType,
|
|
238
|
+
filename: ckbfsCell.data.filename,
|
|
239
|
+
}, constants_1.ProtocolVersion.V3);
|
|
240
|
+
// Get CKBFS script config
|
|
241
|
+
const config = (0, constants_1.getCKBFSScriptConfig)(network, constants_1.ProtocolVersion.V3, false);
|
|
242
|
+
// Calculate the required capacity for the output cell
|
|
243
|
+
const ckbfsCellSize = BigInt(outputData.length + ckbfsCell.type.occupiedSize + ckbfsCell.lock.occupiedSize + 8) *
|
|
244
|
+
100000000n;
|
|
245
|
+
// Use the maximum value between calculated size and original capacity
|
|
246
|
+
const outputCapacity = ckbfsCellSize > ckbfsCell.capacity ? ckbfsCellSize : ckbfsCell.capacity;
|
|
247
|
+
// Create initial transaction with the CKBFS cell input
|
|
248
|
+
let preTx;
|
|
249
|
+
if (from) {
|
|
250
|
+
// If from is not empty, inject/merge the fields
|
|
251
|
+
preTx = core_1.Transaction.from({
|
|
252
|
+
...from,
|
|
253
|
+
inputs: from.inputs.length === 0
|
|
254
|
+
? [
|
|
255
|
+
{
|
|
256
|
+
previousOutput: ckbfsCell.outPoint,
|
|
257
|
+
since: "0x0",
|
|
258
|
+
},
|
|
259
|
+
]
|
|
260
|
+
: [
|
|
261
|
+
...from.inputs,
|
|
262
|
+
{
|
|
263
|
+
previousOutput: ckbfsCell.outPoint,
|
|
264
|
+
since: "0x0",
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
outputs: from.outputs.length === 0
|
|
268
|
+
? [
|
|
269
|
+
{
|
|
270
|
+
lock: ckbfsCell.lock,
|
|
271
|
+
type: ckbfsCell.type,
|
|
272
|
+
capacity: outputCapacity,
|
|
273
|
+
},
|
|
274
|
+
]
|
|
275
|
+
: [
|
|
276
|
+
...from.outputs,
|
|
277
|
+
{
|
|
278
|
+
lock: ckbfsCell.lock,
|
|
279
|
+
type: ckbfsCell.type,
|
|
280
|
+
capacity: outputCapacity,
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
outputsData: from.outputsData.length === 0
|
|
284
|
+
? [outputData]
|
|
285
|
+
: [
|
|
286
|
+
...from.outputsData,
|
|
287
|
+
outputData,
|
|
288
|
+
],
|
|
289
|
+
witnesses: from.witnesses.length === 0
|
|
290
|
+
? [
|
|
291
|
+
[], // Empty secp witness for signing if not provided
|
|
292
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
293
|
+
]
|
|
294
|
+
: [
|
|
295
|
+
...from.witnesses,
|
|
296
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
297
|
+
],
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
preTx = core_1.Transaction.from({
|
|
302
|
+
inputs: [
|
|
303
|
+
{
|
|
304
|
+
previousOutput: ckbfsCell.outPoint,
|
|
305
|
+
since: "0x0",
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
outputs: [
|
|
309
|
+
{
|
|
310
|
+
lock: ckbfsCell.lock,
|
|
311
|
+
type: ckbfsCell.type,
|
|
312
|
+
capacity: outputCapacity,
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
witnesses: [
|
|
316
|
+
[], // Empty secp witness for signing
|
|
317
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
318
|
+
],
|
|
319
|
+
outputsData: [outputData],
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
// Add the CKBFS dep group cell dependency
|
|
323
|
+
preTx.addCellDeps({
|
|
324
|
+
outPoint: {
|
|
325
|
+
txHash: (0, shared_1.ensureHexPrefix)(config.depTxHash),
|
|
326
|
+
index: config.depIndex || 0,
|
|
327
|
+
},
|
|
328
|
+
depType: "depGroup",
|
|
329
|
+
});
|
|
330
|
+
const outputIndex = from ? from.outputs.length : 0;
|
|
331
|
+
return { tx: preTx, outputIndex };
|
|
332
|
+
}
|
|
212
333
|
/**
|
|
213
334
|
* Creates a transaction for appending content to a CKBFS v3 file
|
|
214
335
|
* @param signer The signer to use for the transaction
|
|
@@ -216,18 +337,61 @@ async function createPublishV3Transaction(signer, options) {
|
|
|
216
337
|
* @returns Promise resolving to the created transaction
|
|
217
338
|
*/
|
|
218
339
|
async function createAppendV3Transaction(signer, options) {
|
|
219
|
-
const { ckbfsCell,
|
|
340
|
+
const { ckbfsCell, feeRate, } = options;
|
|
341
|
+
const { lock } = ckbfsCell;
|
|
342
|
+
// Use prepareAppendV3Transaction to create the base transaction
|
|
343
|
+
const { tx: preTx, outputIndex } = await prepareAppendV3Transaction(options);
|
|
344
|
+
// Get the recommended address to ensure lock script cell deps are included
|
|
345
|
+
const address = await signer.getRecommendedAddressObj();
|
|
346
|
+
const inputsBefore = preTx.inputs.length;
|
|
347
|
+
// If we need more capacity than the original cell had, add additional inputs
|
|
348
|
+
if (preTx.outputs[outputIndex].capacity > ckbfsCell.capacity) {
|
|
349
|
+
console.log(`Need additional capacity: ${preTx.outputs[outputIndex].capacity - ckbfsCell.capacity} shannons`);
|
|
350
|
+
// Add more inputs to cover the increased capacity
|
|
351
|
+
await preTx.completeInputsByCapacity(signer);
|
|
352
|
+
}
|
|
353
|
+
const witnesses = [];
|
|
354
|
+
// add empty witness for signer if ckbfs's lock is the same as signer's lock
|
|
355
|
+
if (address.script.hash() === lock.hash()) {
|
|
356
|
+
witnesses.push("0x");
|
|
357
|
+
}
|
|
358
|
+
// add ckbfs witnesses (skip the first witness which is for signing)
|
|
359
|
+
witnesses.push(...preTx.witnesses.slice(1));
|
|
360
|
+
// Add empty witnesses for additional signer inputs
|
|
361
|
+
// This is to ensure that the transaction is valid and can be signed
|
|
362
|
+
for (let i = inputsBefore; i < preTx.inputs.length; i++) {
|
|
363
|
+
witnesses.push("0x");
|
|
364
|
+
}
|
|
365
|
+
preTx.witnesses = witnesses;
|
|
366
|
+
// Complete fee
|
|
367
|
+
await preTx.completeFeeChangeToLock(signer, address.script, feeRate || 2000);
|
|
368
|
+
return preTx;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Creates a transaction for appending content to a CKBFS v3 file (dry run without fee completion)
|
|
372
|
+
* @param signer The signer to use for the transaction
|
|
373
|
+
* @param options Options for appending content
|
|
374
|
+
* @returns Promise resolving to the created transaction
|
|
375
|
+
*/
|
|
376
|
+
async function createAppendV3TransactionDry(signer, options) {
|
|
377
|
+
const { ckbfsCell, contentChunks, network = constants_1.DEFAULT_NETWORK, previousTxHash, previousWitnessIndex, previousChecksum, } = options;
|
|
378
|
+
const { lock } = ckbfsCell;
|
|
220
379
|
// Calculate new checksum by updating from previous checksum
|
|
221
380
|
const combinedContent = Buffer.concat(contentChunks);
|
|
222
381
|
const newChecksum = await (0, checksum_1.updateChecksum)(previousChecksum, combinedContent);
|
|
382
|
+
// Get the recommended address to ensure lock script cell deps are included
|
|
383
|
+
const address = await signer.getRecommendedAddressObj();
|
|
384
|
+
// Calculate the actual witness indices where our content is placed
|
|
385
|
+
// CKBFS data starts at index 1 if signer's lock script is the same as ckbfs's lock script
|
|
386
|
+
// else CKBFS data starts at index 0
|
|
387
|
+
const contentStartIndex = address.script.hash() === lock.hash() ? 1 : 0;
|
|
223
388
|
// Create CKBFS v3 witnesses with backlink info
|
|
224
389
|
const ckbfsWitnesses = (0, witness_1.createChunkedCKBFSV3Witnesses)(contentChunks, {
|
|
225
390
|
previousTxHash,
|
|
226
391
|
previousWitnessIndex,
|
|
227
392
|
previousChecksum,
|
|
393
|
+
startIndex: contentStartIndex,
|
|
228
394
|
});
|
|
229
|
-
// V3 format uses single index (first witness containing content)
|
|
230
|
-
const contentStartIndex = 1; // First witness is for signer, content starts at index 1
|
|
231
395
|
// Create updated CKBFS v3 cell output data
|
|
232
396
|
const outputData = molecule_1.CKBFSData.pack({
|
|
233
397
|
index: contentStartIndex,
|
|
@@ -240,9 +404,10 @@ async function createAppendV3Transaction(signer, options) {
|
|
|
240
404
|
// Calculate the required capacity for the output cell
|
|
241
405
|
const ckbfsCellSize = BigInt(outputData.length + ckbfsCell.type.occupiedSize + ckbfsCell.lock.occupiedSize + 8) *
|
|
242
406
|
100000000n;
|
|
407
|
+
console.log(`Original capacity: ${ckbfsCell.capacity}, Calculated size: ${ckbfsCellSize}, Data size: ${outputData.length}`);
|
|
243
408
|
// Use the maximum value between calculated size and original capacity
|
|
244
409
|
const outputCapacity = ckbfsCellSize > ckbfsCell.capacity ? ckbfsCellSize : ckbfsCell.capacity;
|
|
245
|
-
// Create transaction
|
|
410
|
+
// Create initial transaction with the CKBFS cell input
|
|
246
411
|
const tx = core_1.Transaction.from({
|
|
247
412
|
inputs: [
|
|
248
413
|
{
|
|
@@ -257,10 +422,6 @@ async function createAppendV3Transaction(signer, options) {
|
|
|
257
422
|
capacity: outputCapacity,
|
|
258
423
|
},
|
|
259
424
|
],
|
|
260
|
-
witnesses: [
|
|
261
|
-
[], // Empty secp witness for signing
|
|
262
|
-
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
263
|
-
],
|
|
264
425
|
outputsData: [outputData],
|
|
265
426
|
});
|
|
266
427
|
// Add the CKBFS dep group cell dependency
|
|
@@ -271,10 +432,20 @@ async function createAppendV3Transaction(signer, options) {
|
|
|
271
432
|
},
|
|
272
433
|
depType: "depGroup",
|
|
273
434
|
});
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
//
|
|
277
|
-
|
|
435
|
+
const inputsBefore = tx.inputs.length;
|
|
436
|
+
const witnesses = [];
|
|
437
|
+
// add empty witness for signer if ckbfs's lock is the same as signer's lock
|
|
438
|
+
if (address.script.hash() === lock.hash()) {
|
|
439
|
+
witnesses.push("0x");
|
|
440
|
+
}
|
|
441
|
+
// add ckbfs witnesses
|
|
442
|
+
witnesses.push(...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`));
|
|
443
|
+
// Add empty witnesses for signer's input
|
|
444
|
+
// This is to ensure that the transaction is valid and can be signed
|
|
445
|
+
for (let i = inputsBefore; i < tx.inputs.length; i++) {
|
|
446
|
+
witnesses.push("0x");
|
|
447
|
+
}
|
|
448
|
+
tx.witnesses = witnesses;
|
|
278
449
|
return tx;
|
|
279
450
|
}
|
|
280
451
|
/**
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -34,7 +34,10 @@ import {
|
|
|
34
34
|
appendCKBFSV3,
|
|
35
35
|
transferCKBFSV3,
|
|
36
36
|
createPublishV3Transaction,
|
|
37
|
+
prepareAppendV3Transaction,
|
|
38
|
+
preparePublishV3Transaction,
|
|
37
39
|
createAppendV3Transaction,
|
|
40
|
+
createAppendV3TransactionDry,
|
|
38
41
|
createTransferV3Transaction,
|
|
39
42
|
} from "./utils/transaction";
|
|
40
43
|
import {
|
|
@@ -341,6 +341,150 @@ export async function createPublishV3Transaction(
|
|
|
341
341
|
return preTx;
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Prepares a transaction for appending content to a CKBFS v3 file without fee and change handling
|
|
346
|
+
* @param options Options for appending content
|
|
347
|
+
* @returns Promise resolving to the prepared transaction and the output index of CKBFS Cell
|
|
348
|
+
*/
|
|
349
|
+
export async function prepareAppendV3Transaction(
|
|
350
|
+
options: AppendV3Options,
|
|
351
|
+
): Promise<{tx: Transaction, outputIndex: number}> {
|
|
352
|
+
const {
|
|
353
|
+
from,
|
|
354
|
+
ckbfsCell,
|
|
355
|
+
contentChunks,
|
|
356
|
+
network = DEFAULT_NETWORK,
|
|
357
|
+
previousTxHash,
|
|
358
|
+
previousWitnessIndex,
|
|
359
|
+
previousChecksum,
|
|
360
|
+
} = options;
|
|
361
|
+
|
|
362
|
+
// Calculate new checksum by updating from previous checksum
|
|
363
|
+
const combinedContent = Buffer.concat(contentChunks);
|
|
364
|
+
const newChecksum = await updateChecksum(previousChecksum, combinedContent);
|
|
365
|
+
|
|
366
|
+
// Calculate the actual witness indices where our content is placed
|
|
367
|
+
const contentStartIndex = from?.witnesses.length || 1;
|
|
368
|
+
|
|
369
|
+
// Create CKBFS v3 witnesses with backlink info
|
|
370
|
+
const ckbfsWitnesses = createChunkedCKBFSV3Witnesses(contentChunks, {
|
|
371
|
+
previousTxHash,
|
|
372
|
+
previousWitnessIndex,
|
|
373
|
+
previousChecksum,
|
|
374
|
+
startIndex: contentStartIndex,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// Create updated CKBFS v3 cell output data
|
|
378
|
+
const outputData = CKBFSData.pack(
|
|
379
|
+
{
|
|
380
|
+
index: contentStartIndex,
|
|
381
|
+
checksum: newChecksum,
|
|
382
|
+
contentType: ckbfsCell.data.contentType,
|
|
383
|
+
filename: ckbfsCell.data.filename,
|
|
384
|
+
},
|
|
385
|
+
ProtocolVersion.V3,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
// Get CKBFS script config
|
|
389
|
+
const config = getCKBFSScriptConfig(network, ProtocolVersion.V3, false);
|
|
390
|
+
|
|
391
|
+
// Calculate the required capacity for the output cell
|
|
392
|
+
const ckbfsCellSize =
|
|
393
|
+
BigInt(outputData.length + ckbfsCell.type.occupiedSize + ckbfsCell.lock.occupiedSize + 8) *
|
|
394
|
+
100000000n;
|
|
395
|
+
|
|
396
|
+
// Use the maximum value between calculated size and original capacity
|
|
397
|
+
const outputCapacity = ckbfsCellSize > ckbfsCell.capacity ? ckbfsCellSize : ckbfsCell.capacity;
|
|
398
|
+
|
|
399
|
+
// Create initial transaction with the CKBFS cell input
|
|
400
|
+
let preTx: Transaction;
|
|
401
|
+
if (from) {
|
|
402
|
+
// If from is not empty, inject/merge the fields
|
|
403
|
+
preTx = Transaction.from({
|
|
404
|
+
...from,
|
|
405
|
+
inputs: from.inputs.length === 0
|
|
406
|
+
? [
|
|
407
|
+
{
|
|
408
|
+
previousOutput: ckbfsCell.outPoint,
|
|
409
|
+
since: "0x0",
|
|
410
|
+
},
|
|
411
|
+
]
|
|
412
|
+
: [
|
|
413
|
+
...from.inputs,
|
|
414
|
+
{
|
|
415
|
+
previousOutput: ckbfsCell.outPoint,
|
|
416
|
+
since: "0x0",
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
outputs: from.outputs.length === 0
|
|
420
|
+
? [
|
|
421
|
+
{
|
|
422
|
+
lock: ckbfsCell.lock,
|
|
423
|
+
type: ckbfsCell.type,
|
|
424
|
+
capacity: outputCapacity,
|
|
425
|
+
},
|
|
426
|
+
]
|
|
427
|
+
: [
|
|
428
|
+
...from.outputs,
|
|
429
|
+
{
|
|
430
|
+
lock: ckbfsCell.lock,
|
|
431
|
+
type: ckbfsCell.type,
|
|
432
|
+
capacity: outputCapacity,
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
outputsData: from.outputsData.length === 0
|
|
436
|
+
? [outputData]
|
|
437
|
+
: [
|
|
438
|
+
...from.outputsData,
|
|
439
|
+
outputData,
|
|
440
|
+
],
|
|
441
|
+
witnesses: from.witnesses.length === 0
|
|
442
|
+
? [
|
|
443
|
+
[], // Empty secp witness for signing if not provided
|
|
444
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
445
|
+
]
|
|
446
|
+
: [
|
|
447
|
+
...from.witnesses,
|
|
448
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
449
|
+
],
|
|
450
|
+
});
|
|
451
|
+
} else {
|
|
452
|
+
preTx = Transaction.from({
|
|
453
|
+
inputs: [
|
|
454
|
+
{
|
|
455
|
+
previousOutput: ckbfsCell.outPoint,
|
|
456
|
+
since: "0x0",
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
outputs: [
|
|
460
|
+
{
|
|
461
|
+
lock: ckbfsCell.lock,
|
|
462
|
+
type: ckbfsCell.type,
|
|
463
|
+
capacity: outputCapacity,
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
witnesses: [
|
|
467
|
+
[], // Empty secp witness for signing
|
|
468
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
469
|
+
],
|
|
470
|
+
outputsData: [outputData],
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Add the CKBFS dep group cell dependency
|
|
475
|
+
preTx.addCellDeps({
|
|
476
|
+
outPoint: {
|
|
477
|
+
txHash: ensureHexPrefix(config.depTxHash),
|
|
478
|
+
index: config.depIndex || 0,
|
|
479
|
+
},
|
|
480
|
+
depType: "depGroup",
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
const outputIndex = from ? from.outputs.length : 0;
|
|
484
|
+
|
|
485
|
+
return {tx: preTx, outputIndex};
|
|
486
|
+
}
|
|
487
|
+
|
|
344
488
|
/**
|
|
345
489
|
* Creates a transaction for appending content to a CKBFS v3 file
|
|
346
490
|
* @param signer The signer to use for the transaction
|
|
@@ -353,27 +497,86 @@ export async function createAppendV3Transaction(
|
|
|
353
497
|
): Promise<Transaction> {
|
|
354
498
|
const {
|
|
355
499
|
ckbfsCell,
|
|
356
|
-
contentChunks,
|
|
357
500
|
feeRate,
|
|
501
|
+
} = options;
|
|
502
|
+
const { lock } = ckbfsCell;
|
|
503
|
+
|
|
504
|
+
// Use prepareAppendV3Transaction to create the base transaction
|
|
505
|
+
const { tx: preTx, outputIndex } = await prepareAppendV3Transaction(options);
|
|
506
|
+
|
|
507
|
+
// Get the recommended address to ensure lock script cell deps are included
|
|
508
|
+
const address = await signer.getRecommendedAddressObj();
|
|
509
|
+
|
|
510
|
+
const inputsBefore = preTx.inputs.length;
|
|
511
|
+
// If we need more capacity than the original cell had, add additional inputs
|
|
512
|
+
if (preTx.outputs[outputIndex].capacity > ckbfsCell.capacity) {
|
|
513
|
+
console.log(
|
|
514
|
+
`Need additional capacity: ${preTx.outputs[outputIndex].capacity - ckbfsCell.capacity} shannons`,
|
|
515
|
+
);
|
|
516
|
+
// Add more inputs to cover the increased capacity
|
|
517
|
+
await preTx.completeInputsByCapacity(signer);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const witnesses: any = [];
|
|
521
|
+
// add empty witness for signer if ckbfs's lock is the same as signer's lock
|
|
522
|
+
if (address.script.hash() === lock.hash()) {
|
|
523
|
+
witnesses.push("0x");
|
|
524
|
+
}
|
|
525
|
+
// add ckbfs witnesses (skip the first witness which is for signing)
|
|
526
|
+
witnesses.push(...preTx.witnesses.slice(1));
|
|
527
|
+
|
|
528
|
+
// Add empty witnesses for additional signer inputs
|
|
529
|
+
// This is to ensure that the transaction is valid and can be signed
|
|
530
|
+
for (let i = inputsBefore; i < preTx.inputs.length; i++) {
|
|
531
|
+
witnesses.push("0x");
|
|
532
|
+
}
|
|
533
|
+
preTx.witnesses = witnesses;
|
|
534
|
+
|
|
535
|
+
// Complete fee
|
|
536
|
+
await preTx.completeFeeChangeToLock(signer, address.script, feeRate || 2000);
|
|
537
|
+
|
|
538
|
+
return preTx;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Creates a transaction for appending content to a CKBFS v3 file (dry run without fee completion)
|
|
543
|
+
* @param signer The signer to use for the transaction
|
|
544
|
+
* @param options Options for appending content
|
|
545
|
+
* @returns Promise resolving to the created transaction
|
|
546
|
+
*/
|
|
547
|
+
export async function createAppendV3TransactionDry(
|
|
548
|
+
signer: Signer,
|
|
549
|
+
options: AppendV3Options,
|
|
550
|
+
): Promise<Transaction> {
|
|
551
|
+
const {
|
|
552
|
+
ckbfsCell,
|
|
553
|
+
contentChunks,
|
|
358
554
|
network = DEFAULT_NETWORK,
|
|
359
555
|
previousTxHash,
|
|
360
556
|
previousWitnessIndex,
|
|
361
557
|
previousChecksum,
|
|
362
558
|
} = options;
|
|
559
|
+
const { lock } = ckbfsCell;
|
|
363
560
|
|
|
364
561
|
// Calculate new checksum by updating from previous checksum
|
|
365
562
|
const combinedContent = Buffer.concat(contentChunks);
|
|
366
563
|
const newChecksum = await updateChecksum(previousChecksum, combinedContent);
|
|
367
564
|
|
|
565
|
+
// Get the recommended address to ensure lock script cell deps are included
|
|
566
|
+
const address = await signer.getRecommendedAddressObj();
|
|
567
|
+
|
|
568
|
+
// Calculate the actual witness indices where our content is placed
|
|
569
|
+
// CKBFS data starts at index 1 if signer's lock script is the same as ckbfs's lock script
|
|
570
|
+
// else CKBFS data starts at index 0
|
|
571
|
+
const contentStartIndex = address.script.hash() === lock.hash() ? 1 : 0;
|
|
572
|
+
|
|
368
573
|
// Create CKBFS v3 witnesses with backlink info
|
|
369
574
|
const ckbfsWitnesses = createChunkedCKBFSV3Witnesses(contentChunks, {
|
|
370
575
|
previousTxHash,
|
|
371
576
|
previousWitnessIndex,
|
|
372
577
|
previousChecksum,
|
|
578
|
+
startIndex: contentStartIndex,
|
|
373
579
|
});
|
|
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
580
|
|
|
378
581
|
// Create updated CKBFS v3 cell output data
|
|
379
582
|
const outputData = CKBFSData.pack(
|
|
@@ -394,10 +597,14 @@ export async function createAppendV3Transaction(
|
|
|
394
597
|
BigInt(outputData.length + ckbfsCell.type.occupiedSize + ckbfsCell.lock.occupiedSize + 8) *
|
|
395
598
|
100000000n;
|
|
396
599
|
|
|
600
|
+
console.log(
|
|
601
|
+
`Original capacity: ${ckbfsCell.capacity}, Calculated size: ${ckbfsCellSize}, Data size: ${outputData.length}`,
|
|
602
|
+
);
|
|
603
|
+
|
|
397
604
|
// Use the maximum value between calculated size and original capacity
|
|
398
605
|
const outputCapacity = ckbfsCellSize > ckbfsCell.capacity ? ckbfsCellSize : ckbfsCell.capacity;
|
|
399
606
|
|
|
400
|
-
// Create transaction
|
|
607
|
+
// Create initial transaction with the CKBFS cell input
|
|
401
608
|
const tx = Transaction.from({
|
|
402
609
|
inputs: [
|
|
403
610
|
{
|
|
@@ -412,10 +619,6 @@ export async function createAppendV3Transaction(
|
|
|
412
619
|
capacity: outputCapacity,
|
|
413
620
|
},
|
|
414
621
|
],
|
|
415
|
-
witnesses: [
|
|
416
|
-
[], // Empty secp witness for signing
|
|
417
|
-
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
418
|
-
],
|
|
419
622
|
outputsData: [outputData],
|
|
420
623
|
});
|
|
421
624
|
|
|
@@ -428,11 +631,24 @@ export async function createAppendV3Transaction(
|
|
|
428
631
|
depType: "depGroup",
|
|
429
632
|
});
|
|
430
633
|
|
|
431
|
-
|
|
432
|
-
await tx.completeInputsByCapacity(signer);
|
|
634
|
+
const inputsBefore = tx.inputs.length;
|
|
433
635
|
|
|
434
|
-
|
|
435
|
-
|
|
636
|
+
const witnesses: any = [];
|
|
637
|
+
// add empty witness for signer if ckbfs's lock is the same as signer's lock
|
|
638
|
+
if (address.script.hash() === lock.hash()) {
|
|
639
|
+
witnesses.push("0x");
|
|
640
|
+
}
|
|
641
|
+
// add ckbfs witnesses
|
|
642
|
+
witnesses.push(
|
|
643
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
// Add empty witnesses for signer's input
|
|
647
|
+
// This is to ensure that the transaction is valid and can be signed
|
|
648
|
+
for (let i = inputsBefore; i < tx.inputs.length; i++) {
|
|
649
|
+
witnesses.push("0x");
|
|
650
|
+
}
|
|
651
|
+
tx.witnesses = witnesses;
|
|
436
652
|
|
|
437
653
|
return tx;
|
|
438
654
|
}
|