Skip to main content

Staking and neuron management

Intermediate
Rosetta
Tutorial

Overview

This document specifies extensions of the Rosetta API enabling staking funds and managing governance "neurons" on the Internet Computer.

Operations within a transaction are applied in order, so the order of operations is significant. Transactions that contain idempotent operations provided by this API can be re-tried within the 24-hour window.

Due to limitations of the governance canister, neuron management operations are not reflected on the chain. If you lookup transactions by identifier returned from the /construction/submit endpoint, these transactions might not exist or miss neuron management operations. Instead, /construction/submit returns the statuses of all the operations in the metadata field using the same format as /block/transaction would return.

Deriving neuron address

Since version1.3.0

Call the /construction/derive endpoint with metadata field account_type set to "neuron" to compute the ledger address corresponding to the neuron controlled by the public key.

Request

{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_key": {
"hex_bytes": "1b400d60aaf34eaf6dcbab9bba46001a23497886cf11066f7846933d30e5ad3f",
"curve_type": "edwards25519"
},
"metadata": {
"account_type": "neuron",
"neuron_index": 0
}
}

Since version 1.3.0, you can control many neurons using the same key. You can differentiate between neurons by specifying different values of the neuron_index metadata field. The rosetta node supports neuron_index in all neuron management operations. neuron_index is an arbitrary integer between 0 and 264 - 1 (18446744073709551615). It is equal to zero if not specified. If you use JavaScript to construct requests to the Rosetta node, consider using the BigInt type to represent the neuron_index. The Number type can precisely represent only values below 253 - 1 (9007199254740991).

Response

{
"account_identifier": {
"address": "531b163cd9d6c1d88f867bdf16f1ede020be7bcd928d746f92fbf7e797c5526a"
}
}

Stake funds

Since version1.0.5
Idempotent?yes

To stake funds, execute a transfer to the neuron address followed by a STAKE operation.

The only field that you must set for the STAKE operation is account, which should be equal to the ledger account of the neuron controller. You can specify neuron_index field in the metadata field of the STAKE operation. If you do specify the neuron_index, its value must be the same as you used to derive the neuron account identifier.

Request

{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101",
},
"operations": [
{
"operation_identifier": { "index": 0 },
"type": "TRANSACTION",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"amount": {
"value": "-100000000",
"currency": { "symbol": "ICP", "decimals": 8 }
}
},
{
"operation_identifier": { "index": 1 },
"type": "TRANSACTION",
"account": { "address": "531b163cd9d6c1d88f867bdf16f1ede020be7bcd928d746f92fbf7e797c5526a" },
"amount": {
"value": "100000000",
"currency": { "symbol": "ICP", "decimals": 8 }
}
},
{
"operation_identifier": { "index": 2 },
"type": "FEE",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"amount": {
"value": "-10000",
"currency": { "symbol": "ICP", "decimals": 8 }
}
},
{
"operation_identifier": { "index": 3 },
"type": "STAKE",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0
}
}
]
}

Response

{
"transaction_identifier": {
"hash": "2f23fd8cca835af21f3ac375bac601f97ead75f2e79143bdf71fe2c4be043e8f"
},
"metadata": {
"operations": [
{
"operation_identifier": { "index": 0 },
"type": "TRANSACTION",
"status": "COMPLETED",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"amount": {
"value": "-100000000",
"currency": { "symbol": "ICP", "decimals": 8 }
}
},
{
"operation_identifier": { "index": 1 },
"type": "TRANSACTION",
"status": "COMPLETED",
"account": { "address": "531b163cd9d6c1d88f867bdf16f1ede020be7bcd928d746f92fbf7e797c5526a" },
"amount": {
"value": "100000000",
"currency": { "symbol": "ICP", "decimals": 8 }
}
},
{
"operation_identifier": { "index": 2 },
"type": "FEE",
"status": "COMPLETED",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"amount": {
"value": "-10000",
"currency": { "symbol": "ICP", "decimals": 8 }
}
},
{
"operation_identifier": { "index": 3 },
"type": "STAKE",
"status": "COMPLETED",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0
}
}
]
}
}

Managing neurons

Setting dissolve timestamp

Since version1.1.0
Idempotent?yes
Minimal access levelcontroller

This operation updates the time when the neuron can reach the DISSOLVED state.

Prerequisites:

  • account.address is the ledger address of the neuron contoller.

The dissolve timestamp always increases monotonically.

  • If the neuron is in the DISSOLVING state, this operation can move the dissolve timestamp further into the future.

  • If the neuron is in the NOT_DISSOLVING state, invoking SET_DISSOLVE_TIMESTAMP with time T will attempt to increase the neuron’s dissolve delay (the minimal time it will take to dissolve the neuron) to T - current_time.

  • If the neuron is in the DISSOLVED state, invoking SET_DISSOLVE_TIMESTAMP will move it to the NOT_DISSOLVING state and will set the dissolve delay accordingly.

Example

{
"operation_identifier": { "index": 4 },
"type": "SET_DISSOLVE_TIMESTAMP",
"account": {
"address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d"
},
"metadata": {
"neuron_index": 0,
"dissolve_time_utc_seconds": 1879939507
}
}

Start dissolving

Since version1.1.0
Idempotent?yes
Minimal access levelcontroller

The START_DISSOLVNG operation changes the state of the neuron to DISSOLVING.

Prerequisites:

  • account.address is the ledger address of the neuron contoller.

Postconditions:

  • The neuron is in the DISSOLVING state.

Example

{
"operation_identifier": { "index": 5 },
"type": "START_DISSOLVING",
"account": {
"address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d"
},
"metadata": {
"neuron_index": 0
}
}

Stop dissolving

Since version1.1.0
Idempotent?yes
Minimal access levelcontroller

The STOP_DISSOLVNG operation changes the state of the neuron to NOT_DISSOLVING.

Prerequisites:

  • account.address is a ledger address of a neuron contoller.

Postconditions:

  • The neuron is in NOT_DISSOLVING state.

Example

{
"operation_identifier": { "index": 6 },
"type": "STOP_DISSOLVING",
"account": {
"address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d"
},
"metadata": {
"neuron_index": 0
}
}

Adding hotkeys

Since version1.2.0
Idempotent?yes
Minimal access levelcontroller

The ADD_HOTKEY operation adds a hotkey to the neuron. The Governance canister allows some non-critical operations to be signed with a hotkey instead of the controller’s key (e.g., voting and querying maturity).

Prerequisites:

  • account.address is a ledger address of a neuron controller.

  • The neuron has less than 10 hotkeys.

The command has two forms: one form accepts an ICP principal as a hotkey, another form accepts a public key.

Add a principal as a hotkey

{
"operation_identifier": { "index": 0 },
"type": "ADD_HOTKEY",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0,
"principal": "sp3em-jkiyw-tospm-2huim-jor4p-et4s7-ay35f-q7tnm-hi4k2-pyicb-xae"
}
}

Add a public key as a hotkey

{
"operation_identifier": { "index": 0 },
"type": "ADD_HOTKEY",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0,
"public_key": {
"hex_bytes": "1b400d60aaf34eaf6dcbab9bba46001a23497886cf11066f7846933d30e5ad3f",
"curve_type": "edwards25519"
}
}
}

Removing hotkeys

Since version1.2.0
Idempotent?yes
Minimal access levelcontroller

The REMOVE_HOTKEY operation remove a previously added hotkey from the neuron.

Prerequisites:

  • account.address is a ledger address of a neuron controller.

  • The hotkey is linked to the neuron.

The command has two forms: one form accepts an ICP principal as a hotkey, another form accepts a public key.

Remove a principal as a hotkey

{
"operation_identifier": { "index": 0 },
"type": "REMOVE_HOTKEY",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0,
"principal": "sp3em-jkiyw-tospm-2huim-jor4p-et4s7-ay35f-q7tnm-hi4k2-pyicb-xae"
}
}

Remove a public key as a hotkey

{
"operation_identifier": { "index": 0 },
"type": "REMOVE_HOTKEY",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0,
"public_key": {
"hex_bytes": "1b400d60aaf34eaf6dcbab9bba46001a23497886cf11066f7846933d30e5ad3f",
"curve_type": "edwards25519"
}
}
}

Spawn neurons

Since version1.3.0
Idempotent?yes
Minimal access levelcontroller

The SPAWN operation creates a new neuron from an existing neuron with enough maturity. This operation transfers all the maturity from the existing neuron to the staked amount of the newly spawned neuron.

Prerequisites:

  • account.address is a ledger address of a neuron controller.

  • The parent neuron has at least 1 ICP worth of maturity.

Postconditions:

  • Parent neuron maturity is set to 0.

  • A new neuron is spawned with a balance equal to the transferred maturity.

{
"operation_identifier": { "index": 0 },
"type": "SPAWN",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0,
"controller": {
"principal": "sp3em-jkiyw-tospm-2huim-jor4p-et4s7-ay35f-q7tnm-hi4k2-pyicb-xae"
},
"spawned_neuron_index": 1,
"percentage_to_spawn": 75
}
}
  • spawned_neuron_index metadata field is required. The rosetta node uses this index to compute the subaccount for the spawned neuron. All spawned neurons must have different values of spawned_neuron_index.

  • controller metadata field is optional and equal to the existing neuron controller by default.

  • percentage_to_spawn metadata field is optional and equal to 100 by default. If specified, the value must be an integer between 1 and 100 (bounds included).

Stake neuron maturity

Since version1.4.0
Idempotent?no
Minimal access levelcontroller

The STAKE_MATURITY operation stakes the existing maturity of the neuron into its stake. The percentage of maturity to stake can be specified, otherwise the entire maturity is staked.

Prerequisites:

  • account.address is the ledger address of the neuron controller.

  • The neuron has non-zero maturity to stake.

Postconditions:

  • Maturity decreased by the amount staked.

  • Neuron stake increased by the amount staked.

Example

{
"operation_identifier": { "index": 0 },
"type": "STAKE_MATURITY",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0,
"percentage_to_stake": 14
}
}

percentage_to_stake metadata field is optional and equal to 100 by default. If specified, the value must be an integer between 1 and 100 (bounds included).

Follow neurons

Since version1.5.0
Idempotent?yes
Minimal access levelhotkey

The FOLLOW operation sets a follow rule for a neuron. The Governance canister smart contract will automatically deduce the vote of the following neurons from the votes of the followees during the voting.

The followees metadata field contains the list of neurons to follow. If the list contains more than one neuron, the neuron will vote according to the majority of followed neurons votes (or abstain in case of draw). If the list is empty, the rule for this topic will be discarded (i.e., the neuron will not follow any other neuron for proposals of this type).

You can restrict the rule to a specific topic by specifying the topic metadata field. The topic is an integer between 0 and 10 (inclusive). The default value for the topic field is 0. Each topic can have at most one rule associated with it. The topic codes are listed below.

  1. Undefined (all topics).
  2. Neuron management.
  3. Exchange rate.
  4. Network economics.
  5. Governance.
  6. Node administration.
  7. Participant management.
  8. Subnet management.
  9. Network canister management.
  10. KYC.
  11. Node provider rewards.

Prerequisites:

  • account.address is the ledger address of the neuron controller or hotkey.
  • metadata.followees contains an array of valid neuron identifiers.
  • metadata.topic is a valid topic identifier.

Postconditions:

  • Neuron votes according to specified follow rule.

Calling FOLLOW as a controller:

{
"operation_identifier": { "index": 0 },
"type": "FOLLOW",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"topic": 0,
"followees": [4169717477823915596, 7814871076665269296],
"neuron_index": 0
}
}

Calling FOLLOW with a hotkey:

{
"operation_identifier": { "index": 0 },
"type": "FOLLOW",
"account": { "address": "8af54f1fa09faeca18d294e0787346264f9f1d6189ed20ff14f029a160b787e8" },
"metadata": {
"topic": 0,
"followees": [4169717477823915596, 7814871076665269296],
"neuron_index": 0,
"controller": {
"public_key": {
"hex_bytes": "ba5242d02642aede88a5f9fe82482a9fd0b6dc25f38c729253116c6865384a9d",
"curve_type": "edwards25519"
}
}
}
}

The followees metadata field contains list of unique neuron identifiers assigned by the Governance canister smart contract, not the list of neuron indices chosen by the caller. You can obtain unique neuron identifiers of you your neurons from the neuron_id metadata field of the STAKE and NEURON_INFO operations.

Accessing neuron attributes

Accessing public information

Since version1.3.0
Minimal access levelpublic

Call the /account/balance endpoint to access the staked amount and publicly available neuron metadata.

Prerequisites:

  • public_key contains the public key of a neuron’s controller.
  • This operation is available only in online mode.

  • The request should not specify any block identifier because the endpoint always returns the latest state of the neuron.

Request

{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"account_identifier": {
"address": "a4ac33c6a25a102756e3aac64fe9d3267dbef25392d031cfb3d2185dba93b4c4"
},
"metadata": {
"account_type": "neuron",
"neuron_index": 0,
"public_key": {
"hex_bytes": "ba5242d02642aede88a5f9fe82482a9fd0b6dc25f38c729253116c6865384a9d",
"curve_type": "edwards25519"
}
}
}

Response

{
"block_identifier": {
"index": 1150,
"hash": "ca02e34bafa2f58b18a66073deb5f389271ee74bd59a024f9f7b176a890039b2"
},
"balances": [
{
"value": "100000000",
"currency": {
"symbol": "ICP",
"decimals": 8
}
}
],
"metadata": {
"verified_query": false,
"retrieved_at_timestamp_seconds": 1639670156,
"state": "DISSOLVING",
"age_seconds": 0,
"dissolve_delay_seconds": 240269355,
"voting_power": 195170955,
"created_timestamp_seconds": 1638802541
}
}

Accessing protected information

Since version1.5.0
Idempotent?yes
Minimal access levelhotkey

The NEURON_INFO operation retrieves the state of the neuron from the governance canister, including protected fields such as maturity. This operation does not change the state of the neuron. Either the neuron controller or a hotkey can execute this operation.

Calling NEURON_INFO as a controller:

{
"operation_identifier": { "index": 0 },
"type": "NEURON_INFO",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0
}
}
  • account.address is the ledger address of the neuron controller.

Calling NEURON_INFO with a hotkey:

{
"operation_identifier": { "index": 0 },
"type": "NEURON_INFO",
"account": { "address": "8af54f1fa09faeca18d294e0787346264f9f1d6189ed20ff14f029a160b787e8" },
"metadata": {
"neuron_index": 0,
"controller": {
"public_key": {
"hex_bytes": "ba5242d02642aede88a5f9fe82482a9fd0b6dc25f38c729253116c6865384a9d",
"curve_type": "edwards25519"
}
}
}
}
  • account.address is the ledger address of the neuron hotkey.
  • metadata.controller.public_key is the public key of the neuron controller.

Since Rosetta API identifies neurons by the controller’s public key and neuron index, the caller has to specify the public key when executing the operation using a hotkey.

The Rosetta API returns the state of the neuron as operation metadata in the /construction/submit endpoint.

Example response from the /construction/submit endpoint:

{
"transaction_identifier": {
"hash": "0000000000000000000000000000000000000000000000000000000000000000"
},
"metadata": {
"operations": [
{
"operation_identifier": { "index": 0 },
"type": "NEURON_INFO",
"status": "COMPLETED",
"account": {
"address": "8af54f1fa09faeca18d294e0787346264f9f1d6189ed20ff14f029a160b787e8"
},
"metadata": {
"controller": {
"principal": "sp3em-jkiyw-tospm-2huim-jor4p-et4s7-ay35f-q7tnm-hi4k2-pyicb-xae"
},
"followees": [
"0": [111, 222],
"8": [555, 666]
],
"hotkeys": [
"6xpcx-hldf5-4ddrg-onbug-4e2kw-rc25g-rxknf-p2wij-hxhqj-azcii-oqe"
],
"kyc_verified": true,
"maturity_e8s_equivalent": 1000,
"neuron_fees_e8s": 0,
"neuron_id": 18089972080608815000,
"neuron_index": 0,
"state": "DISSOLVING"
}
}
]
}
}