Skip to content

Latest commit

 

History

History
251 lines (192 loc) · 8.01 KB

File metadata and controls

251 lines (192 loc) · 8.01 KB

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.