Introduction
This guide will explore the basics of CRUD (create, read, update and delete) operations using solidity. We will show also the event emitter that can be applied in every operation which can be used in log and in many other beneficial things. The post also will illustrate the test of the operations using the chai library.
Solidity CRUD
C.R.U.D
The CRUD (create, read, update and delete) operation is very obvious happening in many scenarios in the software industry. With blockchain technology and as it still does not have a long history such transactions may be a challenge. For instance, the delete operation is challenging when you write a smart contract in solidity and it is illustrated in the examples below.
Definitions
Solidity
Solidity is a programming language that is used in writing smart contracts. Solidity is similar to javascript in its syntax. Solidity is very famous in blockchain programming and in writing smart contracts. The smart contract can be written using other languages such as Vyper, Yul (previously also called JULIA or IULIA), and others.
Smart contract
The smart contract is a piece of code that runs on the blockchain network. Just smart contract deployed, it can not be edited, deleted, or hacked. The smart contract can be written using different programming languages. The big benefit of the smart contract is that will be run its conditions without the need for a middleman. The smart contract can be considered the initial way to disappear banks (It is true), banks may disappear with time with huge help from smart contracts.
// an example of a smart contract //SPDX-License-Identifier: MIT pragma solidity ^0.8.8; contract CustomerContract { // The Global public variables (make getters with "public") address public owner = msg.sender; uint256 public publicNumber; // Private variables uint256 private currentNumber; string private message; // A function to change the current number by any uint256 value. The stored number and the new number // should not be the same, If it is, it will fail with message "Identical numbers" // If the person who ask to change the number is not the contract owner, require function will fails // With returning error message "Only the owner who can update" function changeNumber(uint256 number) public { require(currentNumber != number, "Identical numbers"); require(msg.sender != owner, "Only the owner who can update"); currentNumber = number; } // This is how to return the current number. function getCurrentNumber() public view returns (uint256) { return currentNumber; } }
Smar contract example
The smart contract CRUD
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract CustomerBalanceContract { struct Customer { uint256 id; string name; } Customer[] private _customers; uint256 private _totalCustomersCount; constructor() { _totalCustomersCount = 0; } event AddEvent(uint256 id, string name); event UpdatedEvent(uint256 id, string name); event DeletedEvent(uint256 id); function add(uint256 id, string memory name) public returns (uint256 totalCustomers) { Customer memory newCustomer = Customer(id, name); _customers.push(newCustomer); _totalCustomersCount++; //emit add event emit AddEvent(id, name); return _totalCustomersCount; } function update(uint256 id, string memory name) public returns (bool success) { for (uint256 i = 0; i < _totalCustomersCount; i++) { if (compare(_customers[i].id, id)) { _customers[i].name = name; emit UpdatedEvent(id, name); return true; } } return false; } function deleteCustomer(uint256 id) public returns (bool success) { require(_totalCustomersCount > 0); require(id >= _customers.length, "Index is not exist"); for (uint i = id; i < _customers.length - 1; i++) { _customers[i] = _customers[i + 1]; } _customers.pop(); return true; } function getCustomer(uint256 id) public view returns (Customer memory) { for (uint256 i = 0; i < _totalCustomersCount; i++) { if (compare(_customers[i].id, id)) { return _customers[i]; } } revert("customer not found"); } function compare(uint256 a, uint256 b) internal pure returns (bool) { return a == b; } function getTotalCustomersCount() public view returns (uint256) { return _totalCustomersCount; } function getTotalCustomers() public view returns (Customer[] memory) { return _customers; } }
Event Emitters
Event is an inheritable member of a contract. An event is emitted, it stores the arguments passed in transaction logs. These logs are stored on the blockchain and are accessible using the address of the contract till the contract is present on the blockchain. An event generated is not accessible from within contracts, not even the one that has created and emitted them.
Testing
const { expect } = require("chai"); const { ethers } = require("hardhat"); describe("CustomerBalanceContract", function () { it("Should return empty array", async function () { const CustomerBalanceContract = await ethers.getContractFactory( "CustomerBalanceContract" ); const customerBalanceContract = await CustomerBalanceContract.deploy(); await customerBalanceContract.deployed(); expect(await customerBalanceContract.getTotalCustomersCount()).to.equal(0); const addItemTransaction = await customerBalanceContract.add(1, "Shaban"); // wait 1 approval await addItemTransaction.wait(1); expect(await customerBalanceContract.getTotalCustomersCount()).to.equal(1); }); });
Deploy
// We require the Hardhat Runtime Environment explicitly here. This is optional // but useful for running the script in a standalone fashion through `node <script>`. // // When running the script with `npx hardhat run <script>` you'll find the Hardhat // Runtime Environment's members available in the global scope. const hre = require("hardhat"); async function main() { // Hardhat always runs the compile task when running scripts with its command // line interface. // // If this script is run directly using `node` you may want to call compile // manually to make sure everything is compiled // await hre.run('compile'); // We get the contract to deploy const CustomerBalanceContract = await hre.ethers.getContractFactory( "CustomerBalanceContract" ); const customerBalanceContract = await CustomerBalanceContract.deploy(); await customerBalanceContract.deployed(); console.log( "CustomerBalanceContract deployed to:", customerBalanceContract.address ); } // We recommend this pattern to be able to use async/await everywhere // and properly handle errors. main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
Run/compile at hardhat
Using hardhat, you can simply run the commands
npx hardhat compile
or
yarn hardhat compile
Any of the above commands will compile the smart contracts to the hardhat local network. If you need to run on another network such as Rinkeby or Ethereum Mainnet network, you can configure the “hardhat.config.js” file.
Conclusion
We showed the usage of solidity with hardhat to apply different CRUD (create, read, update and delete) with showing how can we run different unit tests using the javascript chai library. The post has an example of a smart contract that is written in solidity language.
You can find sample source codes on Github Enjoy…!!!
I can help you to build such as software tools/snippets, you contact me from here