How to Submit an NNS motion Proposal (WIP)

I am working on the easy-to-follow version, but I thought I would at least put the basic steps for the more technical of you.

Caveat: these are my own personal notes and not very user-friendly.

1. Create a neuron with a dissolve delay and link itg to dfx (you do this step only once)

  • I created this neuron using keysmith quill and dfx

a. Install the keysmith:
b. Create seed phrase: $ keysmith generate -o seed.txt
c. Create private.pem: $ keysmith private-key -o private.pem
d. Create Ledger account: $ keysmith account -i 0

1.1. Install that key into dfx and create an identity (in my case it was called “proposals”) on dfx:

a. $ dfx identity import proposals private.pem

1.2 Find the public IDs on quill and dfx to ensure the principals are the same

a. With quill installed, go to: $ target/release/quill --pem-file private.pem public-ids
b. $ dfx --identity proposals ledger account-id

1.3 Send at least 1 ICP to the account in 1.2 (to create the neuron)

1.4 Now that your account has ICP, use quill to spawn a neuron

$ target/release/quill --pem-file private.pem neuron-stake --name $NAME --amount $AMOUNT > message.json
$ target/release/quill send message.json

  • this spawned neuron: 148885932052941245

1.5 Increase neuron dissolve to at least 6 months with quill

  • One year: 31557600
    $ quill --pem-file private.pem neuron-manage 148885932052941245 --additional-dissolve-delay-seconds 31557600 > message.json
    $ target/release/quill send message.json

  • in my case, the neuron is 5241875388871980017

2. Send a message to the governance canister using dfx’s ability to send messages (you do this every time you want to send a proposal):

*Once I set up everything in step #1, I only do step #2 every time I want to create a motion proposal

$ dfx --identity proposals canister --network ic --no-wallet call rrkah-fqaaa-aaaaa-aaaaq-cai manage_neuron '(record {id = null; command=opt variant {MakeProposal=record {url="XXXXXXXXXXXX"; action=opt variant {Motion=record {motion_text="XXXXXXXXXXXX"}}; summary="XXXXXXXXXXXX"}}; neuron_id_or_subaccount=opt variant {NeuronId=record {id=XXXXXXXXXXXX:nat64}}})'

  • actual dfx command I used:

$ dfx --identity proposals canister --network ic --no-wallet call rrkah-fqaaa-aaaaa-aaaaq-cai manage_neuron '(record {id = null; command=opt variant {MakeProposal=record {url=""; title=”In the NNS Frontend dapp, add cycle_dao’s neuron (5967494994762486275) so people can follow cycle_dao”;action=opt variant {Motion=record {motion_text=""}}; summary=”As part of liquid democracy, neuron holders can choose to follow the voting choices of other neurons. This can be done manually, but a very common and consumer-friendly way is to use the ‘follow neuron’ functionality in the NNS Frontend dapp. To further decentralization, this proposal is to add cycle_dao’s neuron: 5967494994762486275.”}}; neuron_id_or_subaccount=opt variant {NeuronId=record {id=5241875388871980017:nat64}}})'


Caveat: careful with formatting mistakes from copy/paste in regards to " " characters

This is the payload I used for this NNS Motion proposal: Internet Computer Network Status

Food for thought: I did a lot of playing around with the Candid web UI to find the best way for me to get comfortable.

Is it necessary to follow step 1 to create a neuron that has permission to make a proposal? Is it possible to use a neuron that was created with the NNS app? @diegop

I set up the neuronID using dfx, quill, and keysmith and can see the neuron in with over 1 ICP and a dissolve delay of 1 year. When I run the call below in the Candid UI, I get the error message below. What else needs to be done to enable this neuron to make a motion proposal?

manage_neuron(record {id=null; command=opt variant {MakeProposal=record {url=""; title=opt “This is a test title”; action=opt variant {Motion=record {motion_text=""}}; summary=“This is a test summary”}}; neuron_id_or_subaccount=opt variant {NeuronId=record {id=12008772471346176261}}})

(record {command=opt variant {Error=record {error_message=“Caller not authorized to propose.”; error_type=3}}})

The error is because the candid ui always sends calls as the anonymous caller. To call the manage_neuron method is with the caller/identity that controls the neuron. In Diego’s case he is using the identity he created called ‘proposals’

1 Like

So just to make sure I understand, this error message will always occur when trying to call manage_neuron with Candid UI because I can’t send it (and therefore authorize it) with the identity that I created for this neuron ID. Basically, it sounds like I have come as far as possible with testing short of submitting an actual proposal and losing 1 ICP for the test because of a Rejected proposal. Does that sound about right or are there other ways of testing the manage_neuron command with my neuron and identity without actually making a proposal? I suppose this may be the reason why several people have submitted test proposals in the past. @levi

Sounds right, I dont know of a way to test it without making a live one. maybe on a local replica if you can get the governance canister running on it.

I will be honest, I actually created a live one while testing :slight_smile: since I started creating them a few months ago when the IC was still baking, and it was my job to actually help write the docs.

Of course, it cost me ICP, but i figured “hey that is the cost of sending proposals”.

Could I have started up a local version of the Governance Canister? I guess so, but i found it easier to do this for testing:

  1. Craft the message via the Candid UI of Governance Canister: DFINITY Canister Candid UI
  • this allowed me to type check the arguments and inputs even if it the message did not work for lack of authorization or neuron with ICP
  1. Once I was relatively certain of the arguments I wanted, I then sent a real proposal with a neuron that had a few ICP for me
  • Personally (and this is indicative of how early I was in the documentation process), the whole experience took me a few hours and cost me maybe 2 ICP.

(fwiw yes I actually did buy ICP on Coinbase in order to have a “start from scratch” experience and identify any pain points)

1 Like

@diegop unless I’m missing something, the title in step 1.2 ("… ensure the principals are the same") should be to ensure the account IDs are the same because $ dfx --identity proposals ledger account-id only returns the account ID, right?

1 Like

You may have figured this out in the last 25 days, but you can add a hot key to your NNS neuron and issue manage_neuron calls from it.

Yes you are right those only return account IDs. I must have meant “check accounts”. I need to do a review of the intent, but quick glance leads me to believe you are right.

1 Like

I believe this is correct, but I have not tested this.

Update: I am moving this to the wiki and cleaning up certain parts:



This page is much cleaner. Please use the wiki: How-To: Create an NNS motion proposal - Internet Computer Wiki


Keysmith was just deprecated, per the README - GitHub - dfinity/keysmith: Hierarchical Deterministic Key Derivation for the Internet Computer

Can you update the wiki to reflect this?

Yes will do. Good catch. That should make things simpler.

I would like to submit some suggestions, but have encountered some problems.

dfx --identity proposals canister --network ic --no-wallet call rrkah-fqaaa-aaaaa-aaaaq-cai manage_neuron '(record {id = null; command=opt variant {MakeProposal=record {url=""; title="$TITLE";action=opt variant {Motion=record {motion_text="$MOTION_TEXT"}}; summary="


"}}; neuron_id_or_subaccount=opt variant {NeuronId=record {id=$NEURON_ID:nat64}}})’

Here is the command to initiate a proposal using dfx.
I need to replace $SUMMARY with proposal content,But the proposals are multi-line, so I can’t enter them in one command. I can only use ‘\n’ to replace all newlines in the proposal. Is there any way to make it simpler

Hmmm I submit multi like proposals all the time by copy pasting from a markdown file. Would it help you if I paste the exact command I used?

dfx --identity proposals canister --network ic --no-wallet call rrkah-fqaaa-aaaaa-aaaaq-cai manage_neuron ‘(record {id = null; command=opt variant {MakeProposal=record {url=“如何在nns发起动议提案-fca251185ab4”; title="$(";action=opt variant {Motion=record {motion_text="$("}}; summary="$("}}; neuron_id_or_subaccount=opt variant {NeuronId=record {id=16392997059792243989:nat64}}})’