Hello IC devs! I’m excited to share a project I’ve been developing over the past few months. It’s called ICE (short for IC Experience).
ICE is a task runner for the IC, similar to hardhat. It’s goal is to become something like the “React” of dev tooling for the IC. Allowing developers to share and depend on each others work more easily.
It’s available on npm:
npm i -S @ice.ts/runner @ice.ts/canisters
Getting Started
The main repository is available at:
Want to take it for a spin? Check out this example project with 26 canisters deployed using a single command:
Core Features
Type-Safety Throughout
No more hand-writing Candid strings. Everything in ICE is fully typed, giving you instant feedback on configuration errors.
NPM install canister
I’ve ported the most common canisters to npm, available in the @ice.ts/canisters
package. All complex setup steps have been abstracted away, allowing you to enable them with just a single line of code:
import {
InternetIdentity,
ICRC1Ledger,
DIP721,
Ledger,
DIP20,
CapRouter,
NNS,
CandidUI,
ICRC7NFT,
CyclesWallet,
CyclesLedger,
NFID,
} from "@ice.ts/canisters";
export const nns = NNS();
export const nfid = NFID();
export const icrc1 = ICRC1Ledger({ name: "My Test Token" });
Smart Defaults
ICE provides you convenient access to resources without requiring explicit configuration. Dependencies, environment variables, user identities, and network settings are automatically managed and made available in your tasks.
export const my_other_canister = motokoCanister({
src: "canisters/my_other_canister/main.mo",
})
.deps({ my_canister })
.installArgs(async ({ deps }) => {
const someVal = await deps.my_canister.actor.someMethod()
return [deps.my_canister.canisterId, someVal]
})
// Access to common resources through the context object
export const myTask = task("example")
.run(async ({ ctx }) => {
// Access user identities without manual setup
console.log(ctx.users.default.principal);
console.log(ctx.users.default.accountId);
// Access network configuration
console.log(ctx.network);
// Dynamically run other tasks
await ctx.runTask(anotherTask);
});
VSCode Extension
There’s also a VSCode extension that enhances your development experience by allowing you to:
- Run tasks directly from your editor with CodeLens
- View actor call results inline in your code
There’s also an experimental terminal UI available throught the CLI, but honestly, after using the VSCode extension, it’s hard to go back.
Design Philosophy
While ICE draws inspiration from tools like Hardhat, its designed from first principles with composability and the Internet Computer in mind.
Pure Data vs. Side Effects
Unlike Hardhat, which relies on APIs like extendEnvironment()
and task()
that produce side effects without returning values, ICE embraces a pure data approach:
// Hardhat: Side-effect-based API
task("compile", "Compiles the entire project")
.setAction(async () => { /* ... */ });
// ICE: Pure data approach
export const compile = task("compile")
.run(async () => { /* ... */ });
This pure data approach means ICE tasks are first-class citizens that can be passed around, composed, and nested infinitely.
References vs. Strings
Hardhat refers to tasks using string identifiers:
// Hardhat: String-based references
await hre.run("compile");
ICE uses direct references instead:
// ICE: Direct references
await ctx.runTask(compile);
This design choice brings several advantages:
- Type Safety: Your IDE can provide autocomplete and type checking
- Refactoring Support: Rename a task, and all references update automatically
- Better composability: Tasks and canisters may declare dependencies on other tasks
Roadmap
ICE is still in early development, and I’m actively working on several key features before it can be considered production-ready:
- Caching for improved performance
- Better handling of canister upgrades, reinstalls, etc.
- Mainnet deployment
- Enhanced context & configuration
- More canisters included out of the box
- Investigate Pocket-IC integration
- Rust, Azle, Kybra canister builders
- Comprehensive docs
- e2e tests
…and more
The long term goal is to revive my old projects (create-ic-app, connect2ic) and bring them under the ICE umbrella, but take them to the next level.
There’s still a long way to go, but hopefully I’ve managed to convey the power of this approach. I’m looking forward to your thoughts and feedback!