Key-Value store Developer Tutorial

Learn how to use SecretPath on EVM to encrypt payloads.

Overview

SecretPath seamlessly handles encrypted payloads on the EVM, which means EVM developers can use SecretPath to encrypt and decrypt messages cross-chain with little-to-no Rust experience required.

Check out the Key Value store demo here to see what you will build 😄

This tutorial explains how to:

  1. Upload your own Key-value store contract on Secret Network

  2. Encrypt data on the EVM and transmit encrypted data cross-chain to Secret Network

  3. Connect your key value store contract to a React frontend.

After this tutorial, you will have the tools you need to use SecretPath to encrypt messages on any EVM-compatible chain.

Getting Started

To get started, clone the EVM key value store repository:

git clone https://github.com/writersblockchain/evm-kv-store-demo.git

Configuring Environment

cd into secret-contract/node

cd secret-contract/node

Install the dependencies:

npm install

Upload the Key-value contract to Secret Network

In the node folder, open the upload.js file and review the instantiate message at line 56:

 let init = {
    gateway_address: gatewayAddress,
    gateway_hash: gatewayHash,
    gateway_key: gatewayPublicKeyBytes,
  };
  • gatewayAddress is the SecretPath gateway contract address for testnet

  • gatewayHash is the SecretPath gateway contract hash for testnet

  • gatewayKey is public key used for SecretPath encryption on Secret testnet

To upload and instantiate the contract, run node upload:

node upload

Upon successful upload, a contractHash and address will be returned:

codeId:  5701
Contract_hash: "6311a3f85261fc720d9a61e4ee46fae1c8a23440122b2ed1bbcebf49e3e46ad2"
contract_address:  "secret1j0gpu6tlwnc9fw55wcfsfuml00kqpcnqz7dck7"

If you want to make any changes to the Secret smart contract before uploading it to Secret Network, you can do that too! Simply update the contract and then compile it by running make build-mainnet:

make build-mainnet

The compiled contract, called contract.wasm.gz, will be outputted in the secret-contract folder.

Encrypt a payload

Now that you have your key value store smart contract uploaded to Secret Network, let's use it to store encrypted messages passed from the EVM. Most of the ECDH cryptography has been abstracted away so there are only a few values you need to change.

cd into evm-kv-store-demo/kv-store-frontend:

cd evm-kv-store-demo/kv-store-frontend

Install the dependencies:

npm i

Open .env and change the routing_contract and routing_code_hash to the address and code hash for your contract:

REACT_APP_SECRET_ADDRESS="secret1neeyum9p9h6u0d5jkxlqx9w5nrz49hzrr63jsw"
REACT_APP_CODE_HASH="6994d8ff9ed1af73ef4685a9f5c8a5568804afb5fa5ce49ec8496fb271a9760a"

That's it! You dApp is now connected to your Secret smart contract and ready to encrypt key value pairs. To start the application, run:

npm run start

Congrats! You now have now deployed a fullstack cross-chain decentralized application that passes encrypted key-value pairs from the EVM and stores them on Secret Network 🎉

SecretPath - a deep dive 🏊‍♀️

Now that you have successfully uploaded a key-value contract to Secret Network and connected it to a React frontend, let's examine the code to understand how SecretPath passes encrypted data from the EVM to Secret Network.

From a high-level, all that you need to know is there is a public SecretPath contract deployed on every EVM chain listed here, which communicates with a private SecretPath contract deployed on Secret Network, and it is these public/private contract pairs that encrypt your data and pass messages from the EVM to Secret Network.

When you create your cross-chain dApp, you simply need to format your data so that it can be transmitted successfully from the public SecretPath contract to the private SecretPath contract.

All of the data formatting for the public EVM contract happens in submit.js. This is almost entirely boilerplate code except for the variables handle and data:

  // The function name of the function that is called on the private contract
  const handle = "store_value";

  // Data are the calldata/parameters that are passed into the contract
  const data = JSON.stringify({
    key: key,
    value: value,
    viewing_key: viewing_key
  });

handle is the name of the function that we are executing in our private Secret smart contract, and data is the data that we pass to the function that we are executing in the Secret contract, which in the case of the key value contract is key, value, and viewing_key.

Let's now look at the Secret Network contract that we uploaded to better understand the store_value function and how it uses the parameters key, value, and viewing_key.

Understanding the Private (Secret) Contract

At line 109, we have a match statement, which includes the handle "store_value":

 // determine which function to call based on the included handle
    let handle = msg.handle.as_str();
    match handle {
        "store_value" => store_value(deps, env, msg.input_values, msg.task, msg.input_hash),
        "retrieve_value" => retrieve_value(deps, env, msg.input_values, msg.task, msg.input_hash),
        "change_value" => change_value(deps, env, msg.input_values, msg.task, msg.input_hash),
        _ => Err(StdError::generic_err("invalid handle".to_string())),
    }

Since we pass the handle store_value from your dApp to your Secret contract, it knows to execute the store_value function!

Inside of the store_value function, we use the key, value, and viewing_key parameters that we pass from the EVM in order to create a key map with our key-value pairs:

let input: InputStoreMsg = serde_json_wasm::from_str(&input_values)
        .map_err(|err| StdError::generic_err(err.to_string()))?;
  
  // create a task information store
    let storage_item = StorageItem {
        value: input.value,
        viewing_key: input.viewing_key,
    };
    
  // map task to task info
    KV_MAP.insert(deps.storage, &input.key, &storage_item)?;

This data is encrypted and stored inside of your Secret smart contract. The data can now only be revealed with a query that uses the proper viewing_key:

const secretjs = new SecretNetworkClient({
                url: "https://pulsar.lcd.secretnodes.com",
                chainId: "pulsar-3",
            });

            const query_tx = await secretjs.query.compute.queryContract({
                contract_address: process.env.REACT_APP_SECRET_ADDRESS,
                code_hash: process.env.REACT_APP_CODE_HASH,
                query: {
                    retrieve_value: {
                        key: key,
                        viewing_key: viewingKey
                    }
                },
            });

You can view the frontend component for querying your Secret contract here! 😄

Summary

In this tutorial, you've learned how to deploy a key-value store smart contract on Secret Network, encrypt and transmit messages from an EVM-compatible chain, and integrate the contract with a React frontend. Using SecretPath, you can now seamlessly handle encrypted payloads cross-chain with minimal Rust experience. By following the steps outlined, you're equipped to build decentralized applications that leverage SecretPath for secure, cross-chain communication and data storage.

If you have any questions or run into any issues, post them on the Secret Developer Discord and somebody will assist you shortly.

Last updated

Was this helpful?