UNPKG

@ckbfs/api

Version:

SDK for CKBFS protocol on CKB

230 lines (192 loc) 8.24 kB
import { CKBFS, NetworkType, ProtocolVersion, CKBFSDataType, extractCKBFSWitnessContent, isCKBFSWitness, CKBFSData, AppendContentOptions } from '../src/index'; import { Script, ClientPublicTestnet, Transaction, ccc } from "@ckb-ccc/core"; // Replace with your actual private key const privateKey = process.env.CKB_PRIVATE_KEY || 'your-private-key-here'; // Parse command line arguments for transaction hash const txHashArg = process.argv.find(arg => arg.startsWith('--txhash=')); const publishTxHash = txHashArg ? txHashArg.split('=')[1] : process.env.PUBLISH_TX_HASH || '0x0000000000000000000000000000000000000000000000000000000000000000'; /** * Ensures a string is prefixed with '0x' * @param value The string to ensure is hex prefixed * @returns A hex prefixed string */ function ensureHexPrefix(value: string): `0x${string}` { if (value.startsWith('0x')) { return value as `0x${string}`; } return `0x${value}` as `0x${string}`; } // Initialize the SDK with network and version options const ckbfs = new CKBFS( privateKey, NetworkType.Testnet, // Use testnet { version: ProtocolVersion.V2, // Use the latest version (V2) chunkSize: 20 * 1024, // 20KB chunks useTypeID: false // Use code hash instead of type ID } ); // Initialize CKB client for testnet const client = new ClientPublicTestnet(); /** * Get cell information from a transaction using CKB client * @param txHash The transaction hash to get cell information from * @returns Promise resolving to the cell information */ async function getCellInfoFromTransaction(txHash: string): Promise<{ outPoint: { txHash: string; index: number }; type: Script; data: CKBFSDataType; lock: Script; capacity: bigint; }> { console.log(`Retrieving transaction data for: ${txHash}`); try { // Get transaction from RPC const txWithStatus = await client.getTransaction(txHash); if (!txWithStatus || !txWithStatus.transaction) { throw new Error(`Transaction ${txHash} not found`); } const tx = Transaction.from(txWithStatus.transaction); console.log(`Transaction found with ${tx.outputs.length} outputs`); // Find the CKBFS cell output (first output with type script) let ckbfsCellIndex = 0; const output = tx.outputs[ckbfsCellIndex]; if (!output || !output.type) { throw new Error('No CKBFS cell found in the transaction'); } console.log(`Found CKBFS cell at index ${ckbfsCellIndex}`); console.log(`Cell type script hash: ${output.type.hash()}`); // Get output data const outputData = tx.outputsData[ckbfsCellIndex]; if (!outputData) { throw new Error('Output data not found'); } // Parse the output data as CKBFS data // First remove 0x prefix if present const rawData = outputData.startsWith('0x') ? ccc.bytesFrom(outputData.slice(2), 'hex') : Buffer.from(outputData, 'hex'); // Actually unpack the raw data using CKBFSData.unpack // Use the same protocol version as configured in the SDK const version = ProtocolVersion.V2; // Use V2 as configured in SDK initialization console.log(`Using protocol version ${version} for unpacking cell data`); let ckbfsData: CKBFSDataType; try { ckbfsData = CKBFSData.unpack(rawData, version); // Log the actual cell data for transparency console.log('Successfully unpacked CKBFS cell data:'); console.log(`- Checksum: ${ckbfsData.checksum}`); console.log(`- File: ${ckbfsData.filename}`); console.log(`- Content Type: ${ckbfsData.contentType}`); console.log(`- Index: ${ckbfsData.index}`); console.log(`- Indexes: ${ckbfsData.indexes}`); console.log(`- Backlinks count: ${ckbfsData.backLinks?.length || 0}`); } catch (error) { console.error('Error unpacking CKBFS data:', error); throw new Error(`Failed to unpack CKBFS data: ${error}`); } return { outPoint: { txHash, index: ckbfsCellIndex }, type: output.type, lock: output.lock, capacity: output.capacity, data: ckbfsData }; } catch (error) { console.error('Error retrieving transaction data:', error); // If we can't get or parse the real data, we should fail - not use mock data throw new Error(`Failed to retrieve or parse cell data: ${error}`); } } /** * Example of appending content to an existing CKBFS file */ async function appendExample() { try { // Validate transaction hash if (publishTxHash === '0x0000000000000000000000000000000000000000000000000000000000000000') { throw new Error('Please provide a valid transaction hash by setting the PUBLISH_TX_HASH environment variable or using --txhash=0x... argument'); } // Get address info const address = await ckbfs.getAddress(); console.log(`Using address: ${address.toString()}`); // Get lock script const lock = await ckbfs.getLock(); // Get the cell information from the transaction console.log(`Getting cell info from transaction: ${publishTxHash}`); const ckbfsCell = await getCellInfoFromTransaction(publishTxHash); // Append content from a file const filePath = './append.txt'; console.log(`Appending file: ${filePath}`); // Create a sample append.txt file for the demonstration const fs = require('fs'); if (!fs.existsSync(filePath)) { fs.writeFileSync(filePath, 'This is content to append to the previously published file.'); console.log(`Created sample file: ${filePath}`); } console.log('Note: When appending data, the capacity of the cell may need to increase.'); console.log('The SDK will automatically handle this by adding additional inputs if needed.'); console.log('Make sure your account has enough CKB to cover the increased capacity and fees.'); const txHash = await ckbfs.appendFile(filePath, ckbfsCell); console.log(`File appended successfully!`); console.log(`Transaction Hash: ${txHash}`); console.log(`View at: https://pudge.explorer.nervos.org/transaction/${txHash}`); return txHash; } catch (error) { console.error('Error appending file:', error); throw error; } } /** * Example of appending content directly (string) to an existing CKBFS file * @param previousAppendTxHash The transaction hash from the previous append operation */ async function appendContentExample(previousAppendTxHash: string) { try { console.log(`Getting cell info from previous append transaction: ${previousAppendTxHash}`); const ckbfsCell = await getCellInfoFromTransaction(previousAppendTxHash); const contentToAppend = "\nAnd this is more content appended directly as a string!"; const options: AppendContentOptions = { // You can optionally specify feeRate, network, version // feeRate: 3000 }; console.log(`Appending direct content: "${contentToAppend}"`); // Append the string content const txHash = await ckbfs.appendContent(contentToAppend, ckbfsCell, options); console.log(`Direct content appended successfully!`); console.log(`Transaction Hash: ${txHash}`); console.log(`View at: https://pudge.explorer.nervos.org/transaction/${txHash}`); return txHash; } catch (error) { console.error('Error appending direct content:', error); throw error; } } /** * Main function to run the example */ async function main() { console.log('Running CKBFS append example...'); console.log('-------------------------------'); console.log(`Using CKBFS protocol version: ${ProtocolVersion.V2}`); try { // Run the file append first const firstAppendTxHash = await appendExample(); console.log('-------------------------------'); // Now run the content append, using the output from the first append await appendContentExample(firstAppendTxHash); console.log('Example completed successfully!'); process.exit(0); } catch (error) { console.error('Example failed:', error); process.exit(1); } } // Run the example if this file is executed directly if (require.main === module) { main().catch(console.error); }