# Key-Value storage tutorial with C# and NetCasperSDK This example shows how to use the NetCasperSDK to: - Fund a new account with 2500 $CSPR tokens from the faucet - Deploy the key-value storage contract example to the network - Call the contract to put a new string - Query the contract state to read the value of the string We are using a local network in this example. Learn [here](https://docs.casperlabs.io/en/latest/dapp-dev-guide/setup-nctl.html) how to install your own local network. ### Get a new instance of the client Prepare an instance of the client and a couple of keys that will be used during the example: ```csharp static string nodeAddress = "http://207.154.217.11:11101/rpc"; static NetCasperClient casperSdk = new NetCasperClient(nodeAddress); static KeyPair faucetAcct = KeyPair.FromPem("/tmp/faucetact.pem"); static KeyPair account2 = KeyPair.FromPem("/tmp/myaccount2.pem"); ``` ### Send $CSPR from the faucet account to `account2` Use the `StandardTransfer` template to create a deploy object that orders a transfer of 2500 $CSPR. Sign it and deploy it to the network. Take the `deploy_hash` from the response and make a call to `GetDeploy` with a timeout of 120 seconds to obtain the result of the transfer (testnet usually needs a longer timeout). ```csharp public static async Task FundAccount() { var deploy = DeployTemplates.StandardTransfer( faucetAcct, account2.PublicKey, 2500000000000, 1200, "casper-net-1"); deploy.Sign(faucetAcct); var response = await casperSdk.PutDeploy(deploy); string deployHash = response.GetDeployHash(); var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); response = await casperSdk.GetDeploy(deployHash, tokenSource.Token); File.WriteAllText("res_FundAccount.json", response.Result.GetRawText()); } ``` ### Deploy the Key-Value Storage contract You may find more information on the key-value storage contract used in this example here: [Key-Value Storage Tutorial](https://docs.casperlabs.io/en/latest/dapp-dev-guide/tutorials/kv-storage-tutorial.html). If you haven't done yet, clone the repository and build the contract following the instruction in the README file. ``` git clone https://github.com/casper-ecosystem/kv-storage-contract cd kv-storage-contract cat README ``` Now use the `ContractDeploy` template to prepare a deploy object, sign it and deploy it. ```csharp public static async Task DeployKVStorageContract() { var wasmFile = "./kv-contract-example/contract.wasm"; var wasmBytes = System.IO.File.ReadAllBytes(wasmFile); var deploy = DeployTemplates.ContractDeploy(wasmBytes, account2, 300000000000, "casper-net-1", 1, 1800000); deploy.Sign(account2); var response = await casperSdk.PutDeploy(deploy); Console.WriteLine(response.Result.GetRawText()); string deployHash = response.GetDeployHash(); var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); response = await casperSdk.GetDeploy(deployHash, tokenSource.Token); Console.WriteLine(response.Result.GetRawText()); File.WriteAllText("res_DeployContract.json", response.Result.GetRawText()); } ``` The deployment of the contract has added to the account a couple of named keys. We can retrieve the account information to see them: ```csharp public async static Task GetAccountInfo() { var response = await casperSdk.GetAccountInfo(account2.PublicKey); Console.WriteLine(response.Result.GetRawText()); File.WriteAllText("res_GetAccountInfo.json", response.Result.GetRawText()); } ``` Which outputs a response like this: ```json { "api_version": "1.0.0", "stored_value": { "Account": { "account_hash": "account-hash-989ca079a5e446071866331468ab949483162588d57ec13ba6bb051f1e15f8b7", "named_keys": [ { "name": "counter", "key": "hash-4941592b93d3c766e077d19f1609b52e955735bbbf6182fe6b235eb0665414aa" }, { "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..." } ``` ### Write a key-value pair Next step is to call one of the entry points in the contract to store a string value. Note how we include two session arguments, one for the name of the key, one for the value. ```csharp public async static Task PutString() { var sessionArgs = new List<NamedArg>(); sessionArgs.Add(new NamedArg("name", CLValue.String("WorkingOn"))); sessionArgs.Add(new NamedArg("value", CLValue.String("Friendly Hackaton"))); var deploy = DeployTemplates.ContractCall("kvstorage_contract", "store_string", sessionArgs, account2, 30000000000, "casper-net-1"); deploy.Sign(account2); var response = await casperSdk.PutDeploy(deploy); string deployHash = response.GetDeployHash(); var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); response = await casperSdk.GetDeploy(deployHash, tokenSource.Token); Console.WriteLine(response.Result.GetRawText()); File.WriteAllText("res_PutString.json", response.Result.GetRawText()); } ``` ### Read the stored value Finally, let's read the value we just stored in the `WorkingOn` key. To do that we are going to retrieve first the hash of the contract using the `GetAccountInfo` method. Then, we'll query the state of the contract with the `QueryState` method: ```csharp= public async static Task ReadStringValue() { // Get the contract hash // var response = await casperSdk.GetAccountInfo(account2.PublicKey); var namedKeys = response.Result.GetProperty("stored_value").GetProperty("Account") .GetProperty("named_keys"); string kvStorageHash = null; foreach (var nk in namedKeys.EnumerateArray()) { if (nk.GetProperty("name").GetString().Equals("kvstorage_contract")) { kvStorageHash = nk.GetProperty("key").GetString(); break; } } if (kvStorageHash == null) throw new Exception("Contract hash not found."); // now read the string querying for the WorkingOn key // response = await casperSdk.QueryState(kvStorageHash, new List<string>() {"WorkingOn"}); Console.WriteLine(response.Result.GetRawText()); File.WriteAllText("res_ReadString.json", response.Result.GetRawText()); } ``` ### Full example code `Program.cs` ```csharp= using System; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using NetCasperSDK; using NetCasperSDK.JsonRpc; using NetCasperSDK.Types; using NetCasperSDK.Utils; namespace CasperMain { public static class KeyValueStorageContract { static string nodeAddress = "http://207.154.217.11:11101/rpc"; static KeyPair faucetAcct = KeyPair.FromPem("/tmp/faucetact.pem"); static KeyPair account2 = KeyPair.FromPem("/tmp/myaccount2.pem"); static NetCasperClient casperSdk; public static async Task FundAccount() { var deploy = DeployTemplates.StandardTransfer( faucetAcct, account2.PublicKey, 25000000000000, 1200, "casper-net-1"); deploy.Sign(faucetAcct); var response = await casperSdk.PutDeploy(deploy); string deployHash = response.GetDeployHash(); var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); response = await casperSdk.GetDeploy(deployHash, tokenSource.Token); File.WriteAllText("res_FundAccount.json", response.Result.GetRawText()); } public static async Task DeployKVStorageContract() { var wasmFile = "./kv-contract-example/contract.wasm"; var wasmBytes = System.IO.File.ReadAllBytes(wasmFile); var deploy = DeployTemplates.ContractDeploy(wasmBytes, account2, 300000000000, "casper-net-1", 1, 1800000); deploy.Sign(account2); var response = await casperSdk.PutDeploy(deploy); Console.WriteLine(response.Result.GetRawText()); string deployHash = response.GetDeployHash(); var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); response = await casperSdk.GetDeploy(deployHash, tokenSource.Token); Console.WriteLine(response.Result.GetRawText()); File.WriteAllText("res_DeployContract.json", response.Result.GetRawText()); } public async static Task GetAccountInfo() { var response = await casperSdk.GetAccountInfo(account2.PublicKey); Console.WriteLine(response.Result.GetRawText()); File.WriteAllText("res_GetAccountInfo.json", response.Result.GetRawText()); } public async static Task PutString() { var sessionArgs = new List<NamedArg>(); sessionArgs.Add(new NamedArg("name", CLValue.String("WorkingOn"))); sessionArgs.Add(new NamedArg("value", CLValue.String("Friendly Hackaton"))); var deploy = DeployTemplates.ContractCall("kvstorage_contract", "store_string", sessionArgs, account2, 30000000000, "casper-net-1"); deploy.Sign(account2); var response = await casperSdk.PutDeploy(deploy); string deployHash = response.GetDeployHash(); var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); response = await casperSdk.GetDeploy(deployHash, tokenSource.Token); Console.WriteLine(response.Result.GetRawText()); File.WriteAllText("res_PutString.json", response.Result.GetRawText()); } public async static Task ReadStringValue() { // Get the contract hash // var response = await casperSdk.GetAccountInfo(account2.PublicKey); var namedKeys = response.Result.GetProperty("stored_value").GetProperty("Account") .GetProperty("named_keys"); string kvStorageHash = null; foreach (var nk in namedKeys.EnumerateArray()) { if (nk.GetProperty("name").GetString().Equals("kvstorage_contract")) { kvStorageHash = nk.GetProperty("key").GetString(); break; } } if (kvStorageHash == null) throw new Exception("Contract hash not found."); // now read the string querying for the WorkingOn key // response = await casperSdk.QueryState(kvStorageHash, new List<string>() {"WorkingOn"}); Console.WriteLine(response.Result.GetRawText()); File.WriteAllText("res_ReadString.json", response.Result.GetRawText()); } public async static Task Main(string[] args) { var loggingHandler = new RpcLoggingHandler(new HttpClientHandler()) { LoggerStream = new StreamWriter(Console.OpenStandardOutput()) }; casperSdk = new NetCasperClient(nodeAddress, loggingHandler); try { await FundAccount(); await DeployKVStorageContract(); await GetAccountInfo(); await PutString(); await ReadStringValue(); } catch (RpcClientException e) { Console.WriteLine(e.RpcError.Message); } catch (Exception e) { Console.WriteLine(e.Message); } } } } ``` `KeyValueStorageContract.csproj` ```xml= <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="NetCasperSDK" Version="0.9.2" /> </ItemGroup> </Project> ```