Developing a real world Smart Rollup

By Pierre-Louis Dubois

Tzwitter is the first non trivial kernel developed inside the kernel gallery. The goal of this kernel was to explore what kind of kernel can be implemented, how to manage assets, what issues have to be resolved when developing a kernel, and what can be improved in the current SDK.

This blog post aims to introduce the Tzwitter Smart Rollup, what are the implemented features and what were the different challenges during the development.


A demonstration is always better than any explanations, thanks to the infra team of Marigold, the Tzwitter Smart Rollup has been originated on Ghostnet.

You can access the application via this link.

Some basic features of Twitter have been implemented: you can publish a tweet and like a tweet of someone. That’s pretty straight forward.

And because we wanted to explore assets management on Smart Rollups, we decided to represent a tweet as a token. So you can transfer a tweet to someone, or withdraw your tweet as a FA2 token on the Layer 1.

Also, don’t be surprised if your tweet takes time to appear, your operation has to be included in the Layer 1.

The Shared Inbox

The shared inbox is common to all Smart Rollups, the shared inbox acts as a transport layer (like TCP). So it means that your rollup should have a way to recognize what messages it should process.

There are many solutions to do so:

  • magic byte
  • framing protocol

Let’s see what are the advantages and the disadvantages of these solutions.

Magic Byte

The easiest solution would be to use a magic byte. When your rollup receives a message, it can check the second byte to check if it matches the defined magic byte (the first one is a byte that indicates if it is an internal or an external message). Then to send a message, all your users will have to prefix this magic byte to their payload.

The Tzwitter kernel is using this solution:

const MAGIC_BYTE: u8 = 0x42;

fn read_input_with_magic_byte<H: Runtime>(host: &mut H) -> Result<Option<Message>, RuntimeError> {
    loop {
        let input = host.read_input()?;
        match input {
            None => return Ok(None),
            Some(msg) => match msg.as_ref() {
                [0x00, ..] => return Ok(Some(msg)), // Any Internal Messages are accepted
                [0x01, MAGIC_BYTE, ..] => return Ok(Some(msg)), // The magic byte is matched, the message is kept
                _ => {} // in any other cases, messages are ignored

Framing Protocol

Magic byte is not a convenient solution, one byte only represents 256 values. What we can do is to implement a new protocol named: the Framing Protocol.

The idea is simple, the first byte is the version of the protocol, the next 20 bytes are your rollup address, and the remaining bytes represent your payload.

If you want to learn more about this announcement, please read our blog post on Marigold website :point_right:
Developing a real world Smart Rollup