Staking and neuron management
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 smart contract, 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 version |
1.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 ).
|
Stake funds
Since version |
1.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 version |
1.1.0 |
Idempotent? |
yes |
Minimal access level |
controller |
This operation updates the time when the neuron can reach the DISSOLVED
state.
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, invokingSET_DISSOLVE_TIMESTAMP
with time T will attempt to increase the neuron’s dissolve delay (the minimal time it will take to dissolve the neuron) toT - current_time
. -
If the neuron is in the
DISSOLVED
state, invokingSET_DISSOLVE_TIMESTAMP
will move it to theNOT_DISSOLVING
state and will set the dissolve delay accordingly.
-
account.address
is the ledger address of the neuron contoller.
{
"operation_identifier": { "index": 4 },
"type": "SET_DISSOLVE_TIMESTAMP",
"account": {
"address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d"
},
"metadata": {
"neuron_index": 0,
"dissolve_time_utc_seconds": 1879939507
}
}
Start dissolving
Since version |
1.1.0 |
Idempotent? |
yes |
Minimal access level |
controller |
The START_DISSOLVNG
operation changes the state of the neuron to DISSOLVING
.
-
account.address
is the ledger address of the neuron contoller.
-
The neuron is in the
DISSOLVING
state.
{
"operation_identifier": { "index": 5 },
"type": "START_DISSOLVING",
"account": {
"address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d"
},
"metadata": {
"neuron_index": 0
}
}
Stop dissolving
Since version |
1.1.0 |
Idempotent? |
yes |
Minimal access level |
controller |
The STOP_DISSOLVNG
operation changes the state of the neuron to NOT_DISSOLVING
.
-
account.address
is a ledger address of a neuron contoller.
-
The neuron is in
NOT_DISSOLVING
state.
{
"operation_identifier": { "index": 6 },
"type": "STOP_DISSOLVING",
"account": {
"address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d"
},
"metadata": {
"neuron_index": 0
}
}
Adding hotkeys
Since version |
1.2.0 |
Idempotent? |
yes |
Minimal access level |
controller |
The ADD_HOTKEY
operation adds a hotkey to the neuron.
The Governance canister smart contract allows some non-critical operations to be signed with a hotkey instead of the controller’s key (e.g., voting and querying maturity).
-
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 IC 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"
}
}
}
Spawn neurons
Since version |
1.3.0 |
Idempotent? |
yes |
Minimal access level |
controller |
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.
-
account.address
is a ledger address of a neuron controller. -
The parent neuron has at least 1 ICP worth of maturity.
-
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": "sp3em-jkiyw-tospm-2huim-jor4p-et4s7-ay35f-q7tnm-hi4k2-pyicb-xae",
"spawned_neuron_index": 1
}
}
|
Merge neuron maturity
Since version |
1.4.0 |
Idempotent? |
no |
Minimal access level |
controller |
The MERGE_MATURITY
operation merges the existing maturity of the neuron into its stake.
The percentage of maturity to merge can be specified, otherwise the entire maturity is merged.
-
account.address
is the ledger address of the neuron controller. -
The neuron has non-zero maturity to merge.
-
Maturity decreased by the amount merged.
-
Neuron stake increased by the amount merged.
{
"operation_identifier": { "index": 0 },
"type": "MERGE_MATURITY",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0,
"percentage_to_merge": 14
}
}
percentage_to_merge metadata field is optional and equal to 100 by default.
If specified, the value must be an integer between 1 and 100 (bounds included).
|
Accessing neuron attributes
Accessing public information
Since version |
1.3.0 |
Minimal access level |
public |
Call the /account/balance
endpoint to access the staked amount and publicly available neuron metadata.
-
public_key
contains the public key of a neuron’s controller.
|
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 version |
1.5.0 |
Idempotent? |
yes |
Minimal access level |
hotkey |
The NEURON_INFO
operation retrieves the state of the neuron from the governance canister smart contract, 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.
-
account.address
is the ledger address of the neuron controller or hotkey.
NEURON_INFO
as a controller:{
"operation_identifier": { "index": 0 },
"type": "NEURON_INFO",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0
}
}
NEURON_INFO
with a hotkey:{
"operation_identifier": { "index": 0 },
"type": "NEURON_INFO",
"account": { "address": "8af54f1fa09faeca18d294e0787346264f9f1d6189ed20ff14f029a160b787e8" },
"metadata": {
"neuron_index": 0,
"controller": {
"hex_bytes": "ba5242d02642aede88a5f9fe82482a9fd0b6dc25f38c729253116c6865384a9d",
"curve_type": "edwards25519"
}
}
}
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.
{
"transaction_identifier": {
"hash": "0000000000000000000000000000000000000000000000000000000000000000"
},
"metadata": {
"operations": [
{
"operation_identifier": { "index": 0 },
"type": "NEURON_INFO",
"status": "COMPLETED",
"account": {
"address": "8af54f1fa09faeca18d294e0787346264f9f1d6189ed20ff14f029a160b787e8"
},
"metadata": {
"controller": "sp3em-jkiyw-tospm-2huim-jor4p-et4s7-ay35f-q7tnm-hi4k2-pyicb-xae",
"kyc_verified": true,
"maturity_e8s_equivalent": 1000,
"neuron_fees_e8s": 0,
"neuron_id": 18089972080608815000,
"neuron_index": 0,
"state": "DISSOLVING"
}
}
]
}
}