ICP Rosetta staking API reference
This section will give you an overview of how ICP staking on the Internet Computer using Rosetta works.
This guide assumes you are familiar with the flow of operations.
- Prerequisites
http://127.0.0.1:8081
You are familiar with the concept of staking on the NNS in general.Neuron lifecycle
A neuron is a type of asset managed by the Internet Computer governance canister. Neurons allow their controllers to participate in the governance of the network by submitting and voting on proposals.
Creating a neuron
Transferring some amount of ICPs from any ledger account to the neuron’s address. Neuron address on the ledger that belongs to the Governance canister. The caller computes the neuron address from two parts:
The principal of the governance canister.
The subaccount that is itself a hash of the controller’s principal with an integer nonce. One principal can control multiple neurons by picking different integer nonces for the subaccount computation.
Creating a neuron involves calling the claim_or_refresh_neuron_from_account
method of the governance canister. If the call is successful, the payload contains the neuron ID, a numeric identifier of the newly created neuron. The governance canister maintains a mapping between neuron IDs and subaccounts. Most management operations can be performed using either the neuron ID or the subaccount address.
Neuron attributes
A neuron has the following attributes that control its lifecycle and rewards distribution:
Dissolve delay
Dissolve delay is how long it will take a neuron to become "liquid" once it starts dissolving. If the neuron is already dissolving, the delay indicates the amount of time left before the neuron transitions to the DISSOLVED state. The dissolve delay of a newly created neuron is 0. Only neurons with a dissolve delay of at least 15778800 seconds (approximately 6 months) can be used to vote on proposals (as of 2021-07-27, the exact delay requirements are subject to change). Once set, the dissolve delay cannot be reduced via management commands.
Dissolve state
NOT_DISSOLVING
A neuron has a fixed dissolve delay and accrues age. This is the default state of a newly created neuron.
DISSOLVING
A neuron’s dissolve delay decreases with the passage of time. Once the dissolve delay becomes 0, the neuron transitions to the DISSOLVED state, and the staked ICPs can be disbursed.
DISSOLVED
A neuron’s dissolve delay is 0. The neuron holder is free to either disburse the staked ICPs or increase the dissolve delay, which will cause a transition to the NOT_DISSOLVING state.
Age
Age is the amount of time that passed since the last time the neuron transitioned to the NOT_DISSOLVING state. If the neuron is DISSOLVING, its age is 0.
Maturity
Maturity is the total amount of unspent rewards accrued by this neuron. A neuron’s controller can spend maturity in two ways:
Spawn a new neuron with a dissolve delay of seven days and stake equal to the maturity.
Merge maturity into the stake, increasing voting power.
Voting power
Voting power is proportional to the amount of staked ICP. Age and dissolve delay multiplicative bonuses increase the voting power if the neuron is in the NOT_DISSOLVING state.
Rewards
Once a proposal is accepted, all the neurons that voted on that proposal receive rewards in the form of a maturity increase. The governance canister distributes rewards daily. The rewards are proportional to the voting power.
For more information on neuron state transitions and rewards, see understanding the Internet Computer’s Network Nervous System, neurons, and ICP utility tokens.
State transitions
stateDiagram-v2 state "Non-dissolving Neuron" as non_dissolving state "Dissolving Reward Neuron" as dissolving state "Dissolved Reward Neuron" as dissolved [*] --> non_dissolving : transfer + claim_or_refresh_neuron_from_account non_dissolving --> non_dissolving : increase_dissolve_delay non_dissolving --> dissolving : start_dissolving dissolving --> non_dissolving : stop_dissolving dissolving --> dissolved : passage of time dissolved --> non_dissolving: increase_dissolve_delay dissolved --> [*] : disburse
Change auto-stake-maturity
Since version | 1.7.0 |
Idempotent? | Yes |
Minimal access level | controller |
If you do not want to manually have to stake the maturity that you acquired, you can set your neuron to automatically stake new maturity.
You can always deactivate this feature by calling it with the boolean false
.
An example command for this feature resembles the following:
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '
{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "CHANGE_AUTO_STAKE_MATURITY",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"neuron_index": 0,
"set_auto_stake_maturity": true
}
}
],
"metadata": null
}'
Derive neuron ID
Since version | 1.3.0 |
Idempotent? | no |
Minimal access level | controller |
Staking requires transferring tokens to a sub-account of the governance canister. To complete the transfer, you need to know both your ICP ledger account identifier (source of the transfer) and the account identifier of the governance sub-account (destination of the transfer). Make sure you have enough ICP in your source account to make the transfer to the governance canister. To derive the account identifier of your public key, you can look at how the derive endpoint works.
Deriving the governance canister account identifier
For neuron creation on the Internet Computer, the process begins with the transfer of ICP tokens to the address destined to become the neuron. This address, while earmarked for a future neuron, does not yet represent an active neuron until the governance canister acknowledges the deposit. It's crucial to understand that, at this juncture, the neuron has not been created; the address is simply a placeholder for the future neuron. By transferring the ICP tokens directly to this designated address, you effectively earmark them for neuron creation. However, the governance canister must be notified of this transfer to activate the neuron. This process is done in the next step of the guide. Once the governance canister is aware, it uses the transferred tokens at the specified address to formally establish the neuron.
You can derive the governance canister account identifier you need to send ICP similarly to how you derive your own account identifier.
curl --location '0.0.0.0:8081/construction/derive' --header 'Content-Type: application/json' --data '{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_key": {
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
},
"metadata": {
"account_type": "neuron",
"neuron_index": 0
}
}'
The response is the account identifier we need to fund with ICP.
{"account_identifier":{"address":"26d877d2a04f0a9ae5e5a481102d5a865115263f2ed93a0804e3ed7b8d824921"}}
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
).
We can then fund this account with the amount of ICP we would like to stake. In this example, we want to stake 10 ICP, or 1_000_000_000
e8s
.
Disburse a neuron
Since version | 1.4.0 |
Idempotent? | No |
Minimal access level | controller |
This section will show you how to use Rosetta to disburse an existing neuron.
Preconditions
account.address
is the ledger address of the neuron controller or hotkey.amount
is the amount that should be extracted from the dissolved neuron and needs to be equal to or less than the amount of ICP locked in the neuron.metadata.recipient
contains an account_identifier that should receive the ICP after the neuron is disbursed.- The neuron needs to be in a
DISSOLVED
state to be able to be disbursed.
Postconditions
- The neurons locked funds were reduced by the amount specified in the request.
metadata.recipient
received the specified amount.
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "DISBURSE",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"amount": {
"value": "1000000000",
"currency": {
"symbol": "ICP",
"decimals": 8
}
},
"metadata": {
"neuron_index": 2,
"recipient": "47e0ae0de8af04a961c4b3225cd77b9652777286ce142c2a07fab98da5263100"
}
}
],
"metadata": null
}'
Start or stop dissolving
Since version | 1.1.0 |
Idempotent? | yes |
Minimal access level | controller |
If you want to retrieve the ICP that was locked in the neuron, you will have to set the neuron to a dissolving state.
You must wait at least 6 months between setting the neuron to DISSOLVING
and receiving back your ICP. The waiting period increases if the set dissolve timestamp is further in the future than 6 months.
Start dissolving
To set a neuron to DISSOLVING
, you can build the following payload request:
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data
'{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "START_DISSOLVING",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"neuron_index": 0
}
}
],
"metadata": null
}'
After that, you can fetch the neuron info and it should display that it is now in DISSOLVING
state.
{
"transaction_identifier": {
"hash": "0000000000000000000000000000000000000000000000000000000000000000"
},
"metadata": {
"operations": [
{
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"controller": "iowfl-yzooa-br3dt-77erl-nlm7f-kplhq-php75-hw3an-aeqn2-swh4t-3qe",
"followees": {},
"hotkeys": [],
"kyc_verified": true,
"maturity_e8s_equivalent": 0,
"neuron_fees_e8s": 0,
"neuron_id": 3094748712371737240,
"neuron_index": 0,
"staked_maturity_e8s": null,
"state": "DISSOLVING"
},
"operation_identifier": {
"index": 0
},
"status": "COMPLETED",
"type": "NEURON_INFO"
}
]
}
}
Stop dissolving
To stop a neuron from dissolving, you can call Rosetta with the following parameters:
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data
'{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "STOP_DISSOLVING",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"neuron_index": 0
}
}
],
"metadata": null
}'
The corresponding state of the neuron will switch to `NOT_DISSOLVING`.
```json
{
"transaction_identifier": {
"hash": "0000000000000000000000000000000000000000000000000000000000000000"
},
"metadata": {
"operations": [
{
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"controller": "iowfl-yzooa-br3dt-77erl-nlm7f-kplhq-php75-hw3an-aeqn2-swh4t-3qe",
"followees": {},
"hotkeys": [],
"kyc_verified": true,
"maturity_e8s_equivalent": 0,
"neuron_fees_e8s": 0,
"neuron_id": 3094748712371737240,
"neuron_index": 0,
"staked_maturity_e8s": null,
"state": "NOT_DISSOLVING"
},
"operation_identifier": {
"index": 0
},
"status": "COMPLETED",
"type": "NEURON_INFO"
}
]
}
}
Hotkeys generation
This section will explain how to generate a hotkey for neuron management and add or remove hotkeys to your neuron using ICP Rosetta.
The recommended way to get hotkeys is to programmatically generate them using the same process used in the public ic
repository.
However, it is possible to generate hotkeys manually as described below.
Generating a hotkey
The manual hotkey generation process consists of generating a PEM file, then deriving the corresponding private and public keys.
Generate PEM
To generate the keys, start with generating a private PEM file:
openssl genpkey -algorithm ed25519 -outform PEM -out private.pem
Example output:
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIHAOt4HGrNdcIFhBy7N9p6iq3iRowd4NZjDZ8aaaDCcX
-----END PRIVATE KEY-----
Then create the public PEM from it:
openssl pkey -in private.pem -pubout > public.pem
Example output:
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA4JKtE2KNVUTo96cl202FgWv5ctwP7f1ds1O73PZ6+VE=
-----END PUBLIC KEY-----
Generating hex key representation
Create the private DER file:
openssl pkey -inform pem -outform der -in private.pem -out private.der
Create the public DER file:
openssl pkey -inform pem -outform der -in private.pem -out public.der -pubout
Hex representation
The generated DER files are in a binary format not intended to be readable by humans. But you can get a hex representation of both keys:
Private key
xxd -p private.der
Example output:
302e020100300506032b657004220420700eb781c6acd75c205841cbb37d
a7a8aade2468c1de0d6630d9f1a69a0c2717
Public key
xxd -p public.der
302a300506032b6570032100e092ad13628d5544e8f7a725db4d85816bf9
72dc0fedfd5db353bbdcf67af951
You need to keep only the last 32 bytes of the public key:
xxd -s 12 -c 32 -p public.der
Example output:
e092ad13628d5544e8f7a725db4d85816bf972dc0fedfd5db353bbdcf67af951
This is the public key you can use in Rosetta operations to identify your hotkey!
Frequently asked questions
Why do I get "Algorithm ed25519 not found" while generating the PEM file?
The version of OpenSSL included in macOS doesn't support ed25519 by default. You may have to install another version of OpenSSL (for example, through brew) or run the command from a Linux machine.
Adding hotkeys to a neuron
Since version | 1.2.0 |
Idempotent? | yes |
Minimal access level | controller |
The ADD_HOTKEY
operation adds a hotkey to the neuron. The Governance canister allows some noncritical operations to be signed with a hotkey instead of the controller’s key (e.g., voting and querying maturity).
The neuron you are trying to add a hotkey for must not have more than 10 hotkeys already linked to it.
The command has two forms: one form accepts an ICP principal as a hotkey, and another form accepts a public key.
Add a principal as a hotkey
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '
{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "ADD_HOTKEY",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"neuron_index": 1,
"principal": "sp3em-jkiyw-tospm-2huim-jor4p-et4s7-ay35f-q7tnm-hi4k2-pyicb-xae"
}
}
],
"metadata": null
}'
Add a public key as a hotkey
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data
'{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "ADD_HOTKEY",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"neuron_index": 1,
"public_key": {
"hex_bytes": "04995aca22a49c8e579a8501f38d64952054f4b33f793376f1d013d93edd10a55c29522e41b5fed16283b70a6f3fa00c6d66420938e89c5d67a58f30c8a5df059e",
"curve_type": "secp256k1"
}
}
}
],
"metadata": null
}'
When you call NeuronInfo
afterwards you can see the hotkey added to the list of hotkeys, like so.
{
"transaction_identifier": {
"hash": "0000000000000000000000000000000000000000000000000000000000000000"
},
"metadata": {
"operations": [
{
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"controller": "iowfl-yzooa-br3dt-77erl-nlm7f-kplhq-php75-hw3an-aeqn2-swh4t-3qe",
"followees": {},
"hotkeys": [
"ykyib-l2eur-2idfx-uyka5-hag7i-3mypz-3kqc2-d6pid-tzat5-bysqw-mae"
],
"kyc_verified": true,
"maturity_e8s_equivalent": 0,
"neuron_fees_e8s": 0,
"neuron_id": 3094748712371737240,
"neuron_index": 0,
"staked_maturity_e8s": 0,
"state": "NOT_DISSOLVING"
},
"operation_identifier": {
"index": 0
},
"status": "COMPLETED",
"type": "NEURON_INFO"
}
]
}
}
Removing hotkeys from a neuron
The REMOVE_HOTKEY
operation removes a previously added hotkey from the neuron.
The command has two forms: one form accepts an ICP principal as a hotkey, and another form accepts a public key.
Remove a principal as a hotkey
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '
{
"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
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data
' {
"operation_identifier": { "index": 0 },
"type": "REMOVE_HOTKEY",
"account": { "address": "907ff6c714a545110b42982b72aa39c5b7742d610e234a9d40bf8cf624e7a70d" },
"metadata": {
"neuron_index": 0,
"public_key": {
"hex_bytes": "1b400d60aaf34eaf6dcbab9bba46001a23497886cf11066f7846933d30e5ad3f",
"curve_type": "edwards25519"
}
}
}'
After this operation the hotkey should not be included in the NeuronInfo
anymore.
Lock a neuron
Since version | 1.1.0 |
Idempotent? | yes |
Minimal access level | controller |
In this section of the staking tutorial, you will learn how to lock your neuron. The amount of rewards you can expect to receive is, among other factors, dependent on the amount of time a neuron is locked up for. If the dissolve timestamp is set to a value that is before 6 months in the future, you will not be getting any rewards for the locked period. This is because the last 6 months of a dissolving neuron, the neuron will not get any rewards. If you set the dissolve timestamp to 1 year in the future and start dissolving the neuron right away, you will receive rewards for the next 6 months.
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, 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.
An example call to the construction/payloads
endpoint resembles the following:
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 4
},
"type": "SET_DISSOLVE_TIMESTAMP",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"neuron_index": 0,
"dissolve_time_utc_seconds": 1712156627
}
}
],
"metadata": null
}'
The response will confirm that the dissolve timestamp has been changed.
{
"transaction_identifier": {
"hash": "0000000000000000000000000000000000000000000000000000000000000000"
},
"metadata": {
"operations": [
{
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"dissolve_time_utc_seconds": 1712156627,
"neuron_index": 0
},
"operation_identifier": {
"index": 0
},
"status": "COMPLETED",
"type": "SET_DISSOLVE_TIMESTAMP"
}
]
}
}
If you then check the neuron you will see that it changed state from DISSOLVED
to NOT_DISSOLVING
. If you set the timestamp to a sufficiently far time in the future you can now start to collect rewards for with your neuron.
Fetch neuron info
Since version | 1.5.0 |
Idempotent? | yes |
Minimal access level | hotkey |
In this section of the staking tutorial you will learn how to fetch information about an existing neuron. There are a few variations to this that depend on whether you are the controller of the neuron or whether you are using a hotkey.
Calling NeuronInfo
as a controller
Assuming that you are the controller of the neuron with the neuron_id
3094748712371737240
and you are not using a hotkey, the corresponding payloads call would resemble the following:
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "NEURON_INFO",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
}
],
"metadata": null
}'
Calling NeuronInfo
with a hotkey
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "NEURON_INFO",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
}
],
"metadata": {
"neuron_index": 0,
"controller": {
"public_key": {
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
}
}
}'
The result of the NeuronInfo
call would resemble the following:
{
"transaction_identifier": {
"hash": "0000000000000000000000000000000000000000000000000000000000000000"
},
"metadata": {
"operations": [
{
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"controller": "iowfl-yzooa-br3dt-77erl-nlm7f-kplhq-php75-hw3an-aeqn2-swh4t-3qe",
"followees": {},
"hotkeys": [],
"kyc_verified": true,
"maturity_e8s_equivalent": 0,
"neuron_fees_e8s": 0,
"neuron_id": 3094748712371737240,
"neuron_index": 0,
"staked_maturity_e8s": null,
"state": "DISSOLVED"
},
"operation_identifier": {
"index": 0
},
"status": "COMPLETED",
"type": "NEURON_INFO"
}
]
}
}
Right now the neuron is DISSOLVED
as it has not yet been locked up.
Currently, ICP Rosetta does not support fetching neuron info of a neuron that you are not either the controller of or have a hotkey for.
Spawn a neuron
Since version | 1.3.0 |
Idempotent? | yes |
Minimal access level | controller |
In this section you will learn how to split a neuron and spawn a new one out of your existing neuron.
The SPAWN
operation transfers all the maturity from the existing neuron to the staked amount of the newly spawned neuron.
Preconditions
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.
An example command to spawn a new neuron resembles the following. Make sure to give the spawned_neuron_index
a different index than the parent neuron.
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '{
{
"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 ofspawned_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 ICP
Since version | 1.5.0 |
Idempotent? | yes |
Minimal access level | controller |
This section of the staking tutorial will show you how to use the ICP that was previously sent to the governance account identifier corresponding to your public key. You can use this ICP to create and fund a neuron.
The full amount of ICP that was sent to the governance account identifier will be used for staking.
A call to the endpoint construction/payloads
would resemble the following.
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "STAKE",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"neuron_index": 0
}
}
],
"metadata": null
}'
The transaction's returned output will resemble the following:
{'transaction_identifier': {'hash': '0000000000000000000000000000000000000000000000000000000000000000'}, 'metadata': {'operations': [{'account': {'address': '8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4'}, 'metadata': {'neuron_id': 3094748712371737240, 'neuron_index': 0}, 'operation_identifier': {'index': 0}, 'status': 'COMPLETED', 'type': 'STAKE'}]}}
Neuron management operations don’t strictly adhere to the Rosetta specification. If the transaction only contains the neuron management operations (like the example above), the returned transaction hash won’t be on the ICP ledger chain. The returned transaction hash does not make any sense for governance operations and is thus left blank.
The governance canister will take your public key and the neuron index provided (in this case it was 0) and either create a new neuron_id
or add the ICP to an already existing neuron with the same derived neuron_id
. In this case the neuron with the neuron_id
3094748712371737240
was created.
The neuron_id
is an internal representation of neurons on the governance canister. The neuron index corresponds to the index of neurons that a certain public key controls. Taking the public key 047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367
, the index 0
and given how many existing neurons already exist,
the governance canister arrives at the neuron_id
3094748712371737240
Stake maturity
Since version | 1.4.0 |
Idempotent? | no |
Minimal access level | controller |
Once you have a neuron starts accumulating maturity, you can either decide to leave the maturity as it is until you decide to convert it to ICP, or you can make use of it and stake it in addition to your ICP. To do the latter, you will have to stake your maturity.
You can set how much of the existing maturity you want to stake. The maturity needs to be larger than zero for the operation to work. In the following example command, 100% of the maturity is staked.
curl --location '0.0.0.0:8081/construction/payloads' --header 'Content-Type: application/json' --data '
{
"network_identifier": {
"blockchain": "Internet Computer",
"network": "00000000000000020101"
},
"public_keys": [
{
"hex_bytes": "047a83e378053f87b49aeae53b3ed274c8b2ffbe59d9a51e3c4d850ca8ac1684f7131b778317c0db04de661c7d08321d60c0507868af41fe3150d21b3c6c757367",
"curve_type": "secp256k1"
}
],
"operations": [
{
"operation_identifier": {
"index": 0
},
"type": "STAKE_MATURITY",
"account": {
"address": "8b84c3a3529d02a9decb5b1a27e7c8d886e17e07ea0a538269697ef09c2a27b4"
},
"metadata": {
"neuron_index": 0,
"percentage_to_stake": 100
}
}
],
"metadata": null
}'