---
# System prepended metadata

title: A guide to building a dApp on Gno.land using Adena and gno-js client
tags: [gnolang, gnoland, gno]

---


**Introduction**
Gno.land is a next-generation layer-1 blockchain platform that leverages cutting-edge components such as Tendermint2 and GnoVM to provide a secure and scalable infrastructure. Unlike other blockchain solutions, it uses the innovative Proof-of-Contributions consensus mechanism. Developers who are welcome to build their usecases on the chain. 

**Starter Guide**

For a starter guide to this tutorial, you can check out the codebase [here](https://github.com/amoweolubusayo/gno-demo)

**Demo**

https://gno-demo.vercel.app

**Basic Terms**

Before you proceed, make sure to have the following background knowledge of the following

* **Adena** : Adena is the non-custodial wallet that currently supports transactions and asset managements on Gno.land. It supports NFTs, custom tokens and can be easily integrated with your dApp. To get started visit adena's [docpage](https://docs.adena.app)
* **Realms**: In Gno.land, smart contracts are refered to as Realms. Realms are written in Gnolang and are stateful. Realms support arguments, data handling, import and access modifiers. To read more about Realms, check out the [docs](https://docs.onbloc.xyz/docs/building-a-realm/overview) 
* **Packages**: Packages in Gno.land have functionalities that are have same capabilities like realms. More information about packages are [here](https://docs.onbloc.xyz/docs/packages)

**Setting up your environment**

Kindly ensure you have all the necessary tools installed. The following should be installed on your computer

* Git - You can download the installer [here](https://git-scm.com/downloads) or run this command in your terminal 
`sudo apt update; sudo apt install git-all -y;`
* Go - You can download the installer [here](https://go.dev/dl) or run this command in your terminal 
`wget -q -O - https://git.io/vQhTU | bash -s - --version 1.19`

>Also make sure to have a package manager installed on your machine. yarn and npm are perfect managers

Once you have `Git` and `Go` installed, go ahead to clone the [gno repository](https://github.com/gnolang/gno). Run this command 

```bash
git clone https://github.com/gnolang/gno
```
Your output should be similar to this
![](https://hackmd.io/_uploads/H1lrS26th.png)

For the next step, we install `gnokey`, `gno` and build gnoland. To do that, run the following command one after the other

```bash
cd gno && make install
```
Here's the output for this command

![](https://hackmd.io/_uploads/Sk0o5a6Kn.png)

```bash
cd gno.land && make install.gnoland && make install.gnoweb
```
Here's the output for this command

![](https://hackmd.io/_uploads/ryEPja6Kh.png)

For last steps, we just need to build tendermint2. Run this command 

```bash
cd - && cd tm2 && make install
```
Here's the output for this command

![](https://hackmd.io/_uploads/SkG7Tppt2.png)

You can confirm that your set-up is done correctly using the '-h' flag in any of the folders. You can run

```bash
gnoweb -h
```
![](https://hackmd.io/_uploads/HkrqV1CYh.png)

There you have it folks :confetti_ball: We have our environment set-up.

# Build and Deploy a Local Testnet(optional)

You can start a local node on your own machine. Just run this command

```bash
gnoland start
```
Here's the output of this command

![](https://hackmd.io/_uploads/B1OwmWAYn.png)


## Build your dApp
Next, let us build a dApp that will implement gno-js client. The @gnolang/gno-js-client is a JavaScript/TypeScript client implementation for Gno chains. We will build something simple to allow us run some VM-specific ABCI queries with the click of a button 

First, start by setting up a Next-JS application. Run the following command in your terminal 

```bash
npx create-next-app@latest
```
![](https://hackmd.io/_uploads/Sk8K6iAt2.png)

After a successful installation, let's tweak the template. Run this command

```bash
cd demo-gno && code .
```
Once your IDE is up, you can install the gno-js-client already. In the terminal, run this command 

```bash
yarn add @gnolang/gno-js-client
```

![](https://hackmd.io/_uploads/Bys2AoRth.png)

Navigate to the src folder and find `pages.tsx`. Replace the code with this basic code

```javascript
"use client";
import React, { useState } from "react";
import {
  GnoJSONRPCProvider
} from "@gnolang/gno-js-client";

export default function Home() {
  const [message, setMessage] = useState("");
  const [expression, setExpression] = useState("");
  const [packagePath, setPackagePath] = useState("");

  const handleButtonClick = async () => {
    try {
      const provider = new GnoJSONRPCProvider("http://test3.gno.land:36657");
      const output = await provider.evaluateExpression(packagePath, expression);
      setMessage(output);
    } catch (error) {
      console.log(error);
      setMessage("Error: Unable to retrieve render output");
    }
  };

  const handleButtonClick2 = async () => {
    try {
      const provider = new GnoJSONRPCProvider("http://test3.gno.land:36657");
      const output = await provider.getFileContent(packagePath);
      setMessage(output);
    } catch (error) {
      console.log(error);
      setMessage("Error: Unable to retrieve file content");
    }
  };

  return (
    <div className="container mx-auto py-8 text-center">
      <h1 className="text-2xl font-bold mb-4">Example demo sdk usage</h1>
      <input
        type="text"
        placeholder="Enter package path..."
        className="border border-gray-300 rounded px-4 py-2 mb-4 text-black"
        value={packagePath}
        onChange={(e) => setPackagePath(e.target.value)}
      />
      <input
        type="text"
        placeholder="Enter expression..."
        className="border border-gray-300 rounded px-4 py-2 mb-4 text-black"
        value={expression}
        onChange={(e) => setExpression(e.target.value)}
      />
      <div className="space-x-4">
        <button
          onClick={handleButtonClick}
          className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded"
        >
          Evaluate expression
        </button>
        <button
          onClick={handleButtonClick2}
          className="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded"
        >
          Display File contents
        </button>
      </div>
      {message && <div className="text-xl font-bold mt-4">{message}</div>}
    </div>
  );
}
```
Let's take a bit to explain some bits of this code, we need the provider to make calls so we import `GnoJSONRPCProvider` from `@gnolang/gno-js-client`

```typescript
import {
  GnoJSONRPCProvider
} from "@gnolang/gno-js-client";
```

We also need to create an instance of the provider so we can use one of the testnets RPC url

```typescript
const provider = new GnoJSONRPCProvider("http://test3.gno.land:36657");
```
Two methods are called here: `evaluateExpression` and `getFileContent`

```typescript
const output = await provider.evaluateExpression(packagePath, expression);

const output = await provider.getFileContent(packagePath);
```

We also have input fields that will handle the parameters. Things are all set and we can run the code.

Run the following command

```bash
yarn dev
```
Go to your broswer on localhost:3000 

Click on `Display file contents` to see the list of contents in a package of your choice.

![](https://hackmd.io/_uploads/HyX88pRF3.png)

Click on `Evaluate expression` to evaluates any expression in readonly mode and return the results for a package of your choice.

![](https://hackmd.io/_uploads/r15DU6AKn.png)

>  To find packages, go to your already set-up gno.land environment via the CLI and run this command 
>  ```bash
>  gnoweb main
>  ```
>  This will redirect you to http://127.0.0.1:8888 where you can explore packages. An image describing this is shown below

![](https://hackmd.io/_uploads/r1Tplyy53.png)


Next, let us add a wallet connection feature to our dapp. Create a new function `handleConnectWallet`. This function will check if you have the Adena extension and connect and if not, you are directed to the website. 

Here is the function to handle that
```javascript
  const handleConnectWallet = async () => {
    try {
      // Check if Adena wallet object exists
      if (!window.adena) {
        //open adena.app in a new tab if the adena object is not found
        window.open("https://adena.app/", "_blank");
      }

      // Connect to Adena wallet
      await adena.AddEstablish("Adena");

      //Get Account details
      const account = await adena.GetAccount();
      console.log(account.address);
      setConnectedAccount(account.address);
      // Set the wallet connection status
      setWalletConnected(true);
    } catch (error) {
      console.log(error);
      setMessage("Error: Unable to connect wallet.");
    }
  };
```
And here is the implementation

```typescript
 <div className="flex justify-end">
        {!walletConnected ? (
          <button
            className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded"
            onClick={handleConnectWallet}
          >
            Connect Wallet
          </button>
        ) : (
          <div className="flex items-center">
            <button
              className="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded"
              disabled
            >
              Connected
            </button>
            <span className="ml-2 text-sm text-white">{connectedAccount}</span>
          </div>
        )}
      </div>
```

![](https://hackmd.io/_uploads/r1kqMC0t3.png)

Once you click on Connect Wallet, you will get a pop-up asking you to connect

![](https://hackmd.io/_uploads/Hkeq70Rtn.png)

Click connect and wait for your page to get updated

![](https://hackmd.io/_uploads/r1bk40RY3.png)


There you have it, the adena wallet has a beautiful experience in terms of implementation and it's a basis for many other use-cases.  
