Nervos: transactions with ckb-sdk
All posts

Nervos: transactions with ckb-sdk

A practical guide to interacting with the Nervos blockchain using ckb-sdk — covering how to fetch living cells via CKB-indexer, sign transactions, and send CKB between accounts.

Guido Giuntoli
Guido Giuntoli
September 6, 2021·4 min read

TL;DR

To send CKB on the Nervos blockchain with ckb-sdk, fetch an account's living cells from the CKB-indexer via a get_cells JSON-RPC request, then use ckb-sdk-core to generate a raw transaction, sign it with the sender's private key, and broadcast it with rpc.sendTransaction. Nervos uses a cell model analogous to Bitcoin's UTXO system.

At Rather Labs we exploring how to interact with Nervos blockchain, this is a third-generation blockchain that deploys multiple layers which combines security and decentralization on layer one and scalability on the sub-layers.

ckb-sdk is a JavaScript library that allows to sign and send transactions within the Nervos blockchain. Nervos is based on the concept of “cells” which is analog to the (unspent transactions output) UTXO system of Bitcoin. The “living cells” are used to generate transactions and to produce new cells. The cells can be employed generally by only one owner. The cells in the inputs of the transactions cannot be transferred anymore and they become “dead cells”. A Nervos node run two main programs:

  • CKB: This is the main CKB blockchain program that does the synchronization with the others nodes on the blockchain and can perform transaction validations and mine new blocks.
  • CKB-indexer: This acts as a database allowing the users to fetch the living cells from the blockchain quicker.

To create a transaction on the CKB blockchain, we need to:

  1. Fetch the living cells of a CKB account, from where we are going to transact the CKBs, using the CKB-indexer. It is required to have an access point to a public indexer or even to run a Testnet node locally.
  2. Transfer the tokens using the living cells from the account gotten from the previous step. For this, we are going to use the ckb-sdk library which enables us to sign and send the transactions.

To transfer CKBs create two accounts, one for sending CKBs and the other for receiving. Then use the CKB Testnet faucet to fill one of them with some CKBs. Check this page for generating new CKB addresses.

For fetching the living cells information from the blockchain we need the lock script that identifies the CKB accounts. Each CKB account has a unique lock script that can be obtained using the ckb-sdk-utils (part of ckb-sdk-core) package:

const ckbSdkUtils = require("@nervosnetwork/ckb-sdk-utils");
let address = "ckt1qyqycgdq0d5fpe9nqpf7j0l2n53lxmceqpas2shhaz";
let lockScript = ckbSdkUtils.addressToScript(address)
console.log(lockScript)

The lock script looks like this:

{
    codeHash: '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8',
    hashType: 'type',
    args: '0x4c21a07b6890e4b30053e93fea9d23f36f19007b'
}

The first step is to fetch the living cells by doing an HTTP request to the indexer. In our case, we are running the ckb-indexer in our private node and it accepts the request with the “get_cells” method. The following code snippet prepares the data to send as an HTTP request and uses Axios to perform the request:

const indexerUrl = "http://localhost:3000"

let post\_data = {
 id: Date.now(),
 jsonrpc: "2.0", method: "get\_cells",
 params: [
 {
 "script": {
 "code\_hash": lockScript.codeHash,
 "hash\_type": lockScript.hashType,
 "args": lockScript.args
 },
 "script\_type": "lock"
 },
 "asc",
 "0x100"
 ],
};

let post\_options = {headers: {'Content-Type': 'application/json'}};

let cells;axios.post(indexerUrl, post\_data, post\_options)
 .then((response) => {
 cells = response.data.result.objects.map((value) => {
 return {
 lock: {
 codeHash: value.output.lock.code\_hash,
 hashType: value.output.lock.hash\_type,
 args: value.output.lock.args,
 },
 outPoint: {
 txHash: value.out\_point.tx\_hash,
 index: value.out\_point.index,
 },
 capacity: value.output.capacity,
 data: value.output\_data,
 };
 });
 })
 .catch((error) => {
 console.log("error:", error);
 })

You will need to adapt the URL of the indexer indexerUrl to a public URL or the http://localhost:port if you are running a node locally.

Once the cells related to the sending CKB account have been fetched, it is time to sign and send the transaction. The most important parameters to consider in this part would be:

  • Sending address:fromAddress
  • Sending private key: addressPrivate
  • Receiving address: toAddress
  • Amount to send:capacity
  • Fee for the transaction: fee
const CKB = require('@nervosnetwork/ckb-sdk-core').default;

const signAndSend = async () => {
 const ckb = new CKB(nodeUrl);
 await ckb.loadDeps();
 const rawTransaction = await ckb.generateRawTransaction({
 fromAddress: address,
 toAddress,
 capacity: BigInt(99 \* 1E8),//1E8 shannons = 1CKB, min 61E8 shannon = 61CKB
 fee: BigInt(0.001 \* 1E8),
 safeMode: true,
 cells,
 deps: ckb.config.secp256k1Dep,
 });

 const signedTx = ckb.signTransaction(addressPrivate)(rawTransaction);
 const realTxHash = await ckb.rpc.sendTransaction(signedTx)
;}

signAndSend();

These are the basic steps to perform a transaction, more advanced things like checking how many nodes confirm the transaction and checking the balances are also going to be covered in new articles.

Please check the full source code use for this article at the Rather Labs Github. Don’t miss the next articles coming where we are going to explain how to deploy and interact with User Defined Tokens (UDT) and how to work with Non-Fungible Tokens (NFT) on the Nervos Blockchain.

For this articled we have used the following NPM packages:

Thanks to Federico Caccia and Franco Scucchiero

Frequently asked questions

What are living cells in Nervos and how do they relate to transactions?

Nervos uses a cell model analogous to Bitcoin's UTXO system, where living cells are unspent cells owned by a single account and used to generate new transactions. When a cell is consumed as a transaction input it becomes a dead cell and can no longer be transferred. To build a transaction with ckb-sdk you first fetch the sender's living cells from the CKB-indexer.

What is the CKB-indexer and why is it needed?

The CKB-indexer is one of the two main programs a Nervos node runs alongside CKB itself, and it acts as a database that lets users fetch living cells from the blockchain quickly. You query it with a get_cells JSON-RPC request, passing the account's lock script and script_type. You need access to a public indexer or can run a Testnet node locally.

How do you sign and send a CKB transaction with ckb-sdk?

Instantiate CKB from @nervosnetwork/ckb-sdk-core with the node URL and call loadDeps, then use generateRawTransaction with the from/to addresses, capacity, fee, and fetched cells. Sign the raw transaction with the sender's private key via signTransaction, and broadcast it using ckb.rpc.sendTransaction. Capacity is measured in shannons, where 1E8 shannons equals 1 CKB and the minimum is 61 CKB.

Share this article