Introducing Mock Replica - LightIC

Hi Everyone,

I’m currently working on a “Light Replica” project under Developers Grant Program.

The goal of this project is to give you a possibility to write tests/deployment scripts in JavaScript/TypeScript and run them against canisters in development. If you have used truffle/hardhat than it is exactly the kind of experience (especially hardhat) I want to bring to IC development.

I’m implementing “mock replica” in TypeScript along with WASM dropins so it will be possible to run in node.js and any brower that supports WASM (so actually all of major browsers).

Project Description
Ethereum and other EVM chains benefit from frameworks such as truffle or hardhat. They enable easy and fast creation, testing and deployment of solutions to EVM based chains. Their strength lies in usage of JS scripts to do everything: setup local node, make tests, make deployments and more.

“Light Replica” is a project to startup creation of similar tool for the Internet Computer ecosystem. It will be composed from two elements:

  • Light Node - it is a local node designed for development (fast startup and cleanup), it will replicate behavior of the real node with additional logging and functions to help testing. It will be able to run and interact with any IC compatible wasm file.
  • Light Runner/Cli - a set of libraries written in JS (available in npm) that can be used in node.js environment to create and build projects, write tests, create deployments and arbitrary scripts that have access to project “context” right from the js code.

Both tools are aimed at developers. To help them efficiently develop, test and deploy canisters to the IC.

I would like your input on what JS testing library should be supported (I was thinking Jest or Mocha). Also the API of the framework is up to discussion.


Starting with jest sounds like a good approach to me. vitetest would probably be neat. never used it yet but, they seem to have quite some momemtum and I heard it’s super fast. Enough momemtum that if I would have to start a new suite of test, I would probably first try vitetest over jest.

Super interesting and valuable project, please keep posted :+1:



I’ve created a repo with code for LightIC - GitHub - icopen/light_ic

What is currently working:

  • Install container in a replica, the canister id will be automatically assigned (Principal object is used from @dfinity/principal). Currently it works only with wasm files (no compilation support yet)
  • The candid file can be specified by hand or it will be obtained from canister (works on all Motoko canisters and Rust canister that explicitly supports this method). It will be automatically transformed in to IDL data (I’m using objects from @dfinity/candid). You will get actor like object, which can be used to call deployed canister
  • Currently you can call only query methods, all debug and traps will be displayed on the console. I’ve checked and the call and result looks identical as in regular “agent” (I’ve tried to match it, so it is familiar to use)

What is next:

  • Refactor of the code, so it can be uploaded to npmjs
  • Adding support for update calls
  • More tests, and examples on how to write tests using published npmjs package

And what is on the roadmap:

  • Ability to redirect canister output to custom endpoint
  • BLS signatures and express server (so that you can use actual @dfinity packages)
  • Implementation of management canister

What do you think about it?


I recommend vitest over jest now. The configuration around ESModules, TypeScript, and so on is just a lot simpler



Here is an example of test transfer using official ledger, deployed to Light IC

it('transfer ICP', async function () {
    const mintingPrincipal = Principal.fromText('3zjeh-xtbtx-mwebn-37a43-7nbck-qgquk-xtrny-42ujn-gzaxw-ncbzw-kqe')
    const targetPrincipal = Principal.fromText('o2ivq-5dsz3-nba5d-pwbk2-hdd3i-vybeq-qfz35-rqg27-lyesf-xghzc-3ae')
    const invokingPrincipal = Principal.fromText('7gaq2-4kttl-vtbt4-oo47w-igteo-cpk2k-57h3p-yioqe-wkawi-wz45g-jae')

    const mintingAccount = getAccount(mintingPrincipal, 0)
    const invokingAccount = getAccount(invokingPrincipal, 0)
    const targetAccount = getAccount(targetPrincipal, 0)

    const canister = await context.deploy('./cache/ledger.wasm', [{
      minting_account: mintingAccount.toHex(),
      initial_values: [[invokingAccount.toHex(), { e8s: 100_000_000_000 }]],
      send_whitelist: [],
      token_symbol: [],
      token_name: [],
      transfer_fee: [{ e8s: 10_000 }],
      transaction_window: [],
      max_message_size_bytes: [],
      icrc1_minting_account: [],
      archive_options: []

    const actor = canister.get_actor(invokingPrincipal)
    const args = {
      amount: { e8s: 100_000 },
      memo: 0,
      fee: { e8s: 10_000 },
      from_subaccount: [],
      to: targetAccount.toUint8Array(),
      created_at_time: []
    const result = await actor.transfer(args) as any


    assert.equal(result[0].Ok, 1n)

I’m trying to make it more verbose, it still required a lot of code to setup the ledger. Any ideas?


I think this is a great project and wish you good luck with it!

One thing that might be difficult to handle efficiently is traps and canister state rollback on trap. Any plans for that?



I’ve published first version of light ic to npm

You can download it via npm

npm i lightic

I will be adding descriptions on how you can use it in tests. In the meantime please look at the tests folder: lightic/test at main · icopen/lightic · GitHub