Working with the NNS
If your canister interacts with the NNS, you'll need to setup the NNS state in your tests. If you've already setup a full NNS state locally using the DFX NNS extension then you'll know that this can take a while to complete.
Fortunately, when writing tests with PocketIC you can restore the subnet state from a directory, which is much faster. This guide will walk through the process of creating this state directory for use in your tests.
Note: this guide has been tested with dfx v0.26.0. It is not guaranteed to work with other dfx versions.
Project setup
To not impact any other projects running on DFX, let's create a new project:
dfx new --no-frontend --type motoko nns_state
Change into the newly created directory:
cd nns_state
Add a local system network to the dfx.json
file. This will create the appropriate network configuration for the NNS without affecting any other projects that are running on DFX:
{
// redacted...
"networks": {
"local": {
"bind": "127.0.0.1:8080",
"type": "ephemeral",
"replica": {
"subnet_type": "system"
}
}
}
// redacted...
}
Stop DFX if it is already running:
dfx stop
Start DFX with a clean network in the background:
dfx start --background --clean --artificial-delay 0
Install the NNS extension for DFX:
dfx extension install nns
Now, use this extension to setup the NNS. This can take up to a few minutes to complete:
dfx extension run nns install
Once you see
######################################
# NNS CANISTER INSTALLATION COMPLETE #
######################################
run
dfx stop
The subnet state is now stored in the .dfx/network/local/state/replicated_state
directory. First find a subfolder of that directory containing many canister states:
ls $(pwd)/.dfx/network/local/state/replicated_state/**/**/canister_states
Then save the absolute path to that subfolder into a global variable for use later:
export NNS_STATE_PATH=$(pwd)/.dfx/network/local/state/replicated_state/46bab453650b3f22d11f0ffe4d3057b855dd752f95eeccc69da5531e94598e2b
Copying the NNS state
Set the root path where you want to copy the NNS state to:
export TARGET_PATH=/path/to/tests
Copy the NNS state into your project:
mkdir -p $TARGET_PATH && cp -r $NNS_STATE_PATH $TARGET_PATH/nns_state/
The state directory includes a lot of files, so if you don't want to commit all of them to your repository, you can compress the state directory and commit the archive instead.
First, change to the directory containing the state:
cd $TARGET_PATH
Then compress the state directory:
tar -Jcvf nns_state.tar.xz nns_state
Then when you need to use the state, you can decompress it from the root of your repository with:
tar -xvf path/to/tests/state/nns_state.tar.xz -C path/to/tests/state
This could be done with an npm
postinstall
script, by adding the following to your package.json
:
{
// redacted...
"scripts": {
"postinstall": "tar -xvf path/to/tests/state/nns_state.tar.xz -C path/to/tests/state"
// redacted...
}
// redacted...
}
Using the NNS state in your tests
You'll need to reference the path to the NNS state:
const NNS_STATE_PATH = resolve(__dirname, '..', 'state', 'nns_state');
Now you can setup your PocketIC instance to use the NNS state:
const pic = await PocketIc.create({
nns: {
state: {
type: SubnetStateType.FromPath,
path: NNS_STATE_PATH,
},
},
});
After creating the instance, make sure to set the PocketIc time to be the same or greater than the time that you created the NNS state:
await pic.setTime(new Date(2025, 4, 29).getTime());
await pic.tick();
Check out the NNS Proxy
example for a full example of how to use the NNS state in your tests.