Installing Smart Contracts and Querying Global State
This tutorial is a continuation of the Smart Contracts on Casper guide, and covers the installation of Casper contracts using the Casper command-line client and the put-deploy
command.
Prerequisites
- You know how to send and verify deploys
- Your environment meets these prerequisites and you have a client to interact with the network, such as the default Casper client
- You have a Casper account with a public and secret key pair to initiate the deploy
- You have enough CSPR tokens in your account to pay for deploys. If you plan to use the Casper Testnet, learn about the faucet to fund your testing account
- You understand how to write basic contract code and session code
- You have a contract Wasm to send to a Casper Network
Installing a Contract in Global State
To install a contract in global state, you need to send a deploy to the network with the contract Wasm. You can do so by using the put-deploy
command. Remember to verify the deploy.
casper-client put-deploy \
--node-address [NODE_SERVER_ADDRESS] \
--chain-name [CHAIN_NAME] \
--secret-key [KEY_PATH]/secret_key.pem \
--payment-amount [PAYMENT_AMOUNT_IN_MOTES] \
--session-path [CONTRACT_PATH]/[CONTRACT_NAME].wasm
The arguments used above are:
node-address
- An IP address of a peer on the network. The default port for JSON-RPC servers on Mainnet and Testnet is 7777chain-name
- The chain name to the network where you wish to send the deploy. For Mainnet, use casper. For Testnet, use casper-testsecret-key
- The file name containing the secret key of the account paying for the deploypayment-amount
- The payment for the deploy in motessession-path
- The path to the contract Wasm, which should point to wherever you compiled the contract (.wasm file) on your computer
Once you call this command, it will return a deploy hash. You can use this hash to verify that the deploy successfully took place.
Example - Install the contract:
Here we send a counter-define.wasm
to a local NCTL network.
casper-client put-deploy \
--node-address http://localhost:11101 \
--chain-name casper-net-1 \
--secret-key [PATH_TO_YOUR_KEY]/secret_key.pem \
--payment-amount 5000000000000 \
--session-path ./counter/target/wasm32-unknown-unknown/release/counter-define.wasm
To verify the deploy, call get-deploy
and provide the deploy hash you received from put-deploy
.
casper-client get-deploy \
--node-address http://localhost:11101 [DEPLOY_HASH]
Querying Global State
Here we look at how to query the global state to see when the network has successfully installed the contract.
Get the state root hash
First, you need to get the state root hash. After sending deploys to the network, you must get the new state root hash to see the new changes reflected. Otherwise, you would be looking at past events.
The state root hash identifies the current network state (global state). It is much like a Git commit ID for commit history. It gives a snapshot of the blockchain state at a moment in time. We use it to query global state after sending deploys to the network.
casper-client get-state-root-hash --node-address [NODE_SERVER_ADDRESS]
Here is an example with the node address filled in:
casper-client get-state-root-hash --node-address http://localhost:11101
Query global state
Next, query the state of a Casper Network at a given time, specified by the state-root-hash
described above. You can dive into the data stored in global state using the query path argument q
.
casper-client query-global-state \
--node-address [NODE_SERVER_ADDRESS] \
--state-root-hash [STATE_ROOT_HASH] \
--key [HASH_STRING] \
-q "[SESSION_NAME]/[SESSION_NAMED_KEY]" (OPTIONAL)
The arguments used above are:
node-address
- An IP address of a peer on the network. The default port for JSON-RPC servers on Mainnet and Testnet is 7777state-root-hash
- Hex-encoded hash of the state rootkey
- The identifier for the query. It must be the account public key, account hash, contract package hash, transfer hash, or deploy hashq
- An optional query path argument that allows you to drill into the specifics of a query with respect to the key
Example - Query the account:
To find your account details, query global state using your account hash.
casper-client query-global-state \
--node-address http://localhost:11101 \
--state-root-hash fa968344a2000282686303f1664c474465f9a028f32ec4f51791d9fa64c0bcd7 \
--key account-hash-1d17e3fdad268f866a73558d1ae45e1eea3924c247871cb63f67ebf1a116e66d
Here is how your account state would look. Notice that the sample response contains several named keys, including "counter", "counter_package_name", and "version". You can use these values to query the contract state further, as shown in the next example.
Sample account state
{
"id": -6831525034388467034,
"jsonrpc": "2.0",
"result": {
"api_version": "1.4.5",
"block_header": null,
"merkle_proof": "[27614 hex chars]",
"stored_value": {
"Account": {
"account_hash": "account-hash-1d17e3fdad268f866a73558d1ae45e1eea3924c247871cb63f67ebf1a116e66d",
"action_thresholds": {
"deployment": 1,
"key_management": 1
},
"associated_keys": [
{
"account_hash": "account-hash-1d17e3fdad268f866a73558d1ae45e1eea3924c247871cb63f67ebf1a116e66d",
"weight": 1
}
],
"main_purse": "uref-d92e420120199f90005802bf3036362f368ab69bebf17e7e53856d6ac82e117f-007",
"named_keys": [
{
"key": "hash-22228188b85b6ee4a4a41c7e98225c3918139e9a5eb4b865711f2e409d85e88e",
"name": "counter"
},
{
"key": "uref-41c3f4ae3c1ce2446f6fd880a96e698ae5abc715151e45e357d88bb739489c03-007",
"name": "counter_access_uref"
},
{
"key": "hash-76a8c3daa6d6ac799ce9f46d82ac98efb271d2d64b517861ec89a06051ef019e",
"name": "counter_package_name"
},
{
"key": "uref-917762490591a1404cba59ed8dcf0bcfa7f644ef6c6be9bf5ea7b1641617cad0-007",
"name": "version"
}
]
}
}
}
}
note
If you don't know your account hash, you can run this command:
casper-client account-address --public-key [PATH_TO_PUBLIC_KEY]
Example - Query the contract:
This example shows how to query global state given a contract hash. We use the contract hash from the sample response above.
casper-client query-global-state \
--node-address http://localhost:11101 \
--state-root-hash fa968344a2000282686303f1664c474465f9a028f32ec4f51791d9fa64c0bcd7 \
--key hash-22228188b85b6ee4a4a41c7e98225c3918139e9a5eb4b865711f2e409d85e88e
Here is how the sample contract would look and would contain details such as the contract_package_hash
, the contract entry_points
, and the named_keys
for the contract.
Sample contract state
{
"id": -4657473054587773855,
"jsonrpc": "2.0",
"result": {
"api_version": "1.4.5",
"block_header": null,
"merkle_proof": "[21330 hex chars]",
"stored_value": {
"Contract": {
"contract_package_hash": "contract-package-76a8c3daa6d6ac799ce9f46d82ac98efb271d2d64b517861ec89a06051ef019e",
"contract_wasm_hash": "contract-wasm-576b1718711d524a79ab2f05ce801006a3fd32eb48b9f7dac69a9fa966d634e3",
"entry_points": [
{
"access": "Public",
"args": [],
"entry_point_type": "Contract",
"name": "counter_get",
"ret": "I32"
},
{
"access": "Public",
"args": [],
"entry_point_type": "Contract",
"name": "counter_inc",
"ret": "Unit"
}
],
"named_keys": [
{
"key": "uref-d40613e50c7b405b02795e3fe3252554bef49b4b522e31a55f39b87c442f922a-007",
"name": "count"
}
],
"protocol_version": "1.4.5"
}
}
}
}
Example - Query a value using its key and the contract hash:
Next, you can query a named key associated with the contract using the -q
option. This example comes from the Counter Contract Tutorial, where a "count" variable is incremented and stored under a named key.
casper-client query-global-state \
--node-address http://localhost:11101 \
--state-root-hash [STATE_ROOT_HASH] \
--key [CONTRACT_HASH] -q "count"
Sample stored value
{
"id": -2540117660598287261,
"jsonrpc": "2.0",
"result": {
"api_version": "1.4.5",
"block_header": null,
"merkle_proof": "[56562 hex chars]",
"stored_value": {
"CLValue": {
"bytes": "00000000",
"cl_type": "I32",
"parsed": 0
}
}
}
}
Example - Query a value using the account hash and named keys:
It is also possible to check the state of a specific contract variable in global state given the account hash under which the contract was installed. Here we query the named key "count", stored under another key identifying the contract and named "counter".
casper-client query-global-state \
--node-address http://localhost:11101 \
--state-root-hash fa968344a2000282686303f1664c474465f9a028f32ec4f51791d9fa64c0bcd7 \
--key account-hash-1d17e3fdad268f866a73558d1ae45e1eea3924c247871cb63f67ebf1a116e66d \
-q "counter/count"
The response should be the same as in Example 3, above.
Example - Query contract package state:
You can query information about a contract package, such as the latest contract hash and contract version, given its contract package hash.
casper-client query-global-state \
--node-address http://localhost:11101 \
--key hash-76a8c3daa6d6ac799ce9f46d82ac98efb271d2d64b517861ec89a06051ef019e \
--state-root-hash 763e737cf55a298d54bcdfb4ee55526538a1a086128914b9cc25ccbdebbbb966
Here is how the contract package details would look. The response would contain the contract_hash
, which you would need to call a contract by hash in the next section. You would also see the access_key
for the ContractPackage
and the current contract_version
.
Sample contract package state
{
"id": -6225901853092301031,
"jsonrpc": "2.0",
"result": {
"api_version": "1.4.5",
"block_header": null,
"merkle_proof": "[20964 hex chars]",
"stored_value": {
"ContractPackage": {
"access_key": "uref-41c3f4ae3c1ce2446f6fd880a96e698ae5abc715151e45e357d88bb739489c03-007",
"disabled_versions": [],
"groups": [],
"versions": [
{
"contract_hash": "contract-22228188b85b6ee4a4a41c7e98225c3918139e9a5eb4b865711f2e409d85e88e",
"contract_version": 1,
"protocol_version_major": 1
}
]
}
}
}
}
What's Next?
- Learn different ways to call contracts using the Casper command-line client