Casper.Network.SDK
Show / Hide Table of Contents

Key-Value contract tutorial with C# and Casper .NET SDK

This example is based on the Key-Value contract tutorial available in the Casper Network web site.

Using the Casper .NET SDK, we'll show you how to:

  • Deploy the key-value contract example to the blockchain.
  • Call an entry point in the contract to store a value with a given named key.
  • Read the previously stored value.

Read first the Counter tutorial to prepare your environment and one account with enough $CSPR to make deploys.

NOTE: We use a local network in this example. Learn here how to install your own local network. Alternatively, you can easily adapt the code in this example to use the casper-test network.

Step 1. Deploy the key-value storage contract

Clone the GitHub repository and build the contract following the instructions in the README file.

git clone https://github.com/casper-ecosystem/kv-storage-contract
cd kv-storage-contract
make build-contract

As a result you will get the contract compiled at target/wasm32-unknown-unknown/release/. Copy the contract.wasm file to your working directory.

Next, use the ContractDeploy deploy template to prepare a Deploy object. Sign it and send it to the network.

public static async Task<HashKey> DeployKVStorageContract()
{
    var wasmFile = "./contract.wasm";
    var wasmBytes = System.IO.File.ReadAllBytes(wasmFile);
    
    var deploy = DeployTemplates.ContractDeploy(
        wasmBytes, 
        myAccountPK,
        300_000_000_000, 
        chainName);
    deploy.Sign(myAccount);

    var response = await casperSdk.PutDeploy(deploy);

    string deployHash = response.GetDeployHash();
    
    var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120));
    var deployResponse = await casperSdk.GetDeploy(deployHash, tokenSource.Token);

    var execResult = deployResponse.Parse().ExecutionResults.First();

    Console.WriteLine("Deploy COST : " + execResult.Cost);
    
    var contractHash = execResult.Effect.Transforms.First(t =>
                t.Type == TransformType.WriteContract).Key;
    Console.WriteLine("Contract key: " + contractHash);
    
    return (HashKey)contractHash;
}

The deployment of the contract has added to the account a couple of named keys. We can retrieve our account information to see them:

public async static Task GetAccountInfo()
{
    var response = await casperSdk.GetAccountInfo(myAccountPK);
    
    File.WriteAllText("res_GetAccountInfo.json", response.Result.GetRawText());
}

In the response we can identify the named key kvstorage_contract. It contains the hash of the contract. We'll use it in a later step to call the contract to store a value.

Print a beautified json using jq like this:

cat res_GetAccountInfo.json | jq
{
  "api_version": "1.0.0",
  "stored_value": {
    "Account": {
      "account_hash": "account-hash-989ca079a5e446071866331468ab949483162588d57ec13ba6bb051f1e15f8b7",
      "named_keys": [
        {
          "name": "kvstorage_contract",
          "key": "hash-0aaab9926971ac1564d6057d269aff4b93e621c788e2cfc2e9fbe48fac345285"
        },
        {
          "name": "kvstorage_contract_hash",
          "key": "uref-eafede874c50f50841e589825d41a6ff9c2abe12434f52d9a85816379f1acefd-007"
        }
      ],
      "main_purse": "uref-ea6dd32086d7878460a042357c8332d2d19251bb21f5ad809fc048855e167e9b-007",
      "associated_keys": [
        {
          "account_hash": "account-hash-989ca079a5e446071866331468ab949483162588d57ec13ba6bb051f1e15f8b7",
          "weight": 1
        }
      ],
      "action_thresholds": {
        "deployment": 1,
        "key_management": 1
      }
    }
  },
  "merkle_proof": "010..."
}

Step 2. Store an integer value in the contract named keys

We can call now the contract and store a key-value pair. In the code below, we use the ContractCall deploy template to call the store_i32 entry point in the kvstorage_contract.

public async static Task StoreI32()
{
    var namedArgs = new List<NamedArg>()
    {
        new NamedArg("name", "I32MinValue"),
        new NamedArg("value", int.MinValue)
    };

    await StoreKeyValue("store_i32", namedArgs, "res_StoreI32.json");
}

private async static Task StoreKeyValue(string entryPoint, List<NamedArg> namedArgs, string saveToFile)
{
    var deploy = DeployTemplates.ContractCall("kvstorage_contract",
        entryPoint,
        namedArgs,
        myAccountPK,
        1_000_000_000,
        chainName);
    deploy.Sign(myAccount);

    var response = await casperSdk.PutDeploy(deploy);
    var deployHash = response.GetDeployHash();

    var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120));
    var deployResponse = await casperSdk.GetDeploy(deployHash, tokenSource.Token);

    if (saveToFile != null)
        File.WriteAllText(saveToFile, deployResponse.Result.GetRawText());
}

Note that for this deploy we're paying 1 $CSPR. Check in the json response how much the actual cost is:

cat res_StoreI32.json | jq | grep "cost"

Step 3. Read the number stored

Combining the name of the contract and the named key we can form a path and query the network to get the value:

public async static Task<CLValue> ReadStoredValue(string namedKey)
{
    var accountKey = new AccountHashKey(myAccountPK);
    var rpcResponse = await casperSdk.QueryGlobalState(accountKey, null,
        $"kvstorage_contract/{namedKey}");

    Console.WriteLine(rpcResponse.Result.GetRawText());
    File.WriteAllText($"res_ReadStoredValue_{namedKey}.json", rpcResponse.Result.GetRawText());

    return rpcResponse.Parse().StoredValue.CLValue;
}

// Main method
//
var clValue = await ReadStoredValue("I32MinValue");
var number = clValue.ToInt32();
Console.WriteLine("Value: " + number);

Step 4. Store a string value calling the contract by its hash key

In the step 2 we stored a value using the contract named key. That works only when the deploy is sent by the same account that deployed the contract. To store a value from a different account, we use the contract hash key instead:

public async static Task StoreString(HashKey contractHash = null)
{
    var namedArgs = new List<NamedArg>()
    {
        new NamedArg("name", "WorkingOn"),
        new NamedArg("value", "Casper .NET SDK")
    };

    var deploy = DeployTemplates.ContractCall(contractHash,
        "store_string",
        namedArgs,
        myAccountPK,
        500_000_000,
        chainName);

    deploy.Sign(myAccount);

    var response = await casperSdk.PutDeploy(deploy);
    var deployHash = response.GetDeployHash();

    var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120));
    var deployResponse = await casperSdk.GetDeploy(deployHash, tokenSource.Token);

    File.WriteAllText("res_StoreString.json", deployResponse.Result.GetRawText());
}

// Main method
//
await StoreString(contractHash);

Step 5. Read the string value using the contract key

Similarly, we can read the stored string with the contract key and the named key:

public async static Task<CLValue> ReadStoredValue(string namedKey, GlobalStateKey contractHash)
{
    var queryResponse = await casperSdk.QueryGlobalState(contractHash, null, namedKey);

    File.WriteAllText($"res_ReadStoredValue_{namedKey}.json", queryResponse.Result.GetRawText());

    return queryResponse.Parse().StoredValue.CLValue;
}

// Main method 
//
var clValue = await ReadStoredValue("WorkingOn", contractHash);
Console.WriteLine("Value: " + (string)clValue);

Note that in this case we're querying the network without our public key. The public key was needed in step 3 to recover the contract hash from a named key. Now, we use the contract hash directly.

Step 6. Store and read different CLValue types

So far, we've stored an integer and a string in the contract named keys. The code in Program.cs contains methods to store many of the other CLValues. We encourage you to test some of them now.

In This Article
Back to top Maintained by MAKE Technology LLC