Skip to main content

Building your first Decentralized Application

A decentralized application (dApp) is one that doesn't rely on any single party to run it. It can be run by anyone, anytime, anywhere, and used for its intended purpose without relying on a central authority or backend servers.

In this tutorial, we will be building a simple, fully decentralized messaging application on the PWR Chain. This messaging application doesn't rely on any company to run it and doesn't use any conventional backend servers or databases. The only infrastructure it uses is the PWR Chain, and the parties who run it are the users themselves.

Prerequisites

Before starting, make sure you have the following:

  • A development environment set up for your preferred programming language.
  • PWR SDK installed and configured in your project.
  • You have finished reading the SDK guides.

Step 1: Set Up the Project

Create a new project in your preferred programming language, and add the necessary dependencies, including the PWR SDK, to your project's configuration file or build tool.

mkdir messageDapp && cd messageDapp
npm init --yes
npm install @pwrjs/core dotenv readline

NOTE: Rust developers will face some issues with file formatting compared to other languages, you can check out this project on Github.

Step 2: ENV setup to load the wallet

Create a .env file in your project folder and add your wallet's PRIVATE_KEY in the file.

PRIVATE_KEY="ADD_YOUR_PRIVATE_KEY_HERE"

Step 3: Send Messages

To send a message, we'll use the method provided by the PWR SDK to send a transaction with the message data.

Create a send_message file in your project and add the following code:

const { PWRWallet } = require("@pwrjs/core");
require('dotenv').config();

// Setting up your wallet in the SDK
const privateKey = process.env.PRIVATE_KEY;
const wallet = new PWRWallet(privateKey);

async function sendMessage() {
const obj = { message: "Hello World!" };
const data = Buffer.from(JSON.stringify(obj), 'utf8');
const vmId = 1234;

const res = await wallet.sendVMDataTxn(vmId, data);
console.log(res.transactionHash);
}
sendMessage();

Step 4: Fetch Messages

To fetch messages from the PWR Chain, we'll use the method provided by the PWR SDK to retrieve transactions within a range of blocks.

Create a sync_messages file in your project and add the following code:

const { PWRJS } = require("@pwrjs/core");

// Setting up your wallet in the SDK
const rpc = new PWRJS("https://pwrrpc.pwrlabs.io/");

async function sync() {
let startingBlock = 880920;
const vmId = 1234;

const loop = async () => {
try {
const latestBlock = await rpc.getLatestBlockNumber();
let effectiveLatestBlock = latestBlock > startingBlock + 1000 ? startingBlock + 1000 : latestBlock;

if (effectiveLatestBlock > startingBlock) {
// fetch the transactions in `vmId = 1234`
const txns = await rpc.getVMDataTransactions(startingBlock, effectiveLatestBlock, vmId);
for (let txn of txns) {
const sender = txn.sender;
const dataHex = txn.data;
// Remove the '0x' prefix and decode the hexadecimal data to bytes data
const data = Buffer.from(dataHex.substring(2), 'hex');
// convert the bytes data to UTF-8 string as json
const object = JSON.parse(data.toString('utf8'));

Object.keys(object).forEach(key => {
if (key.toLowerCase() === "message") {
console.log(`\nMessage from ${sender}: ${object[key]}`);
} else {
// Handle other data fields if needed
}
});
}
startingBlock = effectiveLatestBlock + 1;
}
setTimeout(loop, 1000); // Wait 1 second before the next loop
} catch (e) {
console.error(e);
}
};
loop();
}
module.exports = { sync };

Step 5: Build the DApp

Now that we have the individual components for sending messages, and fetching messages, let's put them all together in a complete application.

The final implementation should look like this:

  1. Run the project to fetch and send messages.
  2. The application keeps fetching messages without stopping.
  3. Write a message and click Enter to send.
  4. Fetch the message to you.

Create a dapp file in your project and add the following code:

const { PWRWallet } = require("@pwrjs/core");
const { sync } = require("./sync_messages.js");
const readline = require("readline");
require('dotenv').config();

// Setting up your wallet in the SDK
const privateKey = process.env.PRIVATE_KEY;
const wallet = new PWRWallet(privateKey);

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});

async function main() {
const vmId = 1234;
await sync();

const messageLoop = () => {
rl.question("", async (message) => {
const object = { message };
// Send the VM data
const response = await wallet.sendVMDataTxn(vmId, Buffer.from(JSON.stringify(object), 'utf8'));
!response.success && console.log('FAILED!');
messageLoop(); // Recursively ask for the next message
});
};

messageLoop();
}
main();

Finally, we enter a loop where the user can input messages, and each message is sent as a transaction to the PWR Chain using the sendVmDataTransaction method.

Step 6: Run the Application

To run the messaging application, add the following command:

node dapp.js

And that's it! You have now built a simple, decentralized messaging application on the PWR Chain. Users can run this application on their own machines, and all the messages will be stored and retrieved from the PWR Chain itself.

Conclusion

In this tutorial, we explored the process of building a decentralized messaging application on the PWR Chain using the PWR SDK. We covered creating or loading a wallet, fetching messages from the chain, sending messages as transactions, and putting all the components together in a complete application.

Remember, this is a basic example to demonstrate the concepts. In a production-ready application, you would need to consider additional factors such as security, error handling, performance optimization, and user interface design.

The power of decentralized applications lies in their ability to operate independently, without relying on a central authority or infrastructure. By leveraging the capabilities of the PWR Chain, developers can build innovative and resilient applications that empower users and promote decentralization.