♾️

ICP Cheat Sheet

🚨

Use these notes and instructions at your own risk. I'm not liable for any damage.

ICP launched last week and I dug into their tools and code in the past few days. Here are my notes about how to interact with the IC, and specifically with the Genesis Token Canister (GTC). These notes are based on what DFINITY has published and notes that team members have shared with me (hat tip to Timo).

Setup & Learnings

Follow the setup instructions DFINITY published in order to set up 'dfx', 'keysmith', 'didc' as well as all the necessary files.

Learnings

  • The dissolve-delay starts in the moment you dissolve. From that point on, the neuron doesn't accumulate any more staking rewards.
  • To KYC your identity/ies, you need a) the legacy address and b) the principal that corresponds to that identity. See below how to get them and then submit your info via: https://kyc.dfinity.org/gtc
  • When a command fails, double check:
    • did you set the '-i N' flag according to your current identity?
    • did you choose the right identity via 'dfx identity use idN'?

Objects & Hierarchy

There are four levels to the objects we interact with.

SEED PHRASE > IDENTITY(IES) > NEURON(S) > TOKEN(S)

  1. Seed Phrase (12 words)
  2. Identities Based on your seed phrase you can derive one (Seed Donors) or multiple (Early Contributors) identities
    1. Each identity has the following identifiers:

    2. an account ID ("ICP address")
    3. 💡

      When you work with different identities, change '-i 0' at the end of the keysmith commands to '-i 0..n' for any of your n+1 identities.

      🚨

      The instructions for ACCOUNT_ID_WITHOUT_CHECKSUM_BYTES don't work on Mac. Use an Ubuntu machine (real or virtual). They now work on Mac.

      ACCOUNT_ID=$(echo $seed | keysmith account -f - -i 0)
      dfx ledger account-id
      
      # looks like this:
      # 1f4923ff5638e12345678f808e9ae722d1d4d9c11234567896123b51235ca6
      
      ACCOUNT_ID_WITHOUT_CHECKSUM_BYTES="$(printf ${ACCOUNT_ID:8} | fold -w 2 | xargs -I {} printf '%d:nat8; ' '0x{}')" 
      
      # looks like this:
      # "123:nat8; 50:nat8; 222:nat8; 26:nat8; 13:nat8; 64:nat8; 64:nat8; 64:nat8; 8:nat8; 233:nat8; 174:nat8; 114:nat8; 64:nat8; 29:nat8; 77:nat8; 64:nat8; 28:nat8; 30:nat8; 63:nat8; 247:nat8; 206:nat8; 101:nat8; 51:nat8; 32:nat8; 60:nat8; 32:nat8; 92:nat8; 166:nat8;"
    4. a legacy address (ETH style)
    5. ADDR="$(echo $seed | keysmith legacy-address -f - -i 0)"
      
      # looks like this
      # 65656565654ecf13b11a8823a0ea95b064646464
    6. a principal identifier
    7. PRINCIPAL="$(echo $seed | keysmith principal -f - -i 0)"
      
      # looks like this
      # barks-64646-gjtbp-64646-7iwlz-64646-hmu36-64646-qfdr2-64646-hae'
    8. a public key
    9. PUBLIC_KEY="$(echo $seed | keysmith public-key -f - -i 0)"
      
      # looks like this
      06464646464c7fb853bb774617a837cea7429092276f47d9b66fec64646464640e1c6c8f5dd5c60f6464646464302ab68d935c5204181b410cce189cd336464646464
      
  3. Neuron(s) Identified by its NEURON_ID
  4. # looks like this
    6_233_864_81_79_115_226
    

    Can be staked and will then generate voting rewards.

  5. Token(s)
  6. Can be disbursed from the Neuron into your wallet and can be transferred from there.

Interacting with neurons & claiming tokens

In order to 'get' tokens that are staked inside your neurons, the following steps are necessary:

  1. Claim your Neuron
  2. KYC your Neuron
  3. Dissolve your Neuron (note: this has a dissolve-delay)
  4. Disburse the tokens from your Neuron
💡

The following commands work for ECT and Seed Donations. In the case of Seed Donations it wouldn't be necessary to loop (since you only have one identity) but for the sake of simplicity, I've only noted on set of instructions.

Claiming your neurons

Derive native IC principal from ETH seed

# since I have 3 addresses, I set N=2
N=2

# read seed
read seed

# define import function
import () {
  PEM=identity-$1$2.pem
  echo $seed | keysmith private-key -f - -i $2 -o $PEM
  dfx identity import $1$2 $PEM
  rm $PEM
}

# choose an alias (i choose 'ect')
for i in `seq 0 $N`; do import ect $i; done

# check identities
dfx identity list

Display legacy addresses and balances

# define the balance function

GTC=renrk-eyaaa-aaaaa-aaada-cai
balance () {
  ADDR=$(echo $seed | keysmith legacy-address -f - -i $1)
  ICPT=$(dfx canister --network=https://ic0.app --no-wallet call $GTC balance '("'$ADDR'")')
  echo $ADDR : $ICPT
}

# get the output
for i in `seq 0 $N`; do balance $i; done

Display Neuron Balances

CANISTER=rrkah-fqaaa-aaaaa-aaaaq-cai
NEURON_ID=...

# get neuron info, doesn't require private key/identity
RESULT="$(dfx canister --network=https://ic0.app --no-wallet call $CANISTER get_neuron_info "($NEURON_ID:nat64)" --output=raw)"
didc decode -t "(Result_2)" -d ~/Downloads/nns-ifaces-0.8.0/governance.did $RESULT

# get full info, does require identity
dfx identity use ...
RESULT="$(dfx canister --network=https://ic0.app --no-wallet call $CANISTER get_full_neuron "($NEURON_ID:nat64)" --output=raw)"
didc decode -t "(Result_1)" -d ~/Downloads/nns-ifaces-0.8.0/governance.did $RESULT

Take control of neurons

# The Genesis Token Canister
GTC=renrk-eyaaa-aaaaa-aaada-cai

# create signed messages for all your identities
sign () {
  PUBKEY=$(echo $seed | keysmith public-key -f - -i $1)
  dfx canister --network=https://ic0.app --no-wallet sign $GTC claim_neurons '("'$PUBKEY'")' --file claim_$1.json
}

for i in `seq 0 $N`; do sign $i; done

# send the messages
send () {
  dfx canister --network=https://ic0.app --no-wallet send claim_$1.json
}

for i in `seq 0 $N`; do send $i; done

Test if taking control was successful

# outputs
# 'ADDR' : 1
# for each address that you successfully claimed

success () {
  ADDR=$(echo $seed | keysmith legacy-address -f - -i $1)
  RESULT=$(dfx canister --network=https://ic0.app --no-wallet call $GTC get_account '("'$ADDR'")' | grep 2_511_217_078 | grep -c true)
  echo $ADDR : $RESULT
}

for i in `seq 0 $N`; do success $i; done

View your neuron ids

You will later interact with your neurons. Save this output (e.g. put it in a spreadsheet)

view () {
  ADDR=$(echo $seed | keysmith legacy-address -f - -i $1)
  dfx canister --network=https://ic0.app --no-wallet call $GTC get_account '("'$ADDR'")' | grep 2_024_218_412 | sed -E 's/;};/\n/g' | grep = | sed -e 's/.*= //'
}

# call the view function for all your identities

view 0
view 1
view ...

Dissolving your neuron

💡

Caution: When you issue the dissolve command, your (pre-aged) neurons will immediately stop generating voting rewards.

I've written three functions to handle dissolution of neurons:

  • check_neurons: Outputs the status of the neuron.
    • status=1 means the neurons is currently staked
    • status=2 means the neuron is dissolving (during the dissolve delay)
    • status=3 means the neuron is dissolved and can disburse tokens
  • dissolve_neuron: This actually sends the dissolution request
  • dnv_neuron: Dissolves and verifies

check_neuron () {
	RESULT="$(dfx canister --network=https://ic0.app --no-wallet call $CANISTER get_neuron_info "($NEURON_ID:nat64)" --output=raw)"
	didc decode -t "(Result_2)" -d ~/Downloads/nns-ifaces-0.8.0/governance.did $RESULT
}

dissolve_neuron () {
	RESULT="$(dfx canister --network=https://ic0.app --no-wallet call $CANISTER manage_neuron "(record { id = opt record { id = $NEURON_ID:nat64 }; command = opt variant { Configure = record { operation = opt variant { StartDissolving = record {} } } } })" --output=raw)"
	didc decode -t "(ManageNeuronResponse)" -d ~/Downloads/nns-ifaces-0.8.0/governance.did $RESULT
}

dnv_neuron () {
	check_neuron 
	dissolve_neuron
	check_neuron
}

# do once
CANISTER=rrkah-fqaaa-aaaaa-aaaaq-cai

# do once per identity
dfx identity use id0

# do for every neuron
NEURON_ID=...
dnv_neuron

💡

Since you have to run this for every neuron, it may make sense to batch-process this either via for-loop or via bash script

Disbursing tokens from your neuron

Check that the neuron has status=3 (i.e. is dissolved) and that you've successfully passed KYC.

🚨

The instructions for ACCOUNT_ID_WITHOUT_CHECKSUM_BYTES currently don't run on Mac. Use VirtualBox or a separate laptop to run Ubuntu and produce it there. They now work on Mac.

# set the canister once
CANISTER=rrkah-fqaaa-aaaaa-aaaaq-cai

# IDENTITY 0

dfx identity use id0
ACCOUNT_ID=$(echo $seed | keysmith account -f - -i 0)
NEURON_ID=...
ACCOUNT_ID_WITHOUT_CHECKSUM_BYTES="$(printf ${ACCOUNT_ID:8} | fold -w 2 | xargs -I {} printf '%d:nat8; ' '0x{}')"

RESULT="$(dfx canister --network=https://ic0.app --no-wallet call $CANISTER manage_neuron "(record { id = opt record { id = $NEURON_ID:nat64 }; command = opt variant { Disburse = record { to_account = opt record { hash = vec { $ACCOUNT_ID_WITHOUT_CHECKSUM_BYTES } }; amount = null } } })" --output=raw)"

didc decode -t "(ManageNeuronResponse)" -d ~/Downloads/nns-ifaces-0.8.0/governance.did $RESULT

Transferring tokens

Display your balance

# replace with the right identity
dfx identity use id0
dfx ledger --network=https://ic0.app balance

Send tokens to another wallet

# replace with the right identity
dfx identity use id0

# test tx
dfx ledger --network ic transfer [put the destination address here] --amount 0.001 --memo 646464