Neuro: A Motoko package for basic staking and neuron management

Neuro is a a Motoko package that provides helper classes to simplify the code needed to stake and control your neurons in your motoko canisters.

https://mops.one/neuro

Recently, canisters on ICP have been given the permission to stake and control neurons on the NNS (pending protocol implementation). I want to avail of this feature in my own projects across different canisters and given the significant amount of boilerplate code required to set up seamless staking and management of neurons in Motoko, creating a package to abstract this process became necessary.

This package does not use HTTP outcalls; rather, it directly calls the governance interfaces. Currently, this only works with the SNS and has been tested with the OpenChat SNS. Once the NNS is updated, which I expect to be soon, the package will be updated to provide the same helper classes for staking neurons on the NNS. The functionality is already present in the code, but it is awaiting that release.

Package features:

  • Class-based design simplifies the code :white_check_mark:
  • Enables staking neurons in canisters with a single line of code :white_check_mark:
  • Interfaces for interacting with the governance frameworks :white_check_mark:
  • Interfaces for interacting with neurons :white_check_mark:
  • Stake neurons on the NNS :x: (Available, pending protocol implementation)
  • Control neurons on the NNS :x: (Available, pending protocol implementation)
  • Stake neurons on the SNS :white_check_mark:
  • Control neurons on the SNS :white_check_mark:

Code examples:

What’s nice about this package is that it also has helper functions to stake a neuron in a single line of code:

import { SNS } "mo:neuro";

// Stake a neuron on an SNS:
public func stake_sns_neuron() : async Result.Result<Blob, Text> {
  let sns = SNS.Governance({
    canister_id = Principal.fromActor(thisCanister);
    sns_canister_id = Principal.fromText("2jvtu-yqaaa-aaaaq-aaama-cai");
    sns_ledger_canister_id = Principal.fromText("2ouva-viaaa-aaaaq-aaamq-cai");
  });

  switch (await sns.stake({ amount_e8s = 400_000_000 })) {
    case (#ok result) {
      return #ok(result);
    };
    case (#err result) {
      return #err(result);
    };
  };
};

Below is a before-and-after example of interacting with a neuron using Neuro (illustrative purposes):

Before neuro:

        let SnsGovernance = actor ("2jvtu-yqaaa-aaaaq-aaama-cai") : SnsGovernanceInterface.Self;

        public func disburseMaturity({ neuron_id : NeuronId; percentage_to_disburse : Nat32; to_account :Types.SnsAccount }) : async SnsDisburseMaturityResult {
            let { command } = await SnsGovernance.manage_neuron({
                subaccount = neuron_id;
                command = ? #DisburseMaturity({
                    to_account = to_account;
                    percentage_to_disburse = percentage_to_disburse;
                });
            });

            let ?commandList = command else return #err("Failed to execute neuron command. Neuron ID: " # debug_show neuron_id);

            switch (commandList) {
                case (#DisburseMaturity result) {
                    return #ok(result);
                };
                case _ {
                    return #err("Command failed: " # debug_show commandList);
                };
            };
        };

After neuro:

public func disburse_maturity(id: Blob) : async Result.Result<NeuroTypes.SnsNeuronInformation, Text> {
  let neuron = SNS.Neuron({
    neuron_id = id;
    sns_canister_id = Principal.fromText("2jvtu-yqaaa-aaaaq-aaama-cai");
  });

  return await neuron.disburseMaturity({
            percentage_to_disburse = 100;
            to_account = ?<some account>;
        });
};

The goal:

The goal of this package is to provide the basic and necessary functions to help you stake and control neurons in canisters. It is not intended to include complex functionalities such as staking neurons on behalf of different users or trading neurons. However, you can fork or build upon this package for your own use cases.

It is also not designed to be a comprehensive governance interface and some governance and neuron management functions are intentionally missing. This is why the package is named “neuro” and not “neuron” — allowing someone else to create a more fully-featured package under that name. If you have suggestions or would like to contribute, pull requests are welcome.

Disclaimer

This package is a work in progress and has not undergone extensive testing and I expect some things to change. It is recommended to conduct your own research and thoroughly test the package before using it in your projects. Use this package at your own risk for the staking and management of neurons.

Feedback welcome!

I’m excited to use this package in my own projects and any feedback is welcome. The package has just been released on Mops and can be used on the SNS.

3 Likes

Thank you for expanding the Motoko ecosystem with this. Looks great

1 Like

Nice! I’m sure this will help anyone looking to take advantage of the unrestricted control of neurons. Thank you for the work.

1 Like