Skip to content
/ kuai Public

A protocol and framework for building universal dapps on Nervos CKB

License

Notifications You must be signed in to change notification settings

ckb-js/kuai

Repository files navigation

Kuai Logo

Overview

Kuai is an application development philosophy and all-in-one development framework designed specifically for Nervos CKB. It encompasses generic application development patterns, convenient development scaffolding, plug-and-play application architecture, and development manuals to facilitate development processes.

Design Philosophy

The cell model of CKB is a generalization of Bitcoin's UTXO model and naturally has the ability to execute in parallel. Inspired by the Actor model, the Kuai project abstracts a group of cells into an actor based on patterns. Actors communicate with each other through message passing, which enables efficient parallel execution of multiple actors and improves the interoperability from module level to application level. This design facilitates the development of highly concurrent and scalable applications.

Read Kuai Proposal for more details on the design.

Modules

kuai-cli maps kuai's task system to a unix command-line interface and executes it, providing additional commands such as help

how to use this module

build or download kuai-cli

  • Build locally
$ git clone git@github.com:ckb-js/kuai.git
$ npm i
$ npx lerna run build
$ npx link ./packages/cli

Built-in commands list

  • kuai test
# kuai test [options]

# Options:
#   --projectRootPath [path]    The Root of testing project (default: "")
#   -h, --help                  display help for command
  • kuai node
# start
# kuai node [options] start

# Options:
#   --port [number]            port number (default: 8114)
#   --detached                 run in backend (default: false)
#   --genesisArgs [string...]  The genesis args
#   -h, --help                 display help for command
# close
$ kuai node close
  • kuai init
# kuai init [options]

# Options:
#   -h, --help                 display help for command
  • kuai build
# Usage: kuai build [options] [command]

# build the dapp

# Options:
#   --server    Build Server (default: true)
#   -h, --help  display help for command

# Commands:
#   dev         build server side application in dev mode
#   server      build server side application
  • Kuai contract [TODO]
  • kuai contract deploy
# Usage: kuai contract deploy [options]

# Options:
#   --name [string]      name of the contract to be deployed (default: "")
#   --bin-path [path]    path of contract bin file (default: "")
#   --from <string...>   address or multisig config of the contract deployer (default: "")
#   --signer [string]    signer provider [default: ckb-cli] [possible values: ckb-cli, ckb-cli-multisig]
#                        (default: "")
#   --fee-rate [number]  per transaction's fee, deployment may involve more than one transaction. default:
#                        [1000] shannons/Byte (default: 1000)
#   --export [path]      export transaction to file (default: "")
#   --send               send transaction directly (default: false)
#   --no-type-id         not use type id deploy
#   -h, --help           display help for command
  • kuai contract sign-message
# Usage: kuai contract sign-message [options]

# Options:
#   --message <string>  message to be signed (default: "")
#   --address <string>  the address of message signer (default: "")
#   --prefix [string]   the prefix of signature (default: "")
#   --signer [string]   signer provider [default: ckb-cli] [possible values: ckb-cli, ckb-cli-multisig]
#                       (default: "ckb-cli")
#   -h, --help          display help for command
  • kuai contract build
# Usage: kuai contract build [options]

# Options:
#   --name [string]  contract name (default: "")
#   --release        build contracts in release mode (default: false)
#   -h, --help       display help for command
  • kuai contract new
# Usage: kuai contract new [options]

# Options:
#   --name <string>      The name of new contract
#   --template [string]  language template  [default: rust]  [possible values: rust, c, c-sharedlib]
#                        (default: "rust")
#   -h, --help           display help for command
  • Kuai signer [built-in signer]
  • kuai signer account-list
# Usage: kuai signer account-list [options]

# list accounts of built-in signer

# Options:
#   --console   console output (default: false)
#   -h, --help  display help for command
  • kuai signer account-new
# Usage: kuai signer account-new [options]

# create new account for built-in signer

# Options:
#   --password <string>  The password of account
#   --console            console output (default: false)
#   -h, --help           display help for command
  • kuai signer account-import
# Usage: kuai signer account-import [options]

# import an unencrypted private key from <privkey-path> and create a new account.

# Options:
#   --privkey-path <string>           The privkey is assumed to contain an unencrypted private key in
                                    hexadecimal format. (only read first line)
#   --password [string]               The Password to lock your account (default: "")
#   --extended-privkey-path <string>  Extended private key path (include master private key and chain
                                    code)
#   --console                         console output (default: false)
#   -h, --help                        display help for command
  • kuai signer account-export
# Usage: kuai signer account-export [options]

# export master private key and chain code as hex plain text (USE WITH YOUR OWN RISK)

# Options:
#   --lock-arg <string>               Lock argument (account identifier, blake2b(pubkey)[0..20])
#   --password [string]               The Password for unlock your account (default: "")
#   --extended-privkey-path <string>   Output extended private key path (PrivKey + ChainCode)
#   --console                         console output (default: false)
#   -h, --help                        display help for command

The common module provides a set of common utility functions and helpers that are used throughout the Kuai repository. [what is this module] @zhengjianhui [how to use this module] @zhengjianhui

The core module exposes the core functionalities for building decentralized applications on Nervos CKB. It includes tasks to create projects in generic development patterns and a universal architecture, to launch a local devnet chain, to run tests in a general manner, to deploy applications smoothly.

how to use this module

  1. define built-in kuai project templates at https://github.com/ckb-js/kuai/tree/develop/packages/core/sample-projects, these templates are used to initialize a DApp;
  2. define tasks at https://github.com/ckb-js/kuai/tree/develop/packages/core/src/builtin-tasks to enhance kuai-cli, it includes basic steps to build a DApp, e.g. build for various modes, develop&deploy a contract, start devnet locally, run tests in a generalized manner;
  3. export base class of ContractDeployer at https://github.com/ckb-js/kuai/blob/develop/packages/core/src/contract.ts for extension;
  4. define scripts metadata of devnet at https://github.com/ckb-js/kuai/blob/develop/packages/core/src/util/chain.ts, dao, secp256k1_blake160, and secp256k1_blake160_multisig are set by default;
  5. wrap ckb-cli at https://github.com/ckb-js/kuai/blob/develop/packages/core/src/ckb-cli.ts to provide a universal development experience(DX);
  6. define default networks at https://github.com/ckb-js/kuai/blob/develop/packages/core/src/constants.ts;

The docker-node module provides a Docker image for running a Nervos CKB node in a containerized environment. It includes all the necessary dependencies and configuration files.

how to use this module

import { CkbDockerNetwork } from '@ckb-js/kuai-docker-node'

const ckbDockerNetwork = new CkbDockerNetwork()

// start ckb-node
await ckbDockerNetwork.start({
  port: 3000, //
  detached: true, //  Whether to run in the background
  genesisAccountArgs: ['0x0000000000000000000000000000000000000000', '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'], // Initial account on the chain
})

// stop ckb-node
await ckbDockerNetwork.stop()

The IO module defines interfaces of applications for various use cases. It includes functions to encode/decode input and output data.

The IO module contains several parts

  • cor is a chain of responsibility implementation with two methods, use and dispatch using use adds middleware to cor, and when using dispacth, the incoming arguments are processed through each middleware

  • router Router is a cor middle built into kuai/io that provides the ability to find and execute a specific middleware based on a path, and is often used to implement restful interfaces

  • adapter Adapter is kuai/io built-in and other http framework compatible adapter, currently supports koaAdapter

  • listener Listener is used to periodically rotate the data on the chain and return a rxjs Observable

how to use this module

  • cor
import { CoR } from '@ckb-js/kuai-io'

const cor = new CoR()

const mockFn = console.log('mockFn')
const mockFn1 = console.log('mockFn1')
const mockFn2 = console.log('mockFn2')

cor.use(async (_, next) => {
  mockFn()
  await next()
})

cor.use(async (ctx) => {
  mockFn1()
  ctx.ok('ok')
})

cor.use(async (_, next) => {
  mockFn2()
  await next()
})

await cor.dispatch({})
// print
// mockFn
// mockFn2
  • router
import { CoR, KuaiRouter } from '@ckb-js/kuai-io'

const cor = new CoR()
const kuaiRouter = new KuaiRouter()
kuaiRouter.get('/', async (ctx) => {
  ctx.ok('hello root')
})

kuaiRouter.get('/parent', async (ctx) => {
  ctx.ok('hello parent')
})

kuaiRouter.get('/parent/children', async (ctx) => {
  ctx.ok('hello children')
})

cor.use(kuaiRouter.middleware())

const rootResult = await cor.dispatch({ method: 'GET', path: '/' })
// hello root

const parentResult = await cor.dispatch({ method: 'GET', path: '/parent' })
// hello parent

const childrenResult = await cor.dispatch({ method: 'GET', path: '/parent/children' })
// hello children
  • adapter
import { CoR, KuaiRouter } from '@ckb-js/kuai-io'

const koaServer = new Koa()
koaServer.use(koaBody())

const cor = new CoR()
const kuaiRouter = new KuaiRouter()
kuaiRouter.get('/', async (ctx) => {
  ctx.ok('hello root')
})

kuaiRouter.get('/parent', async (ctx) => {
  ctx.ok('hello parent')
})

kuaiRouter.get('/parent/children', async (ctx) => {
  ctx.ok('hello children')
})

cor.use(kuaiRouter.middleware())
const koaRouterAdapter = new KoaRouterAdapter(cor)
koaServer.use(koaRouterAdapter.routes())
const server = koaServer.listen(4004)

const res = await fetch('http://localhost:4004/', { method: 'GET' })
console.log(res.status) // 200
const body = await res.text()
console.log(body) // hello root

const res = await fetch('http://localhost:4004/parent', { method: 'GET' })
console.log(res.status) // 200
const body = await res.text()
console.log(body) // hello parent

const res = await fetch('http://localhost:4004/parent/children', { method: 'GET' })
console.log(res.status) // 200
const body = await res.text()
console.log(body) // hello children

This codebase provides a set of data models for working with the Nervos CKB blockchain. It includes models for blocks, transactions, and cells, among others.

Resource binding is a built-in module that serves the purpose of connecting on-chain data with off-chain data. Once the DApp is launched, resource binding synchronizes the data based on pre-defined rules. Whenever there is a change in on-chain data, resource binding promptly updates the off-chain model.

How to use the resource binding?

Initiate the resource binding when bootstrap

First, the resource binding module is a pluggable module that can be utilized by declaring it in the entry file. We provide a function which can help to initialize the resource binding as follow.

import {initiateResourceBindingManager } from '@ckb-js/kuai-models'
import { TipHeaderListener, NervosChainSource } from '@ckb-js/kuai-io'

async function bootstrap() {
  ...
  const dataSource = new NervosChainSource(kuaiEnv.config.ckbChain.rpcUrl)
  initiateResourceBindingManager(dataSource, new TipHeaderListener(dataSource))
  ...
}

For this, we provider the default listener and the default data source in kuai-io module, you can find the import as above.

Define store with decorators

We provider several decorators to help to register the store to resource binding, here is one example to use these decorators as below.

import { ActorProvider, DataFilter, LockFilter, Omnilock } from '@ckb-js/kuai-models'

@ActorProvider({ ref: { name: 'omnilock', path: `/:args/` } })
@LockFilter()
@DataFilter('0x')
@Omnilock()
class OmnilockModel extends JSONStore<Record<string, never>> {
  constructor() {
    @Param('args') args: string,
    ...
  }

  ...
}
Register

kuai should know which store should be connected to the chain, so the store should be registered to the kuai. We provider a decorator ActorProcider to achieve this. The name defined in ref is to register to the Registry with the store, and the parameters in path is used to inject to the constructor as defined in Param decorator.

Lock

As in nervos network, each cell has its own lock script, so the lock script should be defined in store. We provide two default lock decorators, the Omnilock and the Secp256k1Lock, to use to define the most used lock scripts in nervos network, and if you are using other lock scripts in system, the DefaultScript could be used. Also we provide the Lock decorator for you to define your custom lock script.

The Lock decorators we provided as below.

  • Lock
  • DefaultScript
  • Omnilock
  • Secp256k1Lock
Filter

kuai should know which cell should be connected to the store, filter data of them from chain. We provide several decorator for you to do this.

  • LockFilter: Filter the lock to equal to the lockscript of store defined.
  • DataFilter: It has one parameter means data, filter the data in cell to equal to the parameter.
  • DataPrefixFilter: It has one parameter means data prefix, filter the data to start with the parameter.

The store extends from Actor, uses to subscribe data on the ckb chain and transform it to locally structured data, then change them and submit to the ckb chain.

Supported storage location: data, witness, lock.args, type.args.

Supported transform methods: JSON and molecule

Supported types for JSON Store: string, array, object, reference packages/models/src/store/json-storage.ts

Supported types for molecule store: string, number, BI, array, object, Supported molecule types: array, struct, vec, table, option, union. reference molecule-storage

How to use this module

Defined JSONStore and MoleculeStore to handle JSON data or molecule data.

  • Create a new Store:

    • For JSON with JSONStore:
    type Data = {
      field1: string
      field2: string
      field3: string
    }
    export class NewJSONStore extends JSONStore<{ data: Data }> {
      constructor(
        _schemaOption?: { data: true },
        params?: {
          states?: Record<OutPointString, { data: Data }>
          chainData?: Record<OutPointString, UpdateStorageValue>
          cellPattern?: CellPattern
          schemaPattern?: SchemaPattern
        },
      ) {
        super({ data: true }, params)
      }
    }
    • For molecule with MoleculeStore:
    const data = {
      type: 'table',
      value: { field1: 'string', field2: 'Uint8' },
    } as const
    type Data = typeof data
    
    export class NewMoleculeStore extends MoleculeStore<{ data: Data }> {
      constructor(
        _schemaOption?: { data: true },
        params?: {
          states?: Record<OutPointString, GetStorageStruct<GetMoleculeStorageStruct<{ data: Data }>>>
          chainData?: Record<OutPointString, UpdateStorageValue>
          cellPattern?: CellPattern
          schemaPattern?: SchemaPattern
        },
      ) {
        super(
          { data: true },
          {
            ...params,
            options: { data },
          },
        )
      }
    }
  • Use new Store to transform locally structured data to chain data

    • New JSONStore
      new JSONStore.initOnChain({
        data: {
          field1: 'a',
          field2: 'a',
          field3: 'b',
        },
      })
     - New MoleculeStore
      ```typescript
         new MoleculeStore.initOnChain({
               data: {
                   field1: 'a',
                   field2: 10
               }
         })
    
  • Register with resource binding

    [TODO]

  • Update the data

    // Update from the root
    store.set(['data'], { field1: 'a1', field2: 'a1', field3: 'b1' })
    // update a field
    store.set(['data', 'field1'], 'a1')
  • Get the data

    // get from the root
    store.get(['data'])
    // get a filed value with key path
    store.set(['data', 'field1'])

Tutorials & Demos

  • MVP DApp This is a sample Minimum Viable Product (MVP) decentralized application(dAppp) built using Kuai framework. It aims to demonstrate how easy and fast it is to build a simple dApp specific to cell model, and showcase the abstract ability to easily read and write data to cells.

  • Development Guide This is a comprehensive guide that lists the fundamental steps to build the MVP DApp;

Quick start

Environment

node >= 18

Kuai Installation

First, install kuai globally by npm link:

$ git clone git@github.com:ckb-js/kuai.git

$ cd kuai

$ npm install

$ npm run build

$ cd packages/cli

$ npm link

Create a template project using the command.

kuai init

Running

kuai has dev and prod modes

dev

npm run dev

prod

npm run build
npm run start:prod

Others commands

run test

npm run test

build docs

The docs is build by typedoc, typedoc.json is the build configuration.

npm run doc

Kuai Roadmap

- M1 ~ M2
  - [x] Construct a build system similar to https://nx.dev/, only for project structure, basic tasks, and plugins
  - [x] Design storage paradigm
  - [ ] Design action paradigm
  - [ ] Design workflow with base contracts/scripts
- M3 ~ M4
  - [ ] Implements SDK following the designed paradigm and workflow with base contracts/scripts
  - [x] Design generally used tasks and plugins
  - [ ] Cycle reporter
  - [ ] Script sizer
  - [ ] Test-coverage
  - [x] Document
  - [ ] Debugger remover
  - [ ] Typechain: https://github.com/dethcrypto/TypeChain/
  - [ ] Storage layout: https://www.npmjs.com/package/hardhat-storage-layout, when to use on-chain/off-chain data
  - [x] Design local network debug/test/deploy tools
- M5
  - [x] Implement tasks/plugins
  - [x] Implement local network
  - [x] Implement debug/test/deploy tools
- M6
  - [x] Use the project to deliver a simple .bit dapp