TITLE: Calculate Change and Add Change Cell to CKB Transaction DESCRIPTION: This code recalculates the total input capacity after adding new cells and then creates a change cell. The change cell accounts for any remaining CKBytes after covering output capacities and transaction fees, ensuring the transaction balances. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/untitled-1.md#_snippet_7 LANGUAGE: javascript CODE: ``` // Recalculate new input capacity. inputCapacity = transaction.inputs.toArray().reduce((a, c)=>a+hexToInt(c.cell_output.capacity), 0n); // Create a change Cell for the remaining CKBytes. const changeCapacity = intToHex(inputCapacity - outputCapacity - txFee); const change = {cell_output: {capacity: changeCapacity, lock: addressToScript(address1), type: null}, data: "0x"}; transaction = transaction.update("outputs", (i)=>i.push(change)); ``` ---------------------------------------- TITLE: Rust SUDT Script Owner Mode Check DESCRIPTION: The `check_owner_mode` function determines if the script is being run in owner mode. It first validates that the length of the script arguments matches `LOCK_HASH_LEN`. Then, it queries all input cells' lock hashes, comparing them against the lock hash provided in the script arguments. If any input cell's lock hash matches, owner mode is enabled. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/creating-a-token.md#_snippet_3 LANGUAGE: rust CODE: ``` /// Determine if owner mode is enabled. fn check_owner_mode(args: &Bytes) -> Result { // Verify the the arguments length matches the length of a Blake2b hash. if args.len() != LOCK_HASH_LEN { return Err(Error::ArgsLength); } // Compare the Lock Script Hash from the script args with the Lock Scripts // of each input cell to determine if a match exists. let is_owner_mode = QueryIter::new(load_cell_lock_hash, Source::Input) .find(|lock_hash|args[..]==lock_hash[..]).is_some(); // Return the owner mode status. Ok(is_owner_mode) } ``` ---------------------------------------- TITLE: List Accounts in CKB-CLI DESCRIPTION: Use the `account list` command in `ckb-cli` to display all accounts managed by the tool. This is crucial for verifying the special dev blockchain accounts, which are pre-funded with a large amount of CKBytes for development purposes. The output also introduces Nervos CKB addresses, which are unique identifiers for transaction sources or destinations. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/sending-a-transaction.md#_snippet_0 LANGUAGE: Shell CODE: ``` account list ``` ---------------------------------------- TITLE: Manage Transaction Inputs, Outputs, and Change Cells (JavaScript) DESCRIPTION: This comprehensive snippet demonstrates how to manage inputs and outputs for a CKB transaction. It adds the 'always success' cell as an input, collects additional capacity cells to meet requirements, calculates total input and output capacities, and finally creates a change cell to return remaining CKBytes to the sender's default lock script. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/using-scripts.md#_snippet_10 LANGUAGE: javascript CODE: ``` // Add the always success cell to the transaction. const input = await getLiveCell(nodeUrl, alwaysSuccessCellOutPoint); transaction = transaction.update("inputs", (i)=>i.push(input)); // Add input capacity cells. const capacityRequired = ckbytesToShannons(61n) + txFee; const collectedCells = await collectCapacity(indexer, addressToScript(address1), capacityRequired); transaction = transaction.update("inputs", (i)=>i.concat(collectedCells.inputCells)); // Determine the capacity from all input Cells. const inputCapacity = transaction.inputs.toArray().reduce((a, c)=>a+hexToInt(c.cellOutput.capacity), 0n); const outputCapacity = transaction.outputs.toArray().reduce((a, c)=>a+hexToInt(c.cellOutput.capacity), 0n); // Create a change Cell for the remaining CKBytes. const changeCapacity = intToHex(inputCapacity - outputCapacity - txFee); let change = {cellOutput: {capacity: changeCapacity, lock: addressToScript(address1), type: null}, data: "0x"}; transaction = transaction.update("outputs", (i)=>i.push(change)); ``` ---------------------------------------- TITLE: Validate JSON Cell Data in Rust DESCRIPTION: This Rust snippet demonstrates the core logic for a CKB JSON Cell type script. It iterates through output cells, loads their data using `load_cell_data` and `QueryIter` from `ckb_std`, converts it to a UTF-8 string, and then validates the string's format as JSON using the `lite_json` library. The script operates in `no_std` mode and handles potential errors during string decoding or JSON parsing. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/script-groups.md#_snippet_1 LANGUAGE: rust CODE: ``` // Import from `core` instead of from `std` since we are in no-std mode. use core::result::Result; use core::str; // Import CKB syscalls and structures. // https://nervosnetwork.github.io/ckb-std/riscv64imac-unknown-none-elf/doc/ckb_std/index.html use ckb_std::ckb_constants::Source; use ckb_std::high_level::{load_cell_data, QueryIter}; // Import the lite-json library for JSON parsing/validation. use lite_json::json_parser::parse_json; // Import our local error codes. use crate::error::Error; // Main entry point. pub fn main() -> Result<(), Error> { // Load the cell data from each cell. for data in QueryIter::new(load_cell_data, Source::GroupOutput) { // Parse the cell data into a UTF-8 string. let json_str = str::from_utf8(&data).map_err(|_|Error::InvalidStringData)?; // Validate the string as JSON by parsing it. parse_json(json_str).map_err(|_|Error::InvalidJson)?; } Ok(()) } ``` ---------------------------------------- TITLE: JavaScript: Convert Address to CKB Lock Script DESCRIPTION: This JavaScript snippet demonstrates the usage of the `addressToScript()` function. It converts a human-readable CKB address into a structured lock script object, which is essential for defining cell ownership in CKB transactions. The function generates the necessary `codeHash`, `hashType`, and `args` for the script. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/using-scripts.md#_snippet_0 LANGUAGE: javascript CODE: ``` let lockScript = addressToScript("ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqvc32wruaxqnk4hdj8yr4yp5u056dkhwtc94sy8q"); ``` ---------------------------------------- TITLE: Sign CKB Transaction (JavaScript) DESCRIPTION: This snippet signs the transaction using a provided private key and the Secp256k1 algorithm. Signing is crucial as it authorizes the use of input cells owned by that private key, and the signTransaction() function simplifies this cryptographic operation. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/introduction-to-lumos.md#_snippet_9 LANGUAGE: javascript CODE: ``` // Sign the transaction. const signedTx = signTransaction(transaction, PRIVATE_KEY); ``` ---------------------------------------- TITLE: Lumos Transaction for Updating Cell Data DESCRIPTION: This comprehensive Lumos JavaScript snippet demonstrates the process of 'updating' cell data. It first locates an existing cell with specific data to be consumed as an input. Then, it calculates the total input capacity and constructs a new output cell with updated data, recycling the capacity from the consumed input, effectively replacing the old cell. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/updating-data-in-a-cell.md#_snippet_3 LANGUAGE: javascript CODE: ``` // Locate a single live cell with the desired data and add it as an input. const {hexString: hexString1} = await readFileToHexString(dataFile1); const query = {lock: addressToScript(address1), type: null, data: hexString1}; const cellCollector = new CellCollector(indexer, query); for await (const cell of cellCollector.collect()) { transaction = addInput(transaction, cell); break; } if(transaction.inputs.size === 0) throw new Error("Unable to locate a live cell with the expected data."); // Calculate the total capacity of all inputs. const inputCapacity = transaction.inputs.toArray().reduce((a, c)=>a+hexToInt(c.cellOutput.capacity), 0n); // Create a cell with data from the specified file. const {hexString: hexString2} = await readFileToHexString(dataFile2); const outputCapacity1 = intToHex(inputCapacity - txFee); const output1 = {cellOutput: {capacity: outputCapacity1, lock: addressToScript(address1), type: null}, data: hexString2}; transaction = addOutput(transaction, output1); ``` ---------------------------------------- TITLE: Rust Function: Count Tokens in Transaction Source DESCRIPTION: The `determine_token_amount` function in Rust calculates the total tokens from a specified source (either `GroupInput` or `GroupOutput`). It iterates through cell data, ensuring each entry is at least 16 bytes (the size of a `u128`), converts the first 16 bytes to a `u128` amount, and sums them. Returns an `Error::Encoding` if data is less than 16 bytes. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/creating-a-token.md#_snippet_4 LANGUAGE: Rust CODE: ``` /// Count the number of tokens in the specified source. Source should be either GroupInput or GroupOutput. fn determine_token_amount(source: Source) -> Result { // Track the number of tokens that are counted. let mut total_token_amount = 0; // Cycle through the data in each cell within the specified source. let cell_data = QueryIter::new(load_cell_data, source); for data in cell_data { // Check that the length of the data is >= 16 bytes, the size of a u128. if data.len() >= SUDT_DATA_LEN { // Convert the binary data in the cell to a u128 value. let mut buffer = [0u8; SUDT_DATA_LEN]; buffer.copy_from_slice(&data[0..SUDT_DATA_LEN]); let amount = u128::from_le_bytes(buffer); // Add the amount of tokens in the cell to the total amount of tokens. total_token_amount += amount; } // If the data is less than 16 bytes, then return an encoding error. else { return Err(Error::Encoding); } } // Return the total amount of tokens found in the specified source. Ok(total_token_amount) } ``` ---------------------------------------- TITLE: Transfer CKBytes using CKB-CLI Wallet DESCRIPTION: Execute a CKByte transfer between two accounts using the `wallet transfer` command in `ckb-cli`. This command requires specifying the source account (`--from-account`), the destination address (`--to-address`), and the amount of CKBytes to send (`--capacity`). After execution, the tool prompts for a password and returns a transaction ID upon success. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/sending-a-transaction.md#_snippet_1 LANGUAGE: Shell CODE: ``` wallet transfer --from-account ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwgx292hnvmn68xf779vmzrshpmm6epn4c0cgwga --to-address ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq28phxutezqvjgfv5q38gn5kwek4m9km3cmajeqs --capacity 1000 ``` ---------------------------------------- TITLE: Creating Counter Cells in Lumos DESCRIPTION: This JavaScript code snippet demonstrates how to construct an output cell for a CKB transaction using the Counter type script within a Lumos application. It sets the cell's capacity to the minimum required 102 CKBytes, defines the lock and type scripts, and initializes the counter's data value, ensuring it's properly formatted as a 64-bit little-endian hex string as required by the Counter type script. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/managing-script-state.md#_snippet_2 LANGUAGE: javascript CODE: ``` // Create a cell using the Counter type script. const outputCapacity1 = ckbytesToShannons(102n); const lockScript1 = addressToScript(address1); const typeScript1 = { codeHash: dataFileHash1, hashType: "data", args: "0x" }; const dataValue1 = 9_000n; const data1 = intToU64LeHexBytes(dataValue1); const output1 = {cellOutput: {capacity: intToHex(outputCapacity1), lock: lockScript1, type: typeScript1}, data: data1}; transaction = transaction.update("outputs", (i)=>i.push(output1)); ``` ---------------------------------------- TITLE: Adding Updated Counter Cell as Transaction Output DESCRIPTION: This snippet adds a new Counter cell to the transaction's outputs. It sets the cell's capacity, lock script, and type script. The data value for the new cell is incremented by one from the input Counter cell's value, demonstrating the 'update' by creating a new cell with modified data. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/managing-script-state.md#_snippet_4 LANGUAGE: javascript CODE: ``` // Add the updated Counter cell to the transaction. const outputCapacity1 = ckbytesToShannons(102n); const lockScript1 = addressToScript(address1); const typeScript1 = { codeHash: dataFileHash1, hashType: "data", args: "0x" }; const dataValue1 = counterValue + 1n; const data1 = intToU64LeHexBytes(dataValue1); const output1 = {cellOutput: {capacity: intToHex(outputCapacity1), lock: lockScript1, type: typeScript1}, data: data1}; transaction = transaction.update("outputs", (i)=>i.push(output1)); ``` ---------------------------------------- TITLE: Adding Counter Cell as Transaction Input DESCRIPTION: This code adds an existing Counter cell as an input to a transaction using its out point. It retrieves the cell's data and decodes it from hex to BigInt, which will be used for the output cell. The `getLiveCell` function's third parameter `true` ensures data is returned with the cell, optimizing system calls. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/managing-script-state.md#_snippet_3 LANGUAGE: javascript CODE: ``` // Add the Counter cell to the transaction. const input = await getLiveCell(nodeUrl, counterCellOutPoint, true); const counterValue = u64LeHexBytesToInt(input.data); transaction = transaction.update("inputs", (i)=>i.push(input)); ``` ---------------------------------------- TITLE: Pseudo-code for DataCap Type Script Data Size Validation DESCRIPTION: This pseudo-code outlines the fundamental logic for a type script that enforces a maximum data size on output cells. It demonstrates loading a size limit from arguments, iterating through output cells, and returning an error if any cell's data exceeds the specified limit. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/using-script-args.md#_snippet_0 LANGUAGE: javascript CODE: ``` function main() { max_data_size = integer_from_binary(args); outputGroup = load_output_group(); for cell in outputGroup { if(cell.data.length() > max_data_size) { return 1; } } return 0; } ``` ---------------------------------------- TITLE: Add Output Cell to CKB Transaction (JavaScript) DESCRIPTION: This code creates an output cell, typically a change cell, by calculating its capacity from the input's capacity minus a transaction fee. It sets the cell's lock to define its owner using an address converted to a script structure via addressToScript(), and then adds this output to the transaction. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/introduction-to-lumos.md#_snippet_6 LANGUAGE: javascript CODE: ``` // Add an output cell. const outputCapacity = intToHex(hexToInt(input.cellOutput.capacity) - TX_FEE); const output = {cellOutput: {capacity: outputCapacity, lock: addressToScript(ADDRESS), type: null}, data: "0x"}; transaction = transaction.update("outputs", (i)=>i.push(output)); ``` ---------------------------------------- TITLE: Sign Transaction with JavaScript DESCRIPTION: This snippet demonstrates how to sign a transaction using a private key. While 'always success' locks do not require signing, this step is crucial when additional input cells with default locks (e.g., from address1) are included, as they mandate a standard signature for unlocking. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/using-scripts.md#_snippet_11 LANGUAGE: javascript CODE: ``` // Sign the transaction. const signedTx = signTransaction(transaction, privateKey1); ``` ---------------------------------------- TITLE: JavaScript: Always Success CKB Script Logic DESCRIPTION: This JavaScript pseudo-code illustrates the 'always success' script, a fundamental concept for CKB script development and testing. The script's sole purpose is to return `0`, signaling successful execution regardless of input. This simplifies testing by bypassing complex lock script requirements, allowing developers to focus on other script logic. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/using-scripts.md#_snippet_2 LANGUAGE: javascript CODE: ``` function script() { return 0; } ``` ---------------------------------------- TITLE: CKB Script Hash Type Definitions DESCRIPTION: Defines the `hashType` values used in CKB lock and type scripts to interpret the `codeHash`, specifying how the script code should be matched and which CKB-VM version to use. These types determine whether the `codeHash` refers to the binary executable's data hash or a type script's hash, enabling different execution behaviors and upgradeability. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/using-scripts.md#_snippet_6 LANGUAGE: APIDOC CODE: ``` Hash Type: data Matching: Match code by data hash. CKB-VM Version: 0 Hash Type: type Matching: Match code by type hash. CKB-VM Version: 1 (always newest) Hash Type: data1 Matching: Match code by data hash. CKB-VM Version: 1 ``` ---------------------------------------- TITLE: Troubleshoot 'Live Cell Not Found' Error in CKB Transactions DESCRIPTION: This error occurs when attempting to reuse an `out_point` that has already been consumed in a previous transaction. A live cell, once used as an input, becomes a dead cell and cannot be spent again. This snippet demonstrates the error output and explains its underlying cause. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/introduction-to-lumos.md#_snippet_15 LANGUAGE: javascript CODE: ``` UnhandledPromiseRejectionWarning: Error: Live cell not found at out point: 0x3a52afb04b91097c84ca287ce58f98c1a454a3aa53497fbdd0ad6cba4b66f43b-0x0 ``` ---------------------------------------- TITLE: Pseudo-code for Counter Type Script Logic DESCRIPTION: This pseudo-code outlines the core logic for a counter type script. It validates that a counter value is incremented by exactly one, checks for valid input/output cell counts (expecting one input and one output), and handles the initial creation of a counter cell where no input exists. It returns 0 for success and 1 for errors. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/managing-script-state.md#_snippet_0 LANGUAGE: javascript CODE: ``` function main() { group_input_count = load_input_group().length(); group_output_count = load_output_group().length(); if(group_input_count == 0) return 0; if(group_input_count != 1 || group_output_count != 1) return 1; input_value = integer_from_binary(load_input_group().get_first_cell().data); output_value = integer_from_binary(load_output_group().get_first_cell().data); if(input_value + 1 != output_value) return 1; return 0; } ``` ---------------------------------------- TITLE: Rust SUDT Script Main Entry Point DESCRIPTION: The `main` function serves as the primary entry point for the SUDT script. It loads script arguments, checks if owner mode is enabled (exiting early if true), calculates input and output token amounts, and enforces the token scarcity rule (`input_token_amount >= output_token_amount`). The script returns success if all validations pass, or an error otherwise. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/creating-a-token.md#_snippet_2 LANGUAGE: rust CODE: ``` // Main entry point. pub fn main() -> Result<(), Error> { // Load the currently executing script and get the args. let script = load_script()?; let args: Bytes = script.args().unpack(); // Check if the script is being run by the owner and immediately return success if true. if check_owner_mode(&args)? { return Ok(()); } // Count the number of tokens in the GroupInput and GroupOutput. let input_token_amount = determine_token_amount(Source::GroupInput)?; let output_token_amount = determine_token_amount(Source::GroupOutput)?; // If the amount of input tokens is less than the amount of output tokens, return an error. if input_token_amount < output_token_amount { return Err(Error::Amount); } // No errors were found during validation. Return success. Ok(()) } ``` ---------------------------------------- TITLE: Rust: Validate Transfer/Update Operation for Counter Cell DESCRIPTION: This function validates a 'transfer' or 'update' operation for a counter cell. It loads both input and output cell data, verifies their lengths (8 bytes for u64), converts them to `u64` values, checks for potential overflow, and ensures the output value is exactly one greater than the input value. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/operation-detection.md#_snippet_7 LANGUAGE: Rust CODE: ``` // Validate a transaction to transfer (update) a counter cell and increase its value. fn validate_transfer() -> Result<(), Error> { // Load the input cell data and verify that the length is exactly 8, which is the length of a u64. let input_data = load_cell_data(0, Source::GroupInput)?; if input_data.len() != 8 { return Err(Error::InvalidInputCellData); } // Load the output cell data and verify that the length is exactly 8, which is the length of a u64. let output_data = load_cell_data(0, Source::GroupOutput)?; if output_data.len() != 8 { return Err(Error::InvalidOutputCellData); } // Create a buffer to use for parsing cell data into integers. let mut buffer = [0u8; 8]; // Convert the input cell data to a u64 value. buffer.copy_from_slice(&input_data[0..8]); let input_value = u64::from_le_bytes(buffer); // Convert the output cell data to a u64 value. buffer.copy_from_slice(&output_data[0..8]); let output_value = u64::from_le_bytes(buffer); // Check for an overflow scenario. if input_value == u64::MAX { return Err(Error::CounterValueOverflow); } // Ensure that the output value is always exactly one more that in the input value. if input_value + 1 != output_value { return Err(Error::InvalidCounterValue); } Ok(()) } ``` ---------------------------------------- TITLE: JavaScript: Lumos `collectCapacity` Function DESCRIPTION: The `collectCapacity` function is designed to automatically collect cells for use as transaction capacity from a specified lock script. It queries a Lumos Indexer instance to find live cells with sufficient capacity. The function takes an `indexer` instance, a `lockScript` (representing the cell owner), and `capacityRequired` (the amount of CKBytes in Shannons needed). It iterates through collected cells, accumulating capacity until the requirement is met or throwing an error if insufficient capacity is found. The function returns an object containing the `inputCells` array and the total `inputCapacity`. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/cell-management.md#_snippet_2 LANGUAGE: javascript CODE: ``` /** * Collects cells for use as capacity from the specified lock script. * * This will search for cells with at least capacityRequired. If there is insufficient capacity available an error will be thrown. * * @example * const {inputCells, inputCapacity} = await collectCapacity(indexer, addressToScript("ckt1qyqvsv5240xeh85wvnau2eky8pwrhh4jr8ts8vyj37"), ckbytesToShannons(100n)); * * @param {Object} indexer An instance of a running Lumos Indexer. * @param {Object} lockScript A script used to query the CellCollector to find cells to use as capacity. * @param {BigInt} capacityRequired The number of CKBytes necessary. * * @returns {Object} An object with the inputCells[] found and the inputCapacity contained within the provided Cells. */ async function collectCapacity(indexer, lockScript, capacityRequired) { const query = {lock: lockScript, type: null}; const cellCollector = new CellCollector(indexer, query); let inputCells = []; let inputCapacity = 0n; for await (const cell of cellCollector.collect()) { inputCells.push(cell); inputCapacity += hexToInt(cell.cell_output.capacity); if(inputCapacity >= capacityRequired) break; } if(inputCapacity < capacityRequired) throw new Error("Unable to collect enough cells to fulfill the capacity requirements."); return {inputCells, inputCapacity}; } ``` ---------------------------------------- TITLE: Output Capacity Check (OCC) Lock Pseudo-code DESCRIPTION: This pseudo-code defines the 'Output Capacity Check Lock' (OCC Lock), which requires two arguments: `capacity_required` and `count_required`. The lock unlocks only if at least `count_required` output cells match the `capacity_required`. It demonstrates loading and deserializing arguments, iterating through output cells, and conditional unlocking based on capacity checks. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/untitled-1.md#_snippet_1 LANGUAGE: javascript CODE: ``` function main() { lock_args = load_lock_args().deserializeArray(); capacity_required = integer_from_binary(lock_args[0]); count_required = integer_from_binary(lock_args[1]); matches_found = 0; output_cells = load_output_cells(); for(cell in output_cells) { if(cell.capacity == capacity_required) { matches_found = matches_found + 1; if(matches_found >= count_required) { return 0; } } } return 1; } ``` ---------------------------------------- TITLE: Pseudo-code for Data10 Type Script Validation DESCRIPTION: This pseudo-code outlines the core logic for a script that validates the data length of output cells. It iterates through transaction outputs, identifies cells matching the current script's type, and returns an error if any matching cell's data exceeds 10 bytes. This demonstrates the minimal concern pattern, focusing only on relevant cells. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/accessing-cell-data.md#_snippet_0 LANGUAGE: javascript CODE: ``` function main() { current_script = load_current_script(); outputs = load_outputs(); for cell in outputs { if(cell.type == current_script) { if(cell.data.length() > 10) { return 1; } } } return 0; } ``` ---------------------------------------- TITLE: Creating Cells with DataCap Type Script in Lumos DESCRIPTION: This JavaScript code demonstrates how to create multiple cells using the DataCap type script within a Lumos transaction. It iterates through a predefined list of messages, converting each into a hex string and defining a type script with a fixed 20-byte data capacity limit. The resulting cells, each containing different data, are then added to the transaction's outputs, preparing them for creation on the CKB blockchain. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/using-script-args.md#_snippet_2 LANGUAGE: javascript CODE: ``` // Create cells using the DataCap type script. const messages = ["Hello World!", "Foo Bar", "1234567890"]; for(let message of messages) { const outputCapacity1 = ckbytesToShannons(500n); const lockScript1 = addressToScript(address1); const dataCapSize1 = intToU32LeHexBytes(20); const typeScript1 = { codeHash: dataFileHash1, hashType: "data", args: dataCapSize1 }; const data1 = stringToHex(message); const output1 = {cellOutput: {capacity: intToHex(outputCapacity1), lock: lockScript1, type: typeScript1}, data: data1}; transaction = transaction.update("outputs", (i)=>i.push(output1)); } ``` ---------------------------------------- TITLE: Rust Smart Contract Entry Point Logic (`entry.rs`) DESCRIPTION: This Rust code defines the main logic for a smart contract. It demonstrates how to import necessary libraries, load script arguments and transaction hash, and handle custom errors. The example checks if arguments are supplied and returns an error if they are empty, showcasing basic contract interaction and error handling. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/introduction-to-capsule.md#_snippet_7 LANGUAGE: Rust CODE: ``` // Import from `core` instead of from `std` since we are in no-std mode use core::result::Result; // Import heap related library from `alloc` // https://doc.rust-lang.org/alloc/index.html use alloc::{vec, vec::Vec}; // Import CKB syscalls and structures // https://docs.rs/ckb-std/ use ckb_std::{ debug, high_level::{load_script, load_tx_hash}, ckb_types::{bytes::Bytes, prelude::*}, }; use crate::error::Error; pub fn main() -> Result<(), Error> { // remove below examples and write your code here let script = load_script()?; let args: Bytes = script.args().unpack(); debug!("script args is {:?}", args); // return an error if args is invalid if args.is_empty() { return Err(Error::MyError); } let tx_hash = load_tx_hash()?; debug!("tx hash is {:?}", tx_hash); let _buf: Vec<_> = vec![0u8; 32]; Ok(()) } ``` ---------------------------------------- TITLE: Rust Implementation of Counter Type Script DESCRIPTION: This Rust code provides the real-world implementation of the counter type script. It mirrors the pseudo-code logic, validating that a counter value is incremented by exactly one. It uses CKB syscalls to load cell data, checks input/output cell counts, and handles initial counter creation. Errors are returned via a custom `Error` enum. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/managing-script-state.md#_snippet_1 LANGUAGE: rust CODE: ``` // Import from `core` instead of from `std` since we are in no-std mode. use core::result::Result; // Import CKB syscalls and structures // https://nervosnetwork.github.io/ckb-std/riscv64imac-unknown-none-elf/doc/ckb_std/index.html use ckb_std::ckb_constants::Source; use ckb_std::high_level::{load_cell, load_cell_data, QueryIter}; // Import local modules. use crate::error::Error; // Main entry point. pub fn main() -> Result<(), Error> { // Count on the number of group input and groupt output cells. let group_input_count = QueryIter::new(load_cell, Source::GroupInput).count(); let group_output_count = QueryIter::new(load_cell, Source::GroupOutput).count(); // If there are no inputs, skip validation. if group_input_count == 0 { return Ok(()); } // If there isn't an exact 1 to 1 count, give an error. if group_input_count != 1 || group_output_count != 1 { return Err(Error::InvalidTransactionStructure); } // Load the input cell data and convert the data into a u64 value. let input_data = load_cell_data(0, Source::GroupInput)?; let mut buffer = [0u8; 8]; buffer.copy_from_slice(&input_data[0..8]); let input_value = u64::from_le_bytes(buffer); // Load the output cell data and convert the data into a u64 value. let output_data = load_cell_data(0, Source::GroupOutput)?; let mut buffer = [0u8; 8]; buffer.copy_from_slice(&output_data[0..8]); let output_value = u64::from_le_bytes(buffer); // Ensure that the output value is always exactly one more that in the input value. if input_value + 1 != output_value { return Err(Error::InvalidCounterValue); } Ok(()) } ``` ---------------------------------------- TITLE: Rust: Main Entry Point for Transaction Mode Detection DESCRIPTION: This `main` function serves as the primary entry point for the application, determining the transaction mode (Burn, Create, Transfer) and delegating to specific validation functions or returning immediately for burn operations and errors. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/operation-detection.md#_snippet_5 LANGUAGE: Rust CODE: ``` // Main entry point. pub fn main() -> Result<(), Error> { // Determine the mode and validate as needed. match determine_mode() { Ok(Mode::Burn) => return Ok(()), Ok(Mode::Create) => validate_create()?, Ok(Mode::Transfer) => validate_transfer()?, Err(e) => return Err(e), } Ok(()) } ``` ---------------------------------------- TITLE: Rust ODCounter Determine Operation Mode DESCRIPTION: This Rust function determines the ODCounter script's operational mode (Burn, Create, or Transfer) by analyzing the count of group input and output cells. It queries CKB cells to count inputs and outputs, then applies logic to detect the operation. If no known code structure is used, it returns an `InvalidTransactionStructure` error. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/operation-detection.md#_snippet_4 LANGUAGE: rust CODE: ``` // Determines the mode of operation for the currently executing script. fn determine_mode() -> Result { // Gather counts on the number of group input and group output cells. let group_input_count = QueryIter::new(load_cell, Source::GroupInput).count(); let group_output_count = QueryIter::new(load_cell, Source::GroupOutput).count(); // Detect the operation based on the cell count. if group_input_count == 1 && group_output_count == 0 { return Ok(Mode::Burn); } if group_input_count == 0 && group_output_count == 1 { return Ok(Mode::Create); } if group_input_count == 1 && group_output_count == 1 { return Ok(Mode::Transfer); } // If no known code structure was used, return an error. Err(Error::InvalidTransactionStructure) } ``` ---------------------------------------- TITLE: Rust Smart Contract Error Definitions (`error.rs`) DESCRIPTION: This Rust code defines custom error types for a smart contract, including standard CKB node errors and a custom `MyError`. It implements the `From` trait to map system errors to the contract's custom error types, providing a structured way to handle and debug transaction failures. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/introduction-to-capsule.md#_snippet_8 LANGUAGE: Rust CODE: ``` use ckb_std::error::SysError; /// Error #[repr(i8)] pub enum Error { IndexOutOfBound = 1, ItemMissing, LengthNotEnough, Encoding, // Add customized errors here... MyError, } impl From for Error { fn from(err: SysError) -> Self { use SysError::*; match err { IndexOutOfBound => Self::IndexOutOfBound, ItemMissing => Self::ItemMissing, LengthNotEnough(_) => Self::LengthNotEnough, Encoding => Self::Encoding, Unknown(err_code) => panic!("unexpected sys error {}", err_code), } } } ``` ---------------------------------------- TITLE: Adjust CKB Transaction Capacity by Adding More Inputs DESCRIPTION: This snippet calculates the current input and output capacities of the transaction and then adds additional capacity to balance the transaction. It uses `collectCapacity()` to find and include more cells from `address1` to cover the deficit and transaction fees. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/untitled-1.md#_snippet_6 LANGUAGE: javascript CODE: ``` // Determine the capacity from input and output cells. let inputCapacity = transaction.inputs.toArray().reduce((a, c)=>a+hexToInt(c.cell_output.capacity), 0n); let outputCapacity = transaction.outputs.toArray().reduce((a, c)=>a+hexToInt(c.cell_output.capacity), 0n); // Add capacity to the transaction. const capacityRequired2 = outputCapacity - inputCapacity + ckbytesToShannons(61n) + txFee; const {inputCells} = await collectCapacity(indexer, addressToScript(address1), capacityRequired2); transaction = transaction.update("inputs", (i)=>i.concat(inputCells)); ``` ---------------------------------------- TITLE: Aggregatable Counter Script Pseudo-code (JavaScript) DESCRIPTION: This pseudo-code outlines the updated Aggregatable Counter script, designed to handle multiple input and output cells. It ensures a 1:1 correspondence between inputs and outputs, iterating through each pair to verify that the output value is exactly one greater than its corresponding input value. The script allows for new cell creation if no inputs are present. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/creating-aggregatable-scripts.md#_snippet_1 LANGUAGE: javascript CODE: ``` function main() { group_input_count = load_input_group().length(); group_output_count = load_output_group().length(); if(group_input_count == 0) return 0; if(group_input_count != group_output_count) return 1; for(i = 0; i < group_input_count; i++) { input_value = integer_from_binary(load_input_group_data(i)); output_value = integer_from_binary(load_output_group_data(i)); if(input_value + 1 == output_value) return 1; } return 0; } ``` ---------------------------------------- TITLE: Add Witnesses and Sign CKB Transaction DESCRIPTION: This final step adds necessary witness placeholders to the transaction and then signs it using the provided private key. Signatures are required because additional input cells using the default lock script were added, even though the OCC Lock itself doesn't require them. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/untitled-1.md#_snippet_8 LANGUAGE: javascript CODE: ``` // Add in the witness placeholders. transaction = addDefaultWitnessPlaceholders(transaction); // Sign the transaction. const signedTx = signTransaction(transaction, privateKey1); ``` ---------------------------------------- TITLE: Send Signed CKB Transaction to Node (JavaScript) DESCRIPTION: This code sends the fully signed transaction to the local CKB Dev Blockchain node via RPC. It then prints the transaction hash (TXID) to the console, indicating successful submission to the network for processing. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/introduction-to-lumos.md#_snippet_10 LANGUAGE: javascript CODE: ``` // Send the transaction to the RPC node. const txid = await sendTransaction(NODE_URL, signedTx); console.log("Transaction Sent:", result); ``` ---------------------------------------- TITLE: CKB-VM Syscall & CKB-STD High-Level: load_cell() DESCRIPTION: Loads information about a cell. This function is available as both a high-level CKB-STD function for Rust and a direct CKB-VM syscall. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/syscalls.md#_snippet_3 LANGUAGE: APIDOC CODE: ``` Function: load_cell CKB-STD High-Level: load_cell() CKB-STD Syscall: load_cell() Syscall RFC: ckb_load_cell() ``` ---------------------------------------- TITLE: Verify CKB Indexer Functionality with Lumos (JavaScript) DESCRIPTION: This Lumos JavaScript code snippet demonstrates how to verify that the CKB Indexer functionality is enabled and running correctly. It initializes the Lumos configuration, creates an instance of the `Indexer` class pointing to the CKB node's RPC URL, and then calls `indexer.tip()` to retrieve and display the most recent block tip, confirming successful communication with the Indexer. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/cell-management.md#_snippet_1 LANGUAGE: javascript CODE: ``` import fs from "fs"; import {initializeConfig} from "@ckb-lumos/config-manager"; import {Indexer} from "@ckb-lumos/ckb-indexer"; const CONFIG = JSON.parse(fs.readFileSync("../config.json")); const NODE_URL = "http://127.0.0.1:8114/"; const INDEXER_URL = "http://127.00.1:8114/"; initializeConfig(CONFIG); const indexer = new Indexer(INDEXER_URL, NODE_URL); (async function() { console.log(await indexer.tip()); })(); ``` ---------------------------------------- TITLE: Creating CKB Cells with Hash Lock DESCRIPTION: This code demonstrates how to create CKB cells using a Hash Lock. It sets up the lock script by including a Blake2b hash of the secret preimage in the `args` field. Two cells are created with this hash lock, making the hash visible on-chain while keeping the preimage secret and required for unlocking. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/using-the-witness.md#_snippet_3 LANGUAGE: javascript CODE: ``` const outputCapacity1 = ckbytesToShannons(500n); const lockScript1 = { code_hash: dataFileHash1, hash_type: "data", args: blake2b(32).update(preimage).digest("hex") }; const output1 = {cell_output: {capacity: intToHex(outputCapacity1), lock: lockScript1, type: null}, data: "0x"}; transaction = transaction.update("outputs", (i)=>i.concat([output1, output1])); ``` ---------------------------------------- TITLE: Rust Validation: Enforce Input Token Greater Than Output DESCRIPTION: This Rust code performs a crucial security check, ensuring that the total `input_token_amount` is not less than the `output_token_amount`. If an imbalance is detected, indicating potential unauthorized token creation or loss, an `Error::Amount` is returned, preventing the transaction from succeeding. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/creating-a-token.md#_snippet_6 LANGUAGE: Rust CODE: ``` // If the amount of input tokens is less than the amount of output tokens, return an error. if input_token_amount < output_token_amount { return Err(Error::Amount); } ``` ---------------------------------------- TITLE: Add Default Witness Placeholders to Transaction DESCRIPTION: This JavaScript code snippet adds placeholder entries to the transaction's witness structure. These placeholders are crucial for aligning signature data with the default lock script, ensuring compatibility and proper indexing within the transaction's witness array, especially when multiple inputs use the same lock script. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/using-the-witness.md#_snippet_0 LANGUAGE: javascript CODE: ``` // Add in the witness placeholders. transaction = addDefaultWitnessPlaceholders(transaction); ``` ---------------------------------------- TITLE: Adding Preimage to Witnesses for Hash Lock Cell Consumption DESCRIPTION: This code demonstrates how to add the secret preimage directly to the transaction's witnesses structure when consuming Hash Lock cells. Since only OCC Lock cells are used as inputs and do not require signatures, placeholder and signing steps are skipped. The preimage is placed at index 0, allowing the OCC Lock to quickly locate it during execution. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/using-the-witness.md#_snippet_5 LANGUAGE: javascript CODE: ``` // Add in the witness placeholders. // transaction = addDefaultWitnessPlaceholders(transaction); // Sign the transaction. // const signedTx = signTransaction(transaction, privateKey1); // Add our preimage to the witnesses. transaction = transaction.update("witnesses", (w)=>w.push(preimage)); ``` ---------------------------------------- TITLE: Creating Output Cells with IC3Type Script DESCRIPTION: This code generates output cells that use the IC3Type script as a type script. It demonstrates calculating the required capacity, defining the lock script using `addressToScript()`, structuring the type script with `codeHash`, `hashType`, and `args`, and finally adding multiple identical output cells to the transaction. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/validating-a-transaction.md#_snippet_6 LANGUAGE: javascript CODE: ``` // Create a cell using the IC3Type script as a type script. const outputCapacity1 = ckbytesToShannons(94n); const lockScript1 = addressToScript(address1); const typeScript1 = { codeHash: dataFileHash1, hashType: "data", args: "0x" }; const output1 = {cellOutput: {capacity: intToHex(outputCapacity1), lock: lockScript1, type: typeScript1}, data: "0x"}; transaction = transaction.update("outputs", (i)=>i.push(output1, output1, output1)); ``` ---------------------------------------- TITLE: JavaScript: Lumos Transaction for Creating Aggregatable Counter Cells DESCRIPTION: This JavaScript code, part of a Lumos example, demonstrates how to programmatically create multiple cells that utilize the Aggregatable Counter type script. It iterates through a predefined set of initial amounts, converting each into a u64 LE hex byte format, and then constructs and adds new cell outputs to a transaction, each containing the Aggregatable Counter type script and its initial data. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/creating-aggregatable-scripts.md#_snippet_4 LANGUAGE: JavaScript CODE: ``` // Create cells using the Aggregatable Counter type script. for(const amount of [0n, 42n, 9_000n]) { const outputCapacity1 = ckbytesToShannons(102n); const lockScript1 = addressToScript(address1); const typeScript1 = { codeHash: dataFileHash1, hashType: "data", args: "0x" }; const data1 = intToU64LeHexBytes(amount); const output1 = {cellOutput: {capacity: intToHex(outputCapacity1), lock: lockScript1, type: typeScript1}, data: data1}; transaction = transaction.update("outputs", (i)=>i.push(output1)); } ``` ---------------------------------------- TITLE: Creating CKB Cells with ICC Lock (JavaScript) DESCRIPTION: This snippet demonstrates how to generate a transaction to create two cells that utilize the ICC Lock. It details the setup of the lock script, including the `code_hash`, `hash_type`, and the `args` which specify the required CKBytes for unlocking (500 CKBytes in this case). The cells are created with a capacity of 500 CKBytes each. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/untitled.md#_snippet_2 LANGUAGE: javascript CODE: ``` // Create cells using the ICC Lock. const outputCapacity1 = ckbytesToShannons(500n); const iccLockAmount1 = intToU64LeHexBytes(ckbytesToShannons(500n)); const lockScript1 = { code_hash: dataFileHash1, hash_type: "data", args: iccLockAmount1 }; const output1 = {cell_output: {capacity: intToHex(outputCapacity1), lock: lockScript1, type: null}, data: "0x"}; transaction = transaction.update("outputs", (i)=>i.concat([output1, output1])); ``` ---------------------------------------- TITLE: Default CKB Lock Script Structure with Args DESCRIPTION: This snippet illustrates the structure of a common CKB lock script, highlighting the `code_hash`, `hash_type`, and `args` fields. It represents the default lock script on a local development chain, where the `args` field typically contains a hashed public key that indicates cell ownership. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/managing-permissions/untitled.md#_snippet_0 LANGUAGE: text CODE: ``` { "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", "hash_type": "type", "args": "0x988a9c3e74c09dab76c8e41d481a71f4d36d772f" } ``` ---------------------------------------- TITLE: JavaScript: Example CKB Lock Script Object Structure DESCRIPTION: This JavaScript object literal shows the typical structure of a CKB lock script after conversion from an address. It includes `codeHash` to identify the script's executable code, `hashType` to specify its interpretation, and `args` to pass dynamic data to the script upon execution. This structure defines the 'what' and 'how' of script execution. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/scripting-basics/using-scripts.md#_snippet_1 LANGUAGE: javascript CODE: ``` { codeHash: '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8', hashType: 'type', args: '0x988a9c3e74c09dab76c8e41d481a71f4d36d772f' } ``` ---------------------------------------- TITLE: Lumos CellCollector Query for Basic Cells DESCRIPTION: This query is used with Lumos's `CellCollector()` to find live cells. It searches for cells that have a specific lock script (owner) and no type script, indicating a basic cell without a smart contract, typically used for capacity. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/updating-data-in-a-cell.md#_snippet_0 LANGUAGE: javascript CODE: ``` {lock: lockScript, type: null} ``` ---------------------------------------- TITLE: Lumos Transaction: Create Skeleton DESCRIPTION: Creates an empty transaction skeleton using Lumos's `TransactionSkeleton()` function. This skeleton serves as the base structure that will be progressively populated with inputs, outputs, and other transaction details before being signed and broadcast. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/introduction-to-lumos.md#_snippet_3 LANGUAGE: javascript CODE: ``` // Create a transaction skeleton. let transaction = TransactionSkeleton(); ``` ---------------------------------------- TITLE: Lumos Transaction: Import Dependencies DESCRIPTION: This snippet imports necessary modules for Lumos-based CKB transaction development. It includes core Lumos components for configuration, address conversion, transaction handling, and indexing, along with custom utility and lab-specific functions. SOURCE: https://github.com/jordanmack/developer-training-course-documentation/blob/master/transactions/introduction-to-lumos.md#_snippet_0 LANGUAGE: javascript CODE: ``` import fs from "fs"; import {initializeConfig} from "@ckb-lumos/config-manager"; import {addressToScript, TransactionSkeleton} from "@ckb-lumos/helpers"; import {Indexer} from "@ckb-lumos/ckb-indexer"; import {addDefaultCellDeps, addDefaultWitnessPlaceholders, getLiveCell, sendTransaction, signTransaction, waitForTransactionConfirmation} from "../lib/index.js"; import {hexToInt, intToHex} from "../lib/util.js"; import {describeTransaction} from "./lab.js"; const CONFIG = JSON.parse(fs.readFileSync("../config.json")); ```