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.
The replica mock is run directly in node.js environment (also works with all browsers that support WASM) and enables you to deploy and test and wasm file that is compatible with Internet Computer reference
It can also be used as a local development replica (started by dfx start) replacement
npm i lightic
First you need to choose the js testing framework of your preference, it could be: mocha, jest, vitest or any other.
For the mocha, you can check the examples in the folder tests
npm i @types/mocha @types/node typescript mocha chai ts-node
And create .mochars.json with content
// This config file contains Mocha's defaults.
// This same configuration could be provided in the `mocha` property of your
// project's `package.json`.
{
"diff": true,
"extension": ["js", "cjs", "mjs", "ts"],
"package": "./package.json",
"reporter": "spec",
"slow": "75",
"timeout": "2000",
"ui": "bdd",
"watch-files": ["lib/**/*.js", "test/**/*.ts"],
"watch-ignore": ["lib/vendor"],
"loader": "ts-node/esm"
}
In order to actually test your canister, you need to add to your test file
import { TestContext, u64IntoPrincipalId } from 'lightic'
const context = new TestContext()
This will create a context to run tests, it is a harness that gives you a possibility to install and run canisters
const canister = await context.deploy('./dfx/local/canisters/example/example.wasm')
const caller = u64IntoPrincipalId(0n)
const actor = Actor.createActor(canister.getIdlBuilder(), {
agent: context.getAgent(caller),
canisterId: canister.get_id()
})
- As you can see this works with a wasm file, so you need to first compile the project using dfx.
dfx canister build $CANISTER_NAME
In the future the test harness will also take care of compilation.
-
You also need to specify the identity principle (who is calling the canisters) and create an actor. There is a helper function
u64IntoPrincipalId
that creates a Principle based on supplied number, so you do not need to come up with fake principals. The created principal is not random. -
Then you get an actor, which is the same type as regular dfinity actor. In this example the actual actor class from @dfinity package is used
-
In order to call canister:
const result = await actor.test_caller()
Replace test_caller()
with a function from you canister
Lightic comes with builtin LedgerHelper
that can download and deploy ledger canister. In order to start using ledger:
const minter = u64IntoPrincipalId(0n);
const owner = u64IntoPrincipalId(1n);
const ledgerCanister = await LedgerHelper.defaults(context, minter, owner)
Now you can call ledger both from agent and from other deployed canisters. If possible this will deploy ledger with the same principal as it is found in IC ryjl3-tyaaa-aaaaa-aaaba-cai
Next call account_balance to check if the owner, has some ICP in its account
const balance = await ledgerCanister.balanceOf(minter)
If you need an account number from your principal there is a helper function getAccount
the will do just that
You can find examples of lightic used for testing canisters here: [https://github.com/icopen/evm_utils_ic/blob/master/tests/_common.mjs]. More examples to come.
It is possible to launch lightic in a standalone mode and call it from DFX or any other script that can work with DFX or mainnet. In order to do so:
npx lightic --p port
Where port is the desired TCP port on which the lightic should listen.
Most of the project was written in Type Script.
Util that can parse Candid compliant data and output it as a JSON formatted string
cd candid_util
wasm-pack build --target nodejs
Canister that uses some of the most common features of the IC, used for testing the mock replica
cd spec_test
cargo build --release --target wasm32-unknown-unknown
yarn prePublish
Below is a list of IC0 functions exposed to WASM module on IC environment. Not all calls will be implemented as part of this project.
- - msg_arg_data_size
- - msg_arg_data_copy
- - msg_caller_size
- - msg_caller_copy
- - msg_reject_code
- - msg_reject_msg_size
- - msg_reject_msg_copy
- - msg_reply_data_append
- - msg_reply
- - msg_reject
- - msg_cycles_available
- - msg_cycles_available128
- - msg_cycles_refunded
- - msg_cycles_refunded128
- - msg_cycles_accept
- - msg_cycles_accept128
- - canister_self_size
- - canister_self_copy
- - canister_cycle_balance
- - canister_cycle_balance128
- - canister_status
- - canister_version
- - msg_method_name_size
- - msg_method_name_copy
- - accept_message
- - call_new
- - call_on_cleanup
- - call_data_append
- - call_cycles_add
- - call_cycles_add128
- - call_perform
- - stable_size
- - stable_grow
- - stable_write
- - stable_read
- - stable64_size
- - stable64_grow
- - stable64_write
- - stable64_read
- - certified_data_set
- - data_certificate_present
- - data_certificate_size
- - data_certificate_copy
- - time
- - global_timer_set
- - performance_counter
- - debug_print
- - trap
- - mint_cycles
- - create_canister
- - update_settings
- - install_code
- - uninstall_code
- - canister_status
- - stop_canister
- - start_canisters
- - delete_canister
- - deposit_cycles
- - raw_rand
- - ecdsa_public_key
- - sign_with_ecdsa
- - http_request
- - provisional_create_canister_with_cycles
- - provisional_top_up_canister
- - Candid utils WASM-WASI module
- - WASM Module Loading
- - Assignment of Canister ID upon canister creation
- - Support for canister provided Candid Specs
- - Mocha/Jest integration
- - Canister Memory Rollback
- - Update call support
- - Stable Memory support
- - BLS Signatures
- - HTTP Server Implementation
- - npmjs package with helpers and runners
- - Compatibility and cooperation with dfx
- - Backup of wasm memory (normal and stable) for faster development cycles
- - Log of all calls
- - Support for other environments: local/dfx, production
- - Cycles usage counting per every call
- - Mocking of messages (ingress, egress and xnet)
- - Support for canister upgrades (preupgrade and postupgrade)
- - Limit cycle usage on calls
- - Limit message size to subnet settings, allow for different subnet settings
- - Support for multiple subnets
- - Full compliance ic-ref-test [https://github.com/dfinity/ic-hs#ic-ref-test-an-acceptance-test-suite]