Decentralized journaling — What I learned writing my first solidity project

Nicholas Singh
6 min readJul 31, 2022

--

Etherium logo

Web3 and mental health. Two of the hottest topics right now. As a developer, I thought it would be interesting to learn solidity and create a project with the potential to help people.

Initially, this project incorporated features such as machine learning to track a person’s emotions over time and a global feed for everyone to share their ideas. However, while developing the project I ran into some technical limitations that would have caused the project to be significantly delayed. Namely, getting the machine learning models to deploy. So, I decided that it would be best to continue the project without having them integrated. Also, for the global feed, I learned that the data in a contract is specific to the wallet that runs that contract. Think of it as creating a new instance of a class, where the class is the contract and the data in the class is only accessible through the wallet. Due to this, the global feed isn’t functional.

Site tech stack ⚛️

First things first, a quick explanation of the tech stack for the website. The frontend was built in NextJS and because we’re using smart contracts to store the data we don’t exactly have a traditional REST/GraphQL backend. Rather, we create contracts that get deployed to a blockchain network. We can then interface with these networks using a JSON file to integrate into our frontend.

NOTE: the contracts are deployed to the Etherium Rinkeby test networks

Understanding Web3

This section is intended for people who are unfamiliar with Web3. It’ll explain how the different components of web3 interact with each other in this application and some of the web3 terms I mentioned.

First of all, like how Javascript is the language of the web, solidity is the language for blockchain; more specifically, the Ethereum blockchain. Using solidity you can create things such as smart contracts and NFTs. The language is similar to Java and typescript in terms of syntax.

To add, a smart contract is a piece of code that you can run on the blockchain that executes commands written by someone on the blockchain. For example, if I want to give someone 1 Dogecoin after they sell me an NFT then I can write a program that will transfer the NFT from their wallet to mine and the Dogecoin from my wallet to theirs.

How the web3 integration works

This is cool and all, but how do these different parts of the project interact with each other? In this project we’ll have a smart contract that holds users’ journals. They can create a new one, change properties on an old one (such as if it’s public or not) and have access to all their journals. First, the user would connect their MetaMask wallet and then they would be able to use our contracts that we made through the frontend. As mentioned above, the contract’s instance to their wallet; so they would be the only wallet with access to their journals.

Diving into the code

Quick side note: Because this article is more focused on the solidity part, I won’t be covering much of the React code. Also, I want to give a shoutout to Kitwind for their amazing tailwind css kit which saved me hours in styling (https://kitwind.io/products/kometa)

Now, I’m only going to be explaining how the code works in general, not line by line so if you’re interested in viewing the contract as a whole, check out: https://github.com/Traybot360/decentralized-journaling/blob/master/truffle/contracts/Journal.sol

In addition, I’m using truffle to deploy the contracts feel free to check out their documentation on how to create a new project. We’re going to have one contract (excluding the migrations contract that truffle provides).

  • Step 1: make an array holding all your journals

This part is fairly straight forward, we create a mapping also known as an array and we store the amount of posts in a counter variable

uint256 public postCount = 0; // number of posts 
mapping(uint256 => Post) public posts;
  • Step 2: Create the Structs and events

A struct is an object so we’re going to create a struct for a post, containing the post id, title, content, time of creation, if its published or not, sentiment (an integer rating which is a rating from 1–10 of how someone is feeling), and the strongest sentiment of that post and the author of the post.

A struct is an object so we’re going to create a struct for a post, containing the post id, title, content, time of creation, if its published or not, an integer called sentiment rating which is a rating from 1–10 of how someone’s feeling, the strongest sentiment of that post and the author of the post.

// post struct that contains the data of a post     
struct Post {
uint256 id;
string title;
string content;
uint256 timestamp;
bool isPublished;
uint256 sentimentRating;
string sentiment;
address payable author;
}
// event for when a post is published
event PostCreated(
uint256 id,
string title,
string content,
uint256 timestamp,
bool isPublished,
uint256 sentimentRating,
string sentiment,
address payable author
);
  • Step 3: Lets create a method that allows the user to create the post

All we do here is create a function that creates a new post struct, adds that post to the posts mapping and increases the post count. After this it would then emit the Post created event. We can make sure that the person creating the post is from a valid address and that the post actually has the title and content using the requires.

function createPost(
string memory _title,
string memory _content,
uint256 _sentimentRating,
string memory _sentiment,
bool _isPublished
) public {
// require that the post is not empty
require(bytes(_title).length > 0);
require(bytes(_content).length > 0);
// make sure the sender is valid
require(msg.sender != address(0x0));
// increase the post count
postCount++;
// create the post
Post memory _post;
_post.id = postCount;
_post.title = _title;
_post.content = _content;
_post.timestamp = now;
_post.isPublished = _isPublished;
_post.author = msg.sender;
_post.sentiment = _sentiment;
_post.sentimentRating = _sentimentRating;
posts[postCount] = _post;
// trigger the event
emit PostCreated(
_post.id,
_post.title,
_post.content,
_post.timestamp,
_post.isPublished,
_post.sentimentRating,
_post.sentiment,
_post.author
);
}

That’s all the solidity we’re going to write. After this you would use the truffle migrate command to deploy your contract to the blockchain.

Frontend Integration

Now, the frontend integration. MetaMask ‘Injects’ etherium into the browser. So to interact with web3 I created a custom hook. The hook connects to the MetaMask provider and it then loads the deployed version of the contract from the Journal.json file that was generated when the truffle migrate command gets run. When using the hook, we get access to the account id, the post count and the journal contract.

import { useState, useEffect } from "react";
import Web3 from "web3";
import Journal from "../../../truffle/build/contracts/Journal.json";const useLoadWeb3 = () => {
const [account, setAccount] = useState(null);
const [postCount, setPostCount] = useState(null);
const [journal, setJournal] = useState(null);
// connect to the metamask provider and add web3 to the window
const injectWeb3 = async () => {
if (window.ethereum) {
try {
// load accounts and save the first one
const accounts = await window.ethereum.request({
method: "eth_requestAccounts",
});
setAccount(accounts[0]);
window.web3 = new Web3(window.ethereum);
} catch (error) {
alert(error);
}
}
};
// load the journal contract
const loadContract = async () => {
const web3 = window.web3;
// get the network id
const networkId = await web3.eth.net.getId();
const networkData = Journal.networks[networkId];
if (networkData) {
const journal = new web3.eth.Contract(Journal.abi, networkData.address);
setJournal(journal);
const postCount = await journal.methods.postCount().call();
setPostCount(postCount);
} else {
alert("Contract not deployed");
}
};
useEffect(() => {
(async () => {
await injectWeb3();
await loadContract();
})();
}, []);
return { account, postCount, journal };
};
export default useLoadWeb3;

Hopefully the structure of how I setup the site makes sense now! If you’re interested in checking out the full repo here’s the link.

Now, there’s alot more to explore in the web3 space like ipfs, nfts, tokenomics (incentive structures) and more. I’m looking forward to building more projects and playing around with blockchain technology. That’s all for this article! See you in the next project!

Feel free to connect:

Twitter: @nicholassinghh, Github: @traybot360

--

--