Transaction Skeleton
Transaction Skeleton
Lumos provides an opinionated transaction structure called TransactionSkeleton from the @ckb-lumos/helpers module.
It simplifies assembling transactions on the client-side for CKB.
At A Glance
The following code demonstrates how to build a transaction using TransactionSkeleton
const indexer = new Indexer("https://ckb-rpc-entry")
const rpc = new RPC("https://ckb-rpc-entry")
// txSkeleton is immutable, use 'let' to declare and update
let txSkeleton = TransactionSkeleton({
  cellProvider: (query) =>
    // wrap the indexer as a CellProvider
    indexer.collector({ ...query, type: "empty" }),
})
// update TransactionSkeleton
txSkeleton = txSkeleton
  .update("inputs", (inputs) => inputs.push(inputs0, inputs1))
  .update("outputs", (outputs) => outputs.push(outputs0, outputs1))
  .update("cellDeps", (cellDeps) => cellDeps.push(lockScriptDep, typeScriptDep0))
  .update("witnesses", (witnesses) => witnesses.push(aliceSignature))
// pay fee by the fee rate
txSkeleton = await common.payFeeByFeeRate(txSkeleton, 1000, [fromAddr])
// sign the tranasction
const signatures = txSkeleton
  .get("signingEntries")
  .map(({ message }) => sign(message))
  .toArray()
// convert the TransactionSkeleton to RPC transaction
const signedTransaction = sealTransaction(txSkeleton, signatures)
// broadcast the transaction
const txHash = await rpc.sendTransaction(signedTransaction)
Immutable.js
TransactionSkeleton utilizes Immutable.js to provide an immutable data structure for transactions. This ensures data consistency and simplifies updates
CKB Transaction
CKB, a UTxO-based blockchain inspired by Bitcoin, verifies state transition on-chain instead of doing state transition on-chain. As a result, developers must assemble transaction at client-side instead of calling actions(methods) from a contract to trigger the transition.
The following illustration depicts transferring 1 UDT from Alice to Bob in a CKB transaction:
For a detailed explanation of CKB transaction structure, refer to RFC-0022.
Usage Of TransactionSkeleton
TransactionSkeletonType extends the RPC transaction to simplify client-side transaction assembly.
type TransactionSkeletonType = {
  inputs: ImmutableList<Cell>
  outputs: ImmutableList<Cell>
  cellDeps: ImmutableList<CellDep>
  headerDeps: ImmutableList<Hash>
  witnesses: ImmutableList<HexString>
  inputSinces: ImmutableMap<number, PackedSince>
  // extended fileds
  cellProvider: CellProvider | null
  fixedEntries: ImmutableList<{ field: "inputs" | "outputs"; index: number }>
  signingEntries: ImmutableList<{
    // the "witness_args_lock" is used to work with WitnessArgs.lock in @ckb-lumos/common
    type: string
    index: number
    message: string
  }>
}
Create a TransactionSkeleton:
- Use 
TransactionSkeleton()to create an empty transaction - Optionally, provide properties during creation:
 
TransactionSkeleton({
  // restrict collector to lock-only cells
  cellProvider: (query) => indexer.collector({ ...query, type: "empty" }),
  inputs: [aStickyCellRequiredByMyDapp],
  cellDeps: [aStickyCellDep],
})
inputs and outputs
Lumos uses a type Cell for both inputs and outputs, which differs from the CKB RPC definition
type Cell = {
  cellOutput: {
    lock: Script
    type?: Script
    capacity: HexNumber
  }
  data: HexString
  outPoint?: OutPoint
}
cellOutput:lock: Script representing the ownership of the cell.type(optional): Script indicating the asset type (e.g., Spore, xUDT, sUDT).capacity: Hex string representing the amount of shannon in the cell.
data: Hex string representing the cell's state (e.g., UDT amount).outPoint(for input cells only): Identifies the cell's position in the blockchain
cellDeps and headerDeps
These fields hold dependencies required by the scripts in inputs and outputs.
cellDeps: Include script code for dependencies (e.g., lock script).headerDeps: Include block header dependencies (e.g., CKB DAO deposit block).
witnesses
One-time state for the transaction, such as signatures or preimages of hashes in data.
signingEntries and fixedEntries
signingEntries: An extension for handling transaction signingfixedEntries: Cells that have been tagged withfixedEntries, including previous cells, will no longer participate in later calculations, such aspayFeeandinjectCapacity, but will only use cells after thefixedEntries. The following example will only allow theinput4andinput5to be used as fee cells, and theinput0toinput3are fixed that won't be changed bypayFeeorinjectCapacity
outputs:
  - input0
  - input1 # marked by fixedEntries
  - input2
  - input3 # market by fixedEntries
  - input4
  - input5
cellProvider
A cellProvider: CellProvider is a provider for the cells that required by some functions in @ckb-lumos/common,
such as payFee, payFeeByFeeRate, transfer, etc.
We can customize the cellProvider to avoid using unexpected cells in some s=scenarios like payFee, for example
txSkeleton = txSkeleton.update("cellProvider", (query) =>
  indexer.collector({
    type: "empty",
    ...query,
  })
)