Tips for testing Motoko canisters with NNS and SNS neuron staking

Hi all,

While building out various components on https://vectors.neuronpool.com/, I quickly realised that comprehensive test suites were the key to make any of this work - we have ICP neuron staking, SNS neuron staking and CMC cycles minting and all of my canister code is in Motoko. The obvious solution is to make use of PicJS, without that none of this would have been possible - or at least testable.

Get the NNS state into pocket ic

The following guide is excellent https://js.icp.build/pic-js/latest/guides/working-with-the-nns/, one very important thing I would add, is if you want to test the latest version of the NNS and get the latest NNS features into your tests, you will have to specify the commit you want before you run the dfx extension, you can browse recent NNS proposals (or the IC repo directly) and get the commit hash from there. So the flow would actually be like:

export DFX_IC_COMMIT=<desired_commit_hash>
dfx extension run nns install

This will allow you to grab the latest NNS and test the latest features. You should compress your NNS state after this if you want others to be able to clone your repo and run the tests easily.

Loading it up

You can load up the NNS in your pocket ic server like so:

    let pic = await PocketIc.create(process.env.PIC_URL, {
      nns: {
        state: {
          type: SubnetStateType.FromPath,
          path: NNS_STATE_PATH,
        },
      }
    });

This adds the same NNS subnet with the same canister ID’s into your pocket ic server instance, meaning you don’t need to dynamically look for any of the canister ID’s. The production governance canister rrkah-fqaaa-aaaaa-aaaaq-cai on main net is the same as the one you get in the tests and the same goes for the others like the CMC and SNS-W.

Testing maturity

One of the challenges that comes to mind with testing ICP neurons was generating maturity across neurons, as you need create proposals and pass significant time. The way I figured it was to create a neuron directly in pocket IC and use this as the main followee for all other neurons and for creating proposals. This example repo was a great help: pic-js/examples/nns_proxy at main · dfinity/pic-js · GitHub. Then once your neurons from your canister or other are following your pocket ic neuron you can do something like:

    const response = await this.nnsActor.manage_neuron({
      id: [{ id: neuronId }],
      command: [
        {
          MakeProposal: {
            url: "",
            title: ["Oscar"],
            summary: "Golden Labrador Retriever",
            action: [
              {
                Motion: { motion_text: "Good Boy?" },
              },
            ],
          },
        },
      ],
      neuron_id_or_subaccount: [],
    });

And pass some time (like a week or so) and you will see maturity in your neurons.

Testing an SNS

I haven’t found an easy way to get SNS state into PicJS directly, but a handy workaround is to utilise the SNS-W from the NNS subnet - which is included in the NNS state we got from above. The SNS-W is responsible for spinning up new SNS canister suites like the SNS governance, ledger, index etc. I usually create the SNS-W actor, set the governance canister as the caller and chuck in a bunch of cycles like so:

    let snswActor = pic.createActor<SNSW>(snswIdlFactory, SNSW_CANISTER_ID);
    snswActor.setPrincipal(GOVERNANCE_CANISTER_ID);
    await pic.addCycles(SNSW_CANISTER_ID, 500000000000000);

Then later you can do something like:

    let sns = await snswActor.deploy_new_sns({
      sns_init_payload: [snsPayload],
    });

The payload being the specific SNS parameters you want to launch. This a good way to setup multiple (or even just one) SNS’s in the same pocket ic server and testing them.

Hope it was helpful

Hopefully these tips help speed things up for you. I’ve created 100+ tests across NNS, SNS, and CMC. I am thinking of offering the above as a type of service for projects. With the rise of AI motoko canister - I think tests will be crucial. Not a full on security audit, but more like integration NNS / SNS tests. If this interests you, I’d be eager to hear your use-case and discuss it with you.

5 Likes