# Learn & Build Your First Dapp on Aptos Blockchain

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721671511202/fb7acbe7-a2fa-4515-8017-52e7c6431f3b.jpeg align="center")

## Introduction

Hey there, future blockchain wizard! 🧙‍♂️✨

In this article, we’re diving into the magical world of Aptos to build our very first full-stack Dapp. Whether you’re a Web3 newbie or a seasoned developer looking to conquer Aptos, this guide has got you covered. 🌐💪

By the end of this article, you’ll have a fully functional Dapp running on the Aptos blockchain. 🚀Feel the excitement? Let’s dive in and build something amazing together! 😊

### What is a Dapp 🤔?

> Dapp? D for ? let's understand

Imagine a Dapp (decentralized application) as a magical app that lives on a blockchain instead of a regular server. Because of this, it’s super secure, transparent, and can’t be easily shut down or messed with. 🛡️✨

These apps use smart contracts, which are like digital wizards that automatically follow the rules written in their code. This means Dapps can run on their own without needing a middleman. 🧙‍♂️💻

So, in short, Dapps are like enchanted apps that bring the magic of blockchain to life! 🪄🌐

### Importance of Dapps in the Blockchain Ecosystem

Blockchains are distributed networks of nodes running as a global computer for computing your logic with no control and full transparency

Dapps play a crucial role in the blockchain ecosystem by enhancing security and transparency. Since a single entity doesn’t control them, they resist censorship. Smart contracts allow Dapps to operate autonomously, reducing the need for middlemen and helps in cost-cutting.

Let's cook 🍳 our first Dapp on the Aptos blockchain!

### Overview of Aptos blockchain 🌐

The Aptos blockchain is designed with **scalability, safety, reliability, and upgradeability** as key principles, to match the path of **cloud infrastructure** as a **trusted, scalable, cost-efficient, and continually improving platform** for building widely-used applications.

key features that you can find only in Aptos Blockchain

* It integrates and internally **uses the Move language** for fast and secure transaction execution ⚡
    
* Through its parallel execution engine, It can efficiently support atomicity with arbitrarily complex transactions, enabling **higher throughput and lower latency for real-world applications** and simplifying development.
    
* **Developer-Friendly**: It offers tools and resources to make it easier for developers to build and deploy Dapps.
    

Aptos is designed to support a wide range of applications, from finance to gaming, making it a versatile platform in the blockchain space. Read the [whitepaper here.](https://aptosfoundation.org/whitepaper)

Now you understand all the basics, turn on your desktop and put your hands on the keyboard to build your first application on Aptos!

If you're a developer transitioning from Ethereum to Aptos go through [this link](https://aptos.dev/en/build/get-started/ethereum-cheatsheet) it will help you to relate and understand the terms easily.

## Setting Up Your Development Environment🧑‍💻

### Installing Node.js

1. **Download Node.js**:
    
    * Go to the Node.js [official website.](https://nodejs.org/en)
        
    * Download the LTS version suitable for your operating system.
        
2. **Install Node.js**:
    
    * Run the installer and follow the prompts.
        
    * Verify the installation by opening your terminal and typing:
        
        ```bash
        node -v
        npm -v
        ```
        
    * You should see the versions of Node.js and npm displayed.
        

### Setting up Aptos CLI

1. To install Aptos CLI for your specific system go through the following links
    
    * [**MAC**](https://aptos.dev/en/build/cli/install-cli/install-cli-mac)
        
    * [**Wind**](https://aptos.dev/en/build/cli/install-cli/install-cli-windows)[**ows**](https://aptos.dev/en/build/cli/install-cli/install-cli-mac)
        
    * [**L**](https://aptos.dev/en/build/cli/install-cli/install-cli-linux)[**inu**](https://aptos.dev/en/build/cli/install-cli/install-cli-windows)[**x**](https://aptos.dev/en/build/cli/install-cli/install-cli-mac)  
          
        If you would like to update the Aptos CLI to the latest version via script, you can run `aptos update`
        
2. Verify the installation by opening your terminal and enter this command:  
    
    ```bash
    aptos --version
    ```
    

Now you're ready with your tools 🛠️ to build your next product on Aptos!

## Basics of Move Programming Language 🦾

### Introduction to Move

The Move programming language was originally created by a team of engineers at Facebook for the Diem Payment Network. Move is designed to be a platform-agnostic language to enable common libraries, tooling, and developer communities across diverse blockchains with vastly different data and execution models.

### Why Move 🔑?

Move on Aptos supports the full language built by the team at Facebook, with additional extensions built to improve the security and the developer experience.

Aptos contracts are written using Move, a next generation language for secure, sandboxed, and formally verified programming which is used for multiple chains. Move allows developers to write programs that flexibly manage and transfer assets while providing security and protections against attacks on those assets.

Resources to learn Move Language :  
[Move tutorial](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/move-tutorial)  
[Move Book](https://aptos.dev/en/build/smart-contracts/book)  
[Aptos Move by example](https://move-developers-dao.gitbook.io/aptos-move-by-example)  
[Aptos Learn](https://learn.aptoslabs.com/)

### Writing a simple Move smart contract &lt;/&gt;

**hello\_blockchain.move**

```rust
module hello_blockchain::message {
    use std::error;
    use std::signer;
    use std::string;
    use aptos_framework::event;

    //:!:>resource
    struct MessageHolder has key {
        message: string::String,
    }
    //<:!:resource

    #[event]
    struct MessageChange has drop, store {
        account: address,
        from_message: string::String,
        to_message: string::String,
    }

    /// There is no message present
    const ENO_MESSAGE: u64 = 0;

    #[view]
    public fun get_message(addr: address): string::String acquires MessageHolder {
        assert!(exists<MessageHolder>(addr), error::not_found(ENO_MESSAGE));
        borrow_global<MessageHolder>(addr).message
    }

    public entry fun set_message(account: signer, message: string::String)
    acquires MessageHolder {
        let account_addr = signer::address_of(&account);
        if (!exists<MessageHolder>(account_addr)) {
            move_to(&account, MessageHolder {
                message,
            })
        } else {
            let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
            let from_message = old_message_holder.message;
            event::emit(MessageChange {
                account: account_addr,
                from_message,
                to_message: copy message,
            });
            old_message_holder.message = message;
        }
    }

    #[test(account = @0x1)]
    public entry fun sender_can_set_message(account: signer) acquires MessageHolder {
        let addr = signer::address_of(&account);
        aptos_framework::account::create_account_for_test(addr);
        set_message(account, string::utf8(b"Hello, Blockchain"));

        assert!(
            get_message(addr) == string::utf8(b"Hello, Blockchain"),
            ENO_MESSAGE
        );
    }
}
```

You can learn more about Move smart contracts in [Aptos official documentation](https://aptos.dev/en/build/get-started). Now we will move towards integration and client-server setup for our fullstack application.

## Building Your First Dapp on Aptos ⚙️

**We are going to make a Simple On-Chain Counter application to understand the functions of Move and integrations to smart contract through frontend 🚀.**

1. Create root directory `aptos-counter-dapp` on your desktop.  
    
2. First, we will create Move Smart contracts which will contain all the functions to raise, decrease and view the counter.
    

1. `cd` into the `aptos-counter-dapp` root directory, and create a new `move` directory.
    
2. `cd` into the new `move` directory and run: `aptos move init --name counter` This command creates a `sources/` directory and a `Move.toml` file inside the `move` directory.
    
3. The `sources` directory holds a collection of `.move` module files. When we compile the package using the CLI, the compiler will look for the `sources` directory and its `Move.toml` file.  
    
4. **Let's create a move module.** In our move directory, run `aptos init --network testnet` ( we are going to deploy our application on Testnet )  
      
    It will create `.aptos` a folder in your `move` directory. Go to that folder you will find `config.yaml` file, open it you will find the details resembling below image  
      
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721653561679/82af6b46-c2ed-41be-8949-ffa76a55d30b.png align="center")
    
      
    
5. Open the `Move.toml` file. Add the following code to that Move file, substituting your actual default profile account address from `.aptos/config.yaml`  
      
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721653579689/108e38f1-3d19-4007-9585-d530b8836f9c.png align="center")
    
      
    
6. Now you can create a new file `increase.move` module into your `source` directory
    
    ```rust
    module increase_add::increase {
    
    }
    ```
    
    Here `increase_add` is the variable we just declared in `Move.toml` and `increase` is the random name we selected for the module  
    
7. Add our contract logic, compile and deploy it  
    \- we need one variable to store our counter  
    \- function for initializing the counter for specific address  
    \- two separate functions for increasing and decreasing the counter as user prompted  
    \- add some basic test cases  
      
    This is the overall logic we need to write in Move language in `increase.move` the file. Let's write our code step by step  
    
    1. Writing our `move module`
        
        1. Add dependencies and create simple `struct` for holding `counter` variable
            
        
        ```rust
        module increase_add::increase {
            use std::signer;
            
            #[test_only]
            use aptos_framework::account;
        
            struct Count has key {
                count: u64
            }
        }
        ```
        
        2. Add a function to initialize `counter` variable for specific addresses
            
        
        ```rust
        // remaining code
        
        // for intializing counter variable for the specific smart contract
        
            public entry fun createcounter(account: &signer) {
                let addr = signer::address_of(account);
            
                if (!exists<Count>(addr)) {
                    move_to(account, Count { count: 0 });
                }
            }
               const E_NOT_INITIALIZED: u64 = 1;
        }
        ```
        
        3. Now add final functions to increase and decrease the counter
            
        
        ```rust
        module increase_add::increase {
            use std::signer;
            
            #[test_only]
            use aptos_framework::account;
        
            struct Count has key {
                count: u64
            }
        
          // for intializing counter variable for the specific smart contract
        
            public entry fun createcounter(account: &signer) {
                let addr = signer::address_of(account);
            
                if (!exists<Count>(addr)) {
                    move_to(account, Count { count: 0 });
                }
            }
        
        
           const E_NOT_INITIALIZED: u64 = 1;
        
        // function for raising the counter value 
            public entry fun raise_c(account: &signer) acquires Count {
                let signer_add = signer::address_of(account);
                assert!(exists<Count>(signer_add), E_NOT_INITIALIZED);
                let number_p = borrow_global_mut<Count>(signer_add);
                let counter = number_p.count + 1;
                    number_p.count = counter;
            }
        
         // function for decrementing the counter value   
        
            public entry fun decrement_c(account: &signer) acquires Count {
                let signer_add = signer::address_of(account);
                assert!(exists<Count>(signer_add), E_NOT_INITIALIZED);
                let number_p = borrow_global_mut<Count>(signer_add);
                 let counter = number_p.count - 1;
                    number_p.count = counter;
        
            }
        
            #[test(admin = @0x123)]
            public entry fun test_flow(admin: signer) acquires Count {
                account::create_account_for_test(signer::address_of(&admin));
                createcounter(&admin);
                raise_c(&admin);
                decrement_c(&admin);
            }
            
        }
        ```
        

This is the final code for our `move module`

2. Compile & deploy your `increase.move` module  
    Run command `aptos move compile` to compile your move module before publishing it to the testnet  
      
    then test `aptos move test` , see if you get similar output shown below
    
    ```bash
    PS D:\aptos\aptos-counter-dapp\move> aptos move compile
    Compiling, may take a little while to download git dependencies...
    UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-core.git
    INCLUDING DEPENDENCY AptosFramework
    INCLUDING DEPENDENCY AptosStdlib
    INCLUDING DEPENDENCY MoveStdlib
    BUILDING counter
    {
      "Result": [
        "71461a8e2ddbfd6a74152c8a86df4a3b60d73b2fd9be872f9a8585501edcdd23::increase"
      ]
    }
    PS D:\aptos\aptos-counter-dapp\move> aptos move test
    INCLUDING DEPENDENCY AptosFramework
    INCLUDING DEPENDENCY AptosStdlib
    INCLUDING DEPENDENCY MoveStdlib
    BUILDING counter
    Running Move unit tests
    [ PASS    ] 0x71461a8e2ddbfd6a74152c8a86df4a3b60d73b2fd9be872f9a8585501edcdd23::increase::test_flow
    Test result: OK. Total tests: 1; passed: 1; failed: 0
    {
      "Result": "Success"
    }
    ```
    
3. Deploy/publish your smart contract to the testnet  
    If all you come to this step without any error  
    \- Run: `aptos move publish`  
    \- Enter `yes` in the prompt.  
    \- That will compile, simulate, and finally publish your module into devnet. You should see a success message:
    
    ```bash
    PS D:\aptos\aptos-counter-dapp\move> aptos move publish
    Compiling, may take a little while to download git dependencies...
    UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-core.git
    INCLUDING DEPENDENCY AptosFramework
    INCLUDING DEPENDENCY AptosStdlib
    INCLUDING DEPENDENCY MoveStdlib
    BUILDING counter
    package size 1505 bytes
    Do you want to submit a transaction for a range of [150700 - 226000] Octas at a gas unit price of 100 Octas? [yes/no] >
    yes
    Transaction submitted: https://explorer.aptoslabs.com/txn/0x4d158ff6189b0ca05ed8cd3a952594f4a808796b6c014038b7a4e115dcb20e7c?network=testnet
    {
      "Result": {
        "transaction_hash": "0x4d158ff6189b0ca05ed8cd3a952594f4a808796b6c014038b7a4e115dcb20e7c",
        "gas_used": 1507,
        "gas_unit_price": 100,
        "sender": "71461a8e2ddbfd6a74152c8a86df4a3b60d73b2fd9be872f9a8585501edcdd23",
        "sequence_number": 0,
        "success": true,
        "timestamp_us": 1721653299563427,
        "version": 5511713975,
        "vm_status": "Executed successfully"
      }
    }
    ```
    

You can refer video guide if you encounter problems while deploying your move modules 💡  

%[https://youtu.be/VMDeJY5GB9g] 

Now we have created our required move module `increase.move` & successfully deployed it to the testnet, you can find it live on Explorer via this link

[Explorer Link](https://explorer.aptoslabs.com/account/0x71461a8e2ddbfd6a74152c8a86df4a3b60d73b2fd9be872f9a8585501edcdd23/modules/code/increase?network=testnet)  
  
**Let's design our front-end side**

1. In the root folder of the `aptos-counter-dapp` project, run:  
    
    ```bash
    npx create-react-app client --template typescript
    ```
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721655821340/12ace4c4-9607-4a9c-ae06-35d4f60c061b.png align="center")

Our simple UI will look like this

2. Now in your client folder, you need to install some dependencies  
    `npm i antd@5.1.4`  
    `npm i @aptos-labs/wallet-adapter-react`  
    `npm i @aptos-labs/wallet-adapter-ant-design`  
    `npm i petra-plugin-wallet-adapter`
    

We are working with Petra Wallet here, the above dependencies are for connecting the wallet with the react code  
If you haven’t installed the Petra wallet extension yet:

* [Install Petra Wal](https://petra.app/)[let and open the Chr](https://petra.app/)ome extension.
    
* Follow the [user instr](https://petra.app/docs/use)[u](https://petra.app/docs/use)[ctions on](https://petra.app/) [petra.app](http://petra.app) [](https://petra.app/)for help.
    
* Switch to the Devnet network by clicking **Settings** &gt; **Network** and selecting **testnet**.
    
* Click the **Faucet** button to ensure you can receive test tokens.  
      
    Here I don't want to dive more into front-end libraries that we need to import, you can follow step by step guide to connect wallet to your front-end [here](https://aptos.dev/en/build/guides/build-e2e-dapp/3-add-wallet-support)
    

3. The main part and logic come while integration of smart contracts into your frontend code - there are some basic practices you can follow while interacting with smart contracts
    
      
    you need `provider` , `network` and `module address` to interact with smart contracts  
      
    \- to fetch any data from the blockchain you can write such a function
    
    ```typescript
    const fetch = async () => {
        if (!account) return;
        try {
          const todoListResource = await provider.getAccountResource(
            account?.address,
            `${moduleAddress}::increase::Count`,
          );
          let data = JSON.parse((todoListResource?.data as any).count);
          setCounter(data);
          if (reload) {
            window.location.reload();
          }
        }
        catch (e: any) {
          create_c();
        }
      }
    ```
    
    * to write and change state of variable in blockchain you can write such function
        
        ```typescript
        const raise_cCounter = async () => {
            setTransactionInProgress(true);
            // build a transaction payload to be submited
            const payload = {
              type: "entry_function_payload",
              function: `${moduleAddress}::increase::raise_c`,
              type_arguments: [],
              arguments: [],
            };
            try {
              // sign and submit transaction to chain
              const response = await signAndSubmitTransaction(payload);
              // wait for transaction
              await provider.waitForTransaction(response.hash);
              window.location.reload();
            } catch (error: any) {
              console.log(error);
              // setAccountHasList(false);
            } finally {
              setTransactionInProgress(false);
            }
          };
        ```
        
    
    Now you have a basic understanding of how you can import the smart contract function to the frontend and interact with it.  
      
    
4. Just copy & paste the below code to the `app.tsx` and `index.tsx`  
      
    `app.tsx`
    

```typescript
import { WalletSelector } from "@aptos-labs/wallet-adapter-ant-design";
import { Layout, Row, Col, Button, Spin } from "antd";

import React, { useEffect, useState } from "react";
import { useWallet,InputTransactionData } from "@aptos-labs/wallet-adapter-react";
import "@aptos-labs/wallet-adapter-ant-design/dist/index.css";
import { Network, Provider } from "aptos";

import "./index.css";
export const provider = new Provider(Network.TESTNET);
// change this to be your module account address
export const moduleAddress = "0x71461a8e2ddbfd6a74152c8a86df4a3b60d73b2fd9be872f9a8585501edcdd23";

function App() {
  const { account, signAndSubmitTransaction } = useWallet();
  const [counter, setCounter] = useState<number>(0);
  const [transactionInProgress, setTransactionInProgress] = useState<boolean>(false);
  const [accountHasList, setAccountHasList] = useState<boolean>(false);
  const [reload, setReload] = useState<number>(0);

  const fetch = async () => {
    if (!account) return;
    try {
      const todoListResource = await provider.getAccountResource(
        account?.address,
        `${moduleAddress}::increase::Count`,
      );
      let data = JSON.parse((todoListResource?.data as any).count);
      setCounter(data);
      if (reload) {
        window.location.reload();
      }
    }
    catch (e: any) {
      create_c();
    }
  }

  const create_c = async () => {
    if (!account) return [];
    setTransactionInProgress(true);
    // build a transaction payload to be submited
    const payload:InputTransactionData = {
      data: {
      
      function: `${moduleAddress}::increase::createcounter`,
      functionArguments: [],
      },
    };
    try {
      // sign and submit transaction to chain
      const response = await signAndSubmitTransaction(payload);
      // wait for transaction
      await provider.waitForTransaction(response.hash);
    } catch (error: any) {
      console.log(error);
    } finally {
      setTransactionInProgress(false);
    }
  };

  const raise_cCounter = async () => {
    setTransactionInProgress(true);
    // build a transaction payload to be submited
    const payload:InputTransactionData = {
      data: {
      
      function: `${moduleAddress}::increase::raise_c`,
      functionArguments: [],
    
      },
    };
    try {
      // sign and submit transaction to chain
      const response = await signAndSubmitTransaction(payload);
      // wait for transaction
      await provider.waitForTransaction(response.hash);
      window.location.reload();
    } catch (error: any) {
      console.log(error);
      // setAccountHasList(false);
    } finally {
      setTransactionInProgress(false);
    }
  };

  const decrement_cCounter = async () => {
    setTransactionInProgress(true);
    // build a transaction payload to be submited
    const payload:InputTransactionData = {
      data: {
      
      function: `${moduleAddress}::increase::decrement_c`,
      functionArguments: [],
      
      }
    };
    try {
      // sign and submit transaction to chain
      const response = await signAndSubmitTransaction(payload);
      // wait for transaction
      await provider.waitForTransaction(response.hash);
      window.location.reload();
    } catch (error: any) {
      console.log(error);
      // setAccountHasList(false);
    } finally {
      setTransactionInProgress(false);
    }
  };

  //Runs one Time
  useEffect(() => {
    fetch();
  }, [account?.address]);


  return (
    <>
      <div className="container mx-auto flex justify-center items-center h-[100vh] flex-col">
      <h1 className="text-5xl font-extrabold mb-20 text-center">OSM-.APT</h1>
      <Col style={{ textAlign: "right" , margin:"10px"  }}>
            <WalletSelector />
          </Col>
        <div className="w-[50%] flex justify-between mt-3">

          <div>
            <div className="group relative">
              <div className="absolute -inset-1 rounded-lg bg-gradient-to-r from-rose-400 via-fuchsia-500 to-indigo-500 opacity-75 blur transition duration-500 group-hover:opacity-100"></div>
              <button className="relative rounded-lg bg-black px-7 py-4 text-white"
              onClick={raise_cCounter}>Increase By One</button>
            </div>
          </div>

          <div><h1 className="text-8xl font-extrabold -mt-6">{counter? counter : 0}</h1></div>


          <div>
            <div className="group relative">
              <div className="absolute -inset-1 rounded-lg bg-gradient-to-r from-rose-400 via-fuchsia-500 to-indigo-500 opacity-75 blur transition duration-500 group-hover:opacity-100"></div>
              <button className="relative rounded-lg bg-black px-7 py-4 text-white"
              onClick={decrement_cCounter}>Decrease By One</button>
            </div>
          </div>

        </div>

      </div>

    </>
  );

}

export default App;
```

`index.tsx`

```typescript
import { PetraWallet } from "petra-plugin-wallet-adapter";
import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react";
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

const wallets = [new PetraWallet()];

const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(
  <React.StrictMode>
    <AptosWalletAdapterProvider plugins={wallets} autoConnect={true}>
      <App />
    </AptosWalletAdapterProvider>
  </React.StrictMode>,
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
```

Install and add tailwind for UI

`npm install -D tailwindcss`  
`npx tailwindcss init`

configure your `index.css`

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

You're finally done with your magical on-chain counter which is accessible anywhere from your specific address on Aptos blockchain, you can interact with the application and play around with the code!  
  
Run  
`npm run start` and see the magic you've crafted on the Aptos blockchain and marked 🥳your headstart into the Aptos ecosystem 🎉 🎉 🎉  
  
encountered with some bugs ? refer this video 💡 :

%[https://youtu.be/WhedSX01o-Q] 

## [C](https://petra.app/docs/use)onclusion ✅

> In this article, we guide you through building your first full-stack Dapp on the Aptos blockchain. Starting with an introduction to Dapps and their significance, we dive into the features of the Aptos blockchain and set up the development environment. We explain how to deploy a simple Move smart contract and integrate it with a React frontend to create an on-chain counter application. By the end, you'll have a functional Dapp and the foundational knowledge to explore further in the Aptos ecosystem.
> 
> Whole article is inspired by [build-e2e-dapp on Aptos documentation](https://aptos.dev/en/build/guides/build-e2e-dapp), this article can be great headstart to understand the development on Aptos and you can build more complex application using it, thank you!
> 
> Let's build on Aptos together, we're coming with more such articles in this blog series to learn and build together #CodeCollision

[Access the GitHub Code repo here](https://github.com/marotipatre/aptos-counter-dapp)

## References 🔗

### [Official documentation](https://aptos.dev/en)

### [Tutorials and guides](https://www.youtube.com/watch?v=IJ0Tm0jYN60)

### [Community forums and support channels](https://forum.aptosfoundation.org/)
