# 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>
```