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 aspayFee
andinjectCapacity
, but will only use cells after thefixedEntries
. The following example will only allow theinput4
andinput5
to be used as fee cells, and theinput0
toinput3
are fixed that won't be changed bypayFee
orinjectCapacity
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,
})
)