Skip to main content

Store Data on Cell

⏰ Estimated Time: 2 - 5 min
🔧 What You Will Need:
For detailed installation steps, refer to our Installation Guide

Tutorial Overview

In this tutorial, you will learn how to tuck a nifty message - "Hello CKB!" - into a Cell

on the CKB blockchain. Imagine it as sending a message in a bottle, but the ocean is digital, and the bottle is a super secure, tamper-proof CKB Cell!

As you have learned from the first tutorial Transfer CKB, the Cell can store any type of data in the data field of Cell structure. Here we will put a text message encoding in hex string format and store it in the data field. Once your words are encoded and inscribed into the blockchain, we'll then get the hex string from the same Cell back and then decode them to the original text message. the method of encoding and decoding is totally up to your favorite, we use the TextDecoder for simplicity through the tutorial.

Setup Devnet & Run Example

Step 1: Clone the Repository

To get started with the tutorial dApp, clone the repository and navigate to the appropriate directory using the following commands:

git clone https://github.com/nervosnetwork/docs.nervos.org.git
cd docs.nervos.org/examples/store-data-on-cell

Step 2: Start the Devnet

To interact with the dApp, ensure that your Devnet is up and running. After installing @offckb/cli, open a terminal and start the Devnet with the following command:

offckb node

You might want to check pre-funded accounts and copy private keys for later use. Open another terminal and execute:

offckb accounts

Step 3: Run the Example

Navigate to your project, install the node dependencies, and start running the example:

yarn && NETWORK=devnet yarn start

Now, the app is running in http://localhost:1234


Behind the Scene

Open the lib.ts file in your project, it lists all the important functions that do the most of work for the project.

Encode & Decode Message

Since Cell's data field can store any type of data, we need to design our encoding and decoding method for the message we want to read and write on-chain.

export function utf8ToHex(utf8String: string): string {
const encoder = new TextEncoder();
const uint8Array = encoder.encode(utf8String);
return (
"0x" +
Array.prototype.map
.call(uint8Array, (byte: number) => {
return ("0" + (byte & 0xff).toString(16)).slice(-2);
})
.join("")
);
}

export function hexToUtf8(hexString: string): string {
const decoder = new TextDecoder("utf-8");
const uint8Array = new Uint8Array(
hexString.match(/[\da-f]{2}/gi)!.map((h) => parseInt(h, 16))
);
return decoder.decode(uint8Array);
}

Build Transaction

Now, check out the core function buildMessageTx. It requires two parameters:

  • Private Key: Your private key, used for transaction authorization.
  • Message: The message you want to write into the Cell.

The function then constructs a transaction to create a new Cell that incorporates the specified message in the data field

export async function buildMessageTx(
onChainMemo: string,
privateKey: string
): Promise<string> {
...
}

As always, we first create a transaction using CCC:

const onChainMemoHex = utf8ToHex(onChainMemo);
const tx = ccc.Transaction.from({
outputs: [{ lock: signerAddress.script }],
outputsData: [onChainMemoHex],
});

Here we build the output Cell to store the message data by putting the hex format of the text message into the data field of the output Cell.

Next, we ask CCC to complete the transaction for us with transaction fee:

// Complete missing parts for transaction
await tx.completeInputsByCapacity(signer);
await tx.completeFeeBy(signer, 1000);

Lastly, we use signer to sign and broadcast the transaction to the blockchain network through rpc:

const txHash = await signer.sendTransaction(tx);

Therefore, the message is successfully stored on a Cell and lives in the blockchain.

Read Cell Messages

To read the message we stored on-chain, we need to retrieve the Live Cell

we just produced, read the data field from the Cell and decode the message back to the text format.

To retrieve a specific Live Cell, we use the RPC method getLiveCell with OutPoint parameters:

  • txHash: The transaction hash from which the Cell originated.
  • output Cell index: The position index of the Cell within the transaction's outputs.

Given a specific transaction hash, we can locate the output Cells of the transaction. By knowing the position index of the Cell, we can find out the specific one.

For the way we built the transaction, we know that the Live Cell that carries the message is always the first one of the output Cells. So we set index = "0x0"

export async function readOnChainMessage(txHash: string, index = "0x0") {
const cell = await cccClient.getCellLive({ txHash, index }, true);
if (cell == null) {
return alert("Cell not found, please retry later");
}
const data = cell.outputData;
const msg = hexToUtf8(data);
alert("read msg: " + msg);
return msg;
}

Congratulations!

By following this tutorial this far, you have mastered how storing data on Cells works on CKB. Here's a quick recap:

  • We can store arbitrary data in the data field of Cell.
  • We need a way to encode and decode our data for understanding and using our raw on-chain data later.
  • To read the storing data, we need to locate the Live Cell that we put our data in. This can be done by querying Cells meets our requirement or by getting the Cell directly with a known OutPoint through RPC.

Next Step

So now your dApp works great on the local blockchain, you might want to switch it to different environments like Testnet and Mainnet.

To do that, just change the environment variable NETWORK to testnet:

export NETWORK=testnet

For more details, check out the README.md.

Additional Resources