DEV Community

Cover image for How to Code Solidity Smart Contract CRUD Functions: The Right Way
Gospel Darlington
Gospel Darlington

Posted on • Updated on

How to Code Solidity Smart Contract CRUD Functions: The Right Way

Introduction

Smart contract development and the Web3 economy are still in their infancy, and there is a high demand for blockchain developers globally.

According to a recent LinkedIn report, job postings containing terms like Bitcoin, blockchain, and other digital asset-related roles increased 395 percent in 2021 in the United States compared to the previous year. With a 98 percent increase, this was the fastest growing industry in history, outpacing the entire industry by four times.

Blockchain development has a greater chance of landing you that six-figure dream job of yours. And the coolest part is that most blockchain jobs are remote base, so you don’t need to worry about locations.

But how do you jump in and capitalize on this space? It's by learning how to build things with the technology.

If you are looking for a personal tutor to speed you up with web3 development kindly book a session with me.

With that being said, let’s jump into this tutorial…

Check out my YouTube channel for FREE web3 tutorials now.

Why you should Master CRUD Function in Solidity

Mastering how to create, read, update, and delete records from the blockchain is a skill you must understand. Yes, you can delete stuff from the blockchain (to an extent) using the technique you are about to learn.

CRUD skills are needed to effectively build a content-driven system, and being that the blockchain serves as a database to immutably store records, wisdom demands that you understand how to work with the technology.

Again, if you’re going to work on a blockchain project for either a company or a client, there may still be a need to remove some unnecessary records which is why you should ACE your Solidity CRUD skills now.

What if you’re working on a web3 blog project, and you know there needs to be a delete button for very bad comments. You will need a delete functionality in that case.

You see that a good knowledge of CRUD will become inevitable as you dive deep into blockchain development.

By the way, deleting a record from a blockchain network is kind of different from the way you delete from a regular database. The record will still exist in the network but will be removed from your web3 app.

The full example below will show you step by step how to accomplish this.

CRUD Functions Example

This is a smart contract that I created and tested; to see how it works, you can open it in a remix editor and run it yourself. But first, finish the tutorial; there is important information below that explains each function specifically.

Alright, let's dissect this smart contract step by step; if you're a beginner, don't worry; I wrote this smart contract example to demonstrate simplicity.

Step 1: The smart contract structure

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Blog {
  // code goes here...
}
Enter fullscreen mode Exit fullscreen mode

This is a typical smart contract structure; consider it a class in object-oriented programming because it is one in reality.

The SPDX is used to specify the smart contract's license type. Pragma refers to the version of the solidity compiler that will be used for the smart contract. Finally, the smart contract's name is Blog.

It is critical to note that the file name should always correspond to the class name; this will ensure consistency and prevent unnecessary bugs in your code.

Step 2: Defining Contract Variables

address public owner;
uint256 public activePostCounter = 0;
uint256 public inactivePostCounter = 0;
uint256 private postCounter = 0;
Enter fullscreen mode Exit fullscreen mode

Here we have two types of variables: address and uint256. An address represents a user's account, whereas uint256 represents an unsigned integer ranging from 0 to 2256–1.

The owner will hold the deployer’s account, and activePostCounter holds the number of available posts. inactivePostCounter keeps track of the deleted post, just in case, it needs to be recovered. postCounter holds the total number of posts in the smart contract, but it is private.

Step 3: Defining the Mappings

mapping(uint256 => address) public delPostOf;
mapping(uint256 => address) public authorOf;
mapping(address => uint256) public postsOf;
Enter fullscreen mode Exit fullscreen mode

authorOf() is a mapped variable that accepts a postId and returns the address of the post author. Whereas, postsOf() keep track of an author’s posts, and delPostOf() serves as a recycle bin for each author.

Step 4: Defining an Enumerable

enum Deactivated { NO, YES }
Enter fullscreen mode Exit fullscreen mode

This is used to indicate whether or not a post has been deleted. Enum converts an alphabetical value to unsigned integers beginning with zero. For example, the values NO and YES would become 0 and 1, respectively.

Step 5: Defining the Struct

struct PostStruct {
    uint256 postId;
    string title;
    string description;
    address author;
    Deactivated deleted; // We are using the enum here
    uint256 created;
    uint256 updated;
}

PostStruct[] activePosts;
PostStruct[] inactivePosts;
Enter fullscreen mode Exit fullscreen mode

Structs are used to define complex data structures, which are made up of two or more data types.

We specified in the above PostStruct that a post should have an Id, title, description, author, a deleted key, time of creation, and time of update.

Next, we made two arrays: one for active posts and one for inactive posts, or deleted posts. This method prevents data loss, but you can also use a single array and only toggle the deleted key on and off. However, this raises another issue: you'll have to read them all in your frontend app before showing the active posts. I'd like to spare you that trip.

Step 6: Defining an Event

event Action (
    uint256 postId,
    string actionType,
    Deactivated deleted,
    address indexed executor,
    uint256 created
);
Enter fullscreen mode Exit fullscreen mode

We defined a dynamic event to emit some vital information on created, updated, deleted, and restored functions respectively.

Step 7: Defining modifier

modifier ownerOnly(){
    require(msg.sender == owner, "Owner reserved only");
    _;
}
Enter fullscreen mode Exit fullscreen mode

A modifier is a function that changes the behavior of another function at compile time. We can say, it is a prerequisite for executing a function.

In the above modifier, we specified that the caller of the function to which this modifier will be attached must be the owner or deployer of the smart contract.

Step 8: Defining the Constructor

constructor() {
    owner = msg.sender;
}
Enter fullscreen mode Exit fullscreen mode

From your OOP knowledge, a constructor is always the first function to run when a class is instantiated. OOP construct is mutual to smart contracts.

The first function to run when this smart contract is deployed is the constructor, and we specified that it should make the owner the one who deployed the smart contract.

Step 9: The createPost() function

function createPost(
    string memory title,
    string memory description
) external returns (bool) {
    // Checks for empty string
    require(bytes(title).length > 0, "Title cannot be empty");
    require(bytes(description).length > 0, "Description cannot be empty");

    // Performs computations
    postCounter++;
    authorOf[postCounter] = msg.sender;
    postsOf[msg.sender]++;
    activePostCounter++;

    // Adds post to array
    activePosts.push(
        PostStruct(
            postCounter,
            title,
            description,
            msg.sender,
            Deactivated.NO,
            block.timestamp,
            block.timestamp
        )
    );

    // Emits a created event
    emit Action (
        postCounter,
        "POST CREATED",
        Deactivated.NO,
        msg.sender,
        block.timestamp
    );

    // Returns success
    return true;
}
Enter fullscreen mode Exit fullscreen mode

This function accomplishes the post-creation in five steps.

Firstly, it checks that you are sending real information to the smart contract and not just an empty string.

Secondly, it performs some computations like incrementing post counts for the blog and for the author, including making the author the post owner.

Thirdly, it adds the post into the activePosts using the PostStruct defined earlier.

Fourthly, it emits a “POST CREATED” event which is logged off on the EVM, this is essential for enriching the information captured in a transaction.

Lastly, we returned a true boolean key indicating that the entire function ran successfully.

Step 10: The updatePost() function.

function updatePost(
    uint256 postId,
    string memory title,
    string memory description
) external returns (bool) {
    // Checks for empty string
    require(authorOf[postId] == msg.sender, "Unauthorized entity");
    require(bytes(title).length > 0, "Title cannot be empty");
    require(bytes(description).length > 0, "Description cannot be empty");

    // Changes post record
    for(uint i = 0; i < activePosts.length; i++) {
        if(activePosts[i].postId == postId) {
            activePosts[i].title = title;
            activePosts[i].description = description;
            activePosts[i].updated = block.timestamp;
        }
    }

    // Emits a updated event
    emit Action (
        postId,
        "POST UPDATED",
        Deactivated.NO,
        msg.sender,
        block.timestamp
    );

    // Returns success
    return true;
}
Enter fullscreen mode Exit fullscreen mode

This function operates like the createPost() function. The difference is that when checking for an empty string, the updatePost() function ensures that the person updating the function is the post owner (author).

Next, it targets the particular post by the Id and updates the record.

Step 11: The showPost() function

function showPost(
    uint256 postId
) external view returns (PostStruct memory) {
    PostStruct memory post;
    for(uint i = 0; i < activePosts.length; i++) {
        if(activePosts[i].postId == postId) {
            post = activePosts[i];
        }
    }
    return post;
}
Enter fullscreen mode Exit fullscreen mode

This simply returns our post by recursively searching for a match in the activePost array.

Step 12: The getPosts() function.

function getPosts() external view returns (PostStruct[] memory) {
    return activePosts;
}
Enter fullscreen mode Exit fullscreen mode

Returns all available active blog posts.

Step 13: The getDeletedPost() function

function getDeletedPost() 
  ownerOnly external view returns (PostStruct[] memory) {
    return inactivePosts;
}
Enter fullscreen mode Exit fullscreen mode

This simply returns all deleted posts and it can only be accessed by the owner of the blog.

Step 14: The deletePost() function

function deletePost(uint256 postId) external returns (bool) {
    // check if post belong to user
    require(authorOf[postId] == msg.sender, "Unauthorized entity");

    // find and delete post
    for(uint i = 0; i < activePosts.length; i++) {
        if(activePosts[i].postId == postId) {
            activePosts[i].deleted = Deactivated.YES;
            activePosts[i].updated = block.timestamp;
            inactivePosts.push(activePosts[i]);
            delPostOf[postId] = authorOf[postId];
            delete activePosts[i];
            delete authorOf[postId];
        }
    }

    // Recallibrate counters
    postsOf[msg.sender]--;
    inactivePostCounter++;
    activePostCounter--;

    // Emits event
    emit Action (
        postId,
        "POST DELETED",
        Deactivated.YES,
        msg.sender,
        block.timestamp
    );

    // Returns success
    return true;
}
Enter fullscreen mode Exit fullscreen mode

The above function was achieved in four steps.

First, we validated that the owner of the post is the one deleting, then we searched for the post, deleted it, and emitted a “POST DELETED” event and also returns a boolean value of true.

Please, take note that the delete behavior is not done like in a normal database, rather we specified two arrays; activePosts and inactivePosts. Whenever we deleted a post, it will be moved to the inactivePosts array, and only the blog owner can restore it at the request of the post author.

Step 15: The Post Restore Function

function restorDeletedPost(
    uint256 postId, 
    address author
) ownerOnly external returns (bool) {
    // checks if post exists in users recycle bin
    require(delPostOf[postId] == author, "Unmatched Author");

    // Finds and restore the post to the author
    for(uint i = 0; i < inactivePosts.length; i++) {
        if(inactivePosts[i].postId == postId) {
            inactivePosts[i].deleted = Deactivated.NO;
            inactivePosts[i].updated = block.timestamp;
            activePosts.push(inactivePosts[i]);
            delete inactivePosts[i];
            authorOf[postId] = delPostOf[postId];
            delete delPostOf[postId];
        }
    }

    // Recallibrates counter to reflect restored post
    postsOf[author]++;
    inactivePostCounter--;
    activePostCounter++;

    // Emits a restoration event
    emit Action (
        postId,
        "POST RESTORED",
        Deactivated.NO,
        msg.sender,
        block.timestamp
    );

    // resturns success
    return true;
}
Enter fullscreen mode Exit fullscreen mode

Whenever a post is restored, it is moved from the inactivePosts array to the activePosts array.

It is worth mentioning that you can design your delete pattern a little bit different than mine. You can decide to leverage on the enum Deactivated structure we created above, and only delete by toggling the deleted state to YES or NO.

Men and brethren, that is how you crush the solidity CRUD functions…

Watch my FREE web3 tutorials on YouTube now.

Conclusion

It’s been a blast journeying with you on this tutorial, I hope you got some valuable insights on how to write solidity smart contract CRUD functions.

I want to inform you of my private tutoring classes for those who want to jump into the web3 space. If you’re interested, please book me up on my website.

That’s all for this tutorial, please like and share…

Expecting to see you in the next tutorial, till then, enjoy!

About the Author

Gospel Darlington kick-started his journey as a software engineer in 2016. Over the years, he has grown full-blown skills in JavaScript stacks such as React, ReactNative, NextJs, and now blockchain.

He is currently freelancing, building apps for clients, and writing technical tutorials teaching others how to do what he does.

Gospel Darlington is open and available to hear from you. You can reach him on LinkedIn, Facebook, Github, or on his website.

Top comments (1)

Collapse
 
victorchukwuemeka profile image
victorchukwuemeka

That's really great